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() ; ++iter )
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");
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
197 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
199 child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
200 Toolkit::ChildLayoutData::WRAP_CONTENT );
201 child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
202 Toolkit::ChildLayoutData::WRAP_CONTENT );
205 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
207 for( auto&& child : mImpl->mChildren )
209 //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
211 MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
216 void LayoutGroup::MeasureChild( LayoutItemPtr child,
217 MeasureSpec parentWidthMeasureSpec,
218 MeasureSpec parentHeightMeasureSpec )
220 DALI_LOG_TRACE_METHOD( gLogFilter );
222 auto childOwner = child->GetOwner();
224 auto control = Toolkit::Control::DownCast( childOwner );
226 #if defined( DEBUG_ENABLED )
229 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins naturalSizewidth(%f)\n", control.GetNaturalSize().width );
233 // Get last stored width and height specifications for the child
234 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
235 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
237 // The size of the control could have changed due to padding being altered, the XXX_SPECIFICATIONs will not have been updated.
238 // So GetNaturalSize is called, if the control's natural size includes padding then th size will be updated.
241 auto desiredSize = control.GetNaturalSize(); // Get's child control's size which could include new padding values.
243 // Check if WIDTH_SPECIFICATION was a size value, if so then get update to child controls's width.
244 if ( desiredWidth > 0 )
246 desiredWidth = desiredSize.width;
247 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, LayoutLength::IntType( desiredWidth ) );
250 // Check if HEIGHT_SPECIFICATION was a size value, if so then get update to child controls's height.
251 if ( desiredHeight > 0)
253 desiredHeight = desiredSize.height;
254 childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, LayoutLength::IntType( desiredHeight ) );
258 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild desiredWidth(%d)\n", desiredWidth );
260 auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
262 const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
263 padding.start + padding.end,
265 const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
266 padding.top + padding.bottom,
269 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
272 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
273 MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
274 MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
276 auto childOwner = child->GetOwner();
277 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
278 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
280 auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
283 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredWidth(%d)\n", desiredWidth );
285 MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
286 padding.start + padding.end +
287 widthUsed, desiredWidth );
289 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredHeight(%d)\n", desiredHeight );
291 MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
292 padding.top + padding.bottom +
293 heightUsed, desiredHeight );
295 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
299 MeasureSpec LayoutGroup::GetChildMeasureSpec(
300 MeasureSpec measureSpec,
301 LayoutLength padding,
302 LayoutLength childDimension )
304 auto specMode = measureSpec.GetMode();
305 LayoutLength specSize = measureSpec.GetSize();
307 auto size = std::max( LayoutLength(0), specSize - padding ); // reduce available size by the owners padding
309 MeasureSpec::IntType resultSize = 0;
310 MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
314 // Parent has imposed an exact size on us
315 case MeasureSpec::Mode::EXACTLY:
317 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::EXACTLY\n");
318 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
320 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension MATCH_PARENT\n");
322 // Child wants to be our size. So be it.
324 resultMode = MeasureSpec::Mode::EXACTLY;
326 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
328 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension WRAP_CONTENT\n");
330 // Child wants to determine its own size. It can't be
333 resultMode = MeasureSpec::Mode::AT_MOST;
337 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension UNSPECIFIED\n");
338 resultSize = childDimension;
339 resultMode = MeasureSpec::Mode::EXACTLY;
345 // Parent has imposed a maximum size on us
346 case MeasureSpec::Mode::AT_MOST:
348 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::AT_MOST\n");
349 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
351 // Child wants to be our size, but our size is not fixed.
352 // Constrain child to not be bigger than us.
354 resultMode = MeasureSpec::Mode::AT_MOST;
356 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
358 // Child wants to determine its own size. It can't be
361 resultMode = MeasureSpec::Mode::AT_MOST;
365 // Child wants a specific size... so be it
366 resultSize = childDimension + padding;
367 resultMode = MeasureSpec::Mode::EXACTLY;
373 // Parent asked to see how big we want to be
374 case MeasureSpec::Mode::UNSPECIFIED:
376 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::UNSPECIFIED\n");
378 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
380 // Child wants to be our size... find out how big it should be
381 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
382 resultMode = MeasureSpec::Mode::UNSPECIFIED;
384 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
386 // Child wants to determine its own size.... find out how big
388 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
389 resultMode = MeasureSpec::Mode::UNSPECIFIED;
393 // Child wants a specific size... let him have it
394 resultSize = childDimension + padding;
395 resultMode = MeasureSpec::Mode::EXACTLY;
401 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec resultSize(%u)\n", resultSize );
404 //noinspection ResourceType
405 return MeasureSpec( resultSize, resultMode );
409 void LayoutGroup::OnInitialize()
411 auto control = Toolkit::Control::DownCast( GetOwner() );
415 // Take ownership of existing children
416 for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
418 ChildAddedToOwner( control.GetChildAt( childIndex ) );
421 DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
422 DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
423 DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
427 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
429 DoRegisterChildProperties( containerType );
432 void LayoutGroup::OnUnparent()
437 void LayoutGroup::ChildAddedToOwner( Actor child )
439 LayoutItemPtr childLayout;
440 Toolkit::Control control = Toolkit::Control::DownCast( child );
441 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner(%s)\n", control.GetName().c_str() );
443 if( control ) // Can only support adding Controls, not Actors to layout
445 Internal::Control& childControlImpl = GetImplementation( control );
446 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
447 childLayout = childControlDataImpl.GetLayout();
451 // If the child doesn't already have a layout, then create a LayoutItem for it.
452 childLayout = LayoutItem::New( control );
453 childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
455 auto desiredSize = control.GetNaturalSize();
456 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner desiredSize(%f,%f) (naturalSize)\n", desiredSize.width, desiredSize.height );
458 childControlDataImpl.SetLayout( *childLayout.Get() );
460 // Default layout data for this object
461 child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, LayoutLength::IntType( desiredSize.width ) );
462 child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, LayoutLength::IntType( desiredSize.height ) );
465 Add( *childLayout.Get() );
469 void LayoutGroup::ChildRemovedFromOwner( Actor child )
471 Toolkit::Control control = Toolkit::Control::DownCast( child );
474 Internal::Control& childControlImpl = GetImplementation( control );
475 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
476 auto childLayout = childControlDataImpl.GetLayout();
479 Remove( *childLayout.Get() );
484 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
486 DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n");
487 auto actor = Actor::DownCast( handle );
490 index == Actor::Property::LAYOUT_DIRECTION ||
491 index == Toolkit::Control::Property::PADDING ||
492 index == Toolkit::Control::Property::MARGIN
500 } // namespace Internal
501 } // namespace Toolkit