2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali-toolkit/devel-api/layouting/layout-group-impl.h>
21 #include <dali/public-api/object/type-registry-helper.h>
22 #include <dali/devel-api/actors/actor-devel.h>
23 #include <dali/devel-api/object/handle-devel.h>
24 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/layouting/layout-group-data-impl.h>
28 #include <dali-toolkit/public-api/controls/control-impl.h>
29 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_LAYOUT" );
45 LayoutGroup::LayoutGroup()
46 : mImpl( new LayoutGroup::Impl() ),
51 LayoutGroup::~LayoutGroup()
53 // An object with a unique_ptr to an opaque structure must define it's destructor in the translation unit
54 // where the opaque structure is defined. It cannot use the default method in the header file.
57 Toolkit::LayoutGroup::LayoutId LayoutGroup::Add( LayoutItem& child )
59 LayoutParent* oldParent = child.GetParent();
62 LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
65 parentGroup->Remove( child );
69 Impl::ChildLayout childLayout;
70 childLayout.layoutId = mImpl->mNextLayoutId++;
71 childLayout.child = &child;
72 mImpl->mChildren.emplace_back( childLayout );
74 auto owner = child.GetOwner();
76 // If the owner does not have any LayoutItem child properties, add them
77 if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) )
79 // Set default properties for LayoutGroup and LayoutItem.
80 // Deriving classes can override OnChildAdd() to add their own default properties
81 GenerateDefaultChildPropertyValues( owner );
84 // Inform deriving classes that this child has been added
85 OnChildAdd( *childLayout.child.Get() );
87 // Now listen to future changes to the child properties.
88 DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
92 return childLayout.layoutId;
95 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
97 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
99 if( iter->layoutId == childId )
101 OnChildRemove( *iter->child.Get() );
102 mImpl->mChildren.erase(iter);
109 void LayoutGroup::Remove( LayoutItem& child )
111 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
113 if( iter->child.Get() == &child )
115 OnChildRemove( *iter->child.Get() );
116 mImpl->mChildren.erase(iter);
123 void LayoutGroup::RemoveAll()
125 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; )
127 OnChildRemove( *iter->child.Get() );
128 iter = mImpl->mChildren.erase(iter);
132 unsigned int LayoutGroup::GetChildCount() const
134 return mImpl->mChildren.size();
137 LayoutItemPtr LayoutGroup::GetChildAt( unsigned int index ) const
139 DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
140 return mImpl->mChildren[ index ].child;
143 LayoutItemPtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
145 for( auto&& childLayout : mImpl->mChildren )
147 if( childLayout.layoutId == childId )
149 return childLayout.child;
155 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutItem& child ) const
157 for( auto&& childLayout : mImpl->mChildren )
159 if( childLayout.child.Get() == &child )
161 return childLayout.layoutId;
164 return Toolkit::LayoutGroup::UNKNOWN_ID;
167 void LayoutGroup::OnChildAdd( LayoutItem& child )
171 void LayoutGroup::OnChildRemove( LayoutItem& child )
175 void LayoutGroup::DoInitialize()
179 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
183 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
185 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::OnSetChildProperties\n");
187 if ( ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) &&
188 ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
190 ( index == Toolkit::Control::Property::MARGIN || index == Toolkit::Control::Property::PADDING ) )
192 // If any child properties are set, must perform relayout
194 for( auto&& child : mImpl->mChildren )
196 if( child.child->GetOwner() == handle )
198 child.child->SetLayoutRequested();
205 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
207 child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
208 Toolkit::ChildLayoutData::WRAP_CONTENT );
209 child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
210 Toolkit::ChildLayoutData::WRAP_CONTENT );
213 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
215 for( auto&& child : mImpl->mChildren )
217 //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
219 MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
224 void LayoutGroup::MeasureChild( LayoutItemPtr child,
225 MeasureSpec parentWidthMeasureSpec,
226 MeasureSpec parentHeightMeasureSpec )
228 DALI_LOG_TRACE_METHOD( gLogFilter );
230 auto childOwner = child->GetOwner();
232 auto control = Toolkit::Control::DownCast( childOwner );
234 #if defined( DEBUG_ENABLED )
237 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild natural size(%f, %f)\n", control.GetNaturalSize().width, control.GetNaturalSize().height );
241 // Get last stored width and height specifications for the child
242 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
243 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
244 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild desiredWidth(%d) desiredHeight(%d)\n", desiredWidth, desiredHeight );
246 auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
248 const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
249 padding.start + padding.end,
251 const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
252 padding.top + padding.bottom,
255 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
258 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
259 MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
260 MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
262 auto childOwner = child->GetOwner();
263 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
264 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
266 auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
268 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredWidth(%d)\n", desiredWidth );
270 MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
271 padding.start + padding.end +
272 widthUsed, desiredWidth );
274 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredHeight(%d)\n", desiredHeight );
276 MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
277 padding.top + padding.bottom +
278 heightUsed, desiredHeight );
280 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
284 MeasureSpec LayoutGroup::GetChildMeasureSpec(
285 MeasureSpec measureSpec,
286 LayoutLength padding,
287 LayoutLength childDimension )
289 auto specMode = measureSpec.GetMode();
290 LayoutLength specSize = measureSpec.GetSize();
292 auto size = std::max( LayoutLength(0), specSize - padding ); // reduce available size by the owners padding
294 MeasureSpec::IntType resultSize = 0;
295 MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
299 // Parent has imposed an exact size on us
300 case MeasureSpec::Mode::EXACTLY:
302 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::EXACTLY\n");
303 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
305 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension MATCH_PARENT\n");
307 // Child wants to be our size. So be it.
309 resultMode = MeasureSpec::Mode::EXACTLY;
311 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
313 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension WRAP_CONTENT\n");
315 // Child wants to determine its own size. It can't be
318 resultMode = MeasureSpec::Mode::AT_MOST;
322 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension UNSPECIFIED\n");
323 resultSize = childDimension;
324 resultMode = MeasureSpec::Mode::EXACTLY;
330 // Parent has imposed a maximum size on us
331 case MeasureSpec::Mode::AT_MOST:
333 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::AT_MOST\n");
334 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
336 // Child wants to be our size, but our size is not fixed.
337 // Constrain child to not be bigger than us.
339 resultMode = MeasureSpec::Mode::AT_MOST;
341 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
343 // Child wants to determine its own size. It can't be
346 resultMode = MeasureSpec::Mode::AT_MOST;
350 // Child wants a specific size... so be it
351 resultSize = childDimension + padding;
352 resultMode = MeasureSpec::Mode::EXACTLY;
358 // Parent asked to see how big we want to be
359 case MeasureSpec::Mode::UNSPECIFIED:
361 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::UNSPECIFIED\n");
363 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
365 // Child wants to be our size... find out how big it should be
366 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
367 resultMode = MeasureSpec::Mode::UNSPECIFIED;
369 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
371 // Child wants to determine its own size.... find out how big
373 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
374 resultMode = MeasureSpec::Mode::UNSPECIFIED;
378 // Child wants a specific size... let him have it
379 resultSize = childDimension + padding;
380 resultMode = MeasureSpec::Mode::EXACTLY;
386 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec resultSize(%u)\n", resultSize );
389 //noinspection ResourceType
390 return MeasureSpec( resultSize, resultMode );
394 void LayoutGroup::OnInitialize()
396 auto control = Toolkit::Control::DownCast( GetOwner() );
400 // Take ownership of existing children
401 for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
403 ChildAddedToOwner( control.GetChildAt( childIndex ) );
406 DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
407 DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
408 DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
412 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
414 DoRegisterChildProperties( containerType );
417 void LayoutGroup::OnUnparent()
422 void LayoutGroup::ChildAddedToOwner( Actor child )
424 LayoutItemPtr childLayout;
425 Toolkit::Control control = Toolkit::Control::DownCast( child );
426 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner(%s)\n", control.GetName().c_str() );
428 if( control ) // Can only support adding Controls, not Actors to layout
430 Internal::Control& childControlImpl = GetImplementation( control );
431 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
432 childLayout = childControlDataImpl.GetLayout();
436 // If the child doesn't already have a layout, then create a LayoutItem for it.
437 childLayout = LayoutItem::New( control );
438 childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
439 #if defined(DEBUG_ENABLED)
440 auto desiredSize = control.GetNaturalSize();
441 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner desiredSize(%f,%f) (naturalSize)\n", desiredSize.width, desiredSize.height );
443 childControlDataImpl.SetLayout( *childLayout.Get() );
445 // Default layout data will be generated by Add().
448 Add( *childLayout.Get() );
452 void LayoutGroup::ChildRemovedFromOwner( Actor child )
454 Toolkit::Control control = Toolkit::Control::DownCast( child );
457 Internal::Control& childControlImpl = GetImplementation( control );
458 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
459 auto childLayout = childControlDataImpl.GetLayout();
462 Remove( *childLayout.Get() );
467 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
469 DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n");
470 auto actor = Actor::DownCast( handle );
473 index == Actor::Property::LAYOUT_DIRECTION ||
474 index == Toolkit::Control::Property::PADDING ||
475 index == Toolkit::Control::Property::MARGIN
483 } // namespace Internal
484 } // namespace Toolkit