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::NoLogging, 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 child.SetParent( this );
76 auto owner = child.GetOwner();
78 // If the owner does not have any LayoutItem child properties, add them
79 if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) )
81 // Set default properties for LayoutGroup and LayoutItem.
82 // Deriving classes can override OnChildAdd() to add their own default properties
83 GenerateDefaultChildPropertyValues( owner );
86 // Inform deriving classes that this child has been added
87 OnChildAdd( *childLayout.child.Get() );
89 // Now listen to future changes to the child properties.
90 DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
94 return childLayout.layoutId;
97 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
99 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
101 if( iter->layoutId == childId )
103 RemoveChild( *iter->child.Get() );
104 mImpl->mChildren.erase(iter);
111 void LayoutGroup::Remove( LayoutItem& child )
113 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
115 if( iter->child.Get() == &child )
117 RemoveChild( *iter->child.Get() );
118 mImpl->mChildren.erase(iter);
125 Toolkit::LayoutGroup::LayoutId LayoutGroup::Insert( LayoutItem& target, LayoutItem& child )
127 LayoutParent* oldParent = child.GetParent();
130 LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
133 parentGroup->Remove( child );
137 // Find target position
138 std::vector< Impl::ChildLayout >::iterator position;
139 for( auto iter = mImpl->mChildren.begin(); iter != mImpl->mChildren.end(); ++iter )
141 if( iter->child.Get() == &target )
148 Impl::ChildLayout childLayout;
149 childLayout.layoutId = mImpl->mNextLayoutId++;
150 childLayout.child = &child;
151 mImpl->mChildren.insert( position, childLayout );
153 child.SetParent( this );
155 auto owner = child.GetOwner();
157 // Inform deriving classes that this child has been added
158 OnChildAdd( *childLayout.child.Get() );
160 // Now listen to future changes to the child properties.
161 DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
165 return childLayout.layoutId;
168 Toolkit::LayoutGroup::LayoutId LayoutGroup::Move( LayoutItem& target, LayoutItem& child )
170 // Remove child from the previous position
171 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
173 if( iter->child.Get() == &child )
175 mImpl->mChildren.erase( iter );
180 // Find target position
181 std::vector< Impl::ChildLayout >::iterator position;
182 for( auto iter = mImpl->mChildren.begin(); iter != mImpl->mChildren.end(); ++iter )
184 if( iter->child.Get() == &target )
191 Impl::ChildLayout childLayout;
192 childLayout.layoutId = mImpl->mNextLayoutId++;
193 childLayout.child = &child;
194 mImpl->mChildren.insert( position, childLayout );
198 return childLayout.layoutId;
201 Toolkit::LayoutGroup::LayoutId LayoutGroup::MoveBack( LayoutItem& child )
203 // Remove child from the previous position
204 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
206 if( iter->child.Get() == &child )
208 mImpl->mChildren.erase( iter );
213 Impl::ChildLayout childLayout;
214 childLayout.layoutId = mImpl->mNextLayoutId++;
215 childLayout.child = &child;
216 mImpl->mChildren.emplace_back( childLayout );
220 return childLayout.layoutId;
223 void LayoutGroup::RemoveAll()
225 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; )
227 RemoveChild( *iter->child.Get() );
228 iter = mImpl->mChildren.erase(iter);
232 unsigned int LayoutGroup::GetChildCount() const
234 return mImpl->mChildren.size();
237 LayoutItemPtr LayoutGroup::GetChildAt( unsigned int index ) const
239 DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
240 return mImpl->mChildren[ index ].child;
243 LayoutItemPtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
245 for( auto&& childLayout : mImpl->mChildren )
247 if( childLayout.layoutId == childId )
249 return childLayout.child;
255 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutItem& child ) const
257 for( auto&& childLayout : mImpl->mChildren )
259 if( childLayout.child.Get() == &child )
261 return childLayout.layoutId;
264 return Toolkit::LayoutGroup::UNKNOWN_ID;
267 void LayoutGroup::OnChildAdd( LayoutItem& child )
271 void LayoutGroup::OnChildRemove( LayoutItem& child )
275 void LayoutGroup::DoInitialize()
279 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
283 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
285 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::OnSetChildProperties property(%s)\n", handle.GetPropertyName(index).c_str());
287 if ( ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) &&
288 ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
290 ( index == Toolkit::Control::Property::MARGIN || index == Toolkit::Control::Property::PADDING ) )
292 // If any child properties are set, must perform relayout
293 for( auto&& child : mImpl->mChildren )
295 if( child.child->GetOwner() == handle )
297 child.child->RequestLayout();
304 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
306 child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
307 Toolkit::ChildLayoutData::WRAP_CONTENT );
308 child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
309 Toolkit::ChildLayoutData::WRAP_CONTENT );
312 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
314 for( auto&& child : mImpl->mChildren )
316 //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
318 MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
323 void LayoutGroup::MeasureChild( LayoutItemPtr child,
324 MeasureSpec parentWidthMeasureSpec,
325 MeasureSpec parentHeightMeasureSpec )
327 DALI_LOG_TRACE_METHOD( gLogFilter );
329 auto childOwner = child->GetOwner();
331 auto control = Toolkit::Control::DownCast( childOwner );
333 #if defined( DEBUG_ENABLED )
336 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild natural size(%f, %f)\n", control.GetNaturalSize().width, control.GetNaturalSize().height );
340 // Get last stored width and height specifications for the child
341 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
342 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
343 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild desiredWidth(%d) desiredHeight(%d)\n", desiredWidth, desiredHeight );
345 auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
347 const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
348 padding.start + padding.end,
350 const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
351 padding.top + padding.bottom,
354 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
357 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
358 MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
359 MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
361 auto childOwner = child->GetOwner();
362 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
363 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
365 auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
367 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredWidth(%d)\n", desiredWidth );
369 MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
370 padding.start + padding.end +
371 widthUsed, desiredWidth );
373 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredHeight(%d)\n", desiredHeight );
375 MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
376 padding.top + padding.bottom +
377 heightUsed, desiredHeight );
379 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
383 MeasureSpec LayoutGroup::GetChildMeasureSpec(
384 MeasureSpec measureSpec,
385 LayoutLength padding,
386 LayoutLength childDimension )
388 auto specMode = measureSpec.GetMode();
389 LayoutLength specSize = measureSpec.GetSize();
391 auto size = std::max( LayoutLength(0), specSize - padding ); // reduce available size by the owners padding
393 MeasureSpec::IntType resultSize = 0;
394 MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
398 // Parent has imposed an exact size on us
399 case MeasureSpec::Mode::EXACTLY:
401 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::EXACTLY\n");
402 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
404 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension MATCH_PARENT\n");
406 // Child wants to be our size. So be it.
408 resultMode = MeasureSpec::Mode::EXACTLY;
410 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
412 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension WRAP_CONTENT\n");
414 // Child wants to determine its own size. It can't be
417 resultMode = MeasureSpec::Mode::AT_MOST;
421 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension UNSPECIFIED\n");
422 resultSize = childDimension;
423 resultMode = MeasureSpec::Mode::EXACTLY;
429 // Parent has imposed a maximum size on us
430 case MeasureSpec::Mode::AT_MOST:
432 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::AT_MOST\n");
433 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
435 // Child wants to be our size, but our size is not fixed.
436 // Constrain child to not be bigger than us.
438 resultMode = MeasureSpec::Mode::AT_MOST;
440 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
442 // Child wants to determine its own size. It can't be
445 resultMode = MeasureSpec::Mode::AT_MOST;
449 // Child wants a specific size... so be it
450 resultSize = childDimension + padding;
451 resultMode = MeasureSpec::Mode::EXACTLY;
457 // Parent asked to see how big we want to be
458 case MeasureSpec::Mode::UNSPECIFIED:
460 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::UNSPECIFIED\n");
462 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
464 // Child wants to be our size... find out how big it should be
465 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
466 resultMode = MeasureSpec::Mode::UNSPECIFIED;
468 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
470 // Child wants to determine its own size.... find out how big
472 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
473 resultMode = MeasureSpec::Mode::UNSPECIFIED;
477 // Child wants a specific size... let him have it
478 resultSize = childDimension + padding;
479 resultMode = MeasureSpec::Mode::EXACTLY;
485 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec resultSize(%u)\n", resultSize );
488 //noinspection ResourceType
489 return MeasureSpec( resultSize, resultMode );
493 void LayoutGroup::OnInitialize()
495 auto control = Toolkit::Control::DownCast( GetOwner() );
499 // Take ownership of existing children
500 for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
502 ChildAddedToOwner( control.GetChildAt( childIndex ) );
505 DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
506 DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
507 DevelActor::ChildOrderChangedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildOrderChanged );
508 DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
510 if( control.GetParent() )
512 auto parent = Toolkit::Control::DownCast( control.GetParent() );
515 auto parentLayout = Toolkit::LayoutGroup::DownCast( DevelControl::GetLayout( parent ) );
518 Internal::LayoutGroup& parentLayoutImpl = GetImplementation( parentLayout );
520 unsigned int count = parent.GetChildCount();
521 unsigned int index = static_cast< unsigned int >( control.GetProperty< int >( DevelActor::Property::SIBLING_ORDER ) );
523 // Find insertion position
524 while( ++index < count )
526 auto sibling = Toolkit::Control::DownCast( parent.GetChildAt( index ) );
529 auto siblingLayout = DevelControl::GetLayout( sibling );
532 Internal::LayoutItem& siblingLayoutImpl = GetImplementation( siblingLayout );
533 parentLayoutImpl.Insert( siblingLayoutImpl, *this );
541 parentLayoutImpl.Add( *this );
549 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
551 DoRegisterChildProperties( containerType );
554 void LayoutGroup::OnUnparent()
560 void LayoutGroup::RemoveChild( LayoutItem& item )
562 item.SetParent( nullptr );
563 OnChildRemove( item );
566 void LayoutGroup::ChildAddedToOwner( Actor child )
568 LayoutItemPtr childLayout;
569 Toolkit::Control control = Toolkit::Control::DownCast( child );
571 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner control(%s)\n", control?control.GetName().c_str():"Invalid" );
573 if( control ) // Can only support adding Controls, not Actors to layout
575 Internal::Control& childControlImpl = GetImplementation( control );
576 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
577 childLayout = childControlDataImpl.GetLayout();
581 // If the child doesn't already have a layout, then create a LayoutItem for it.
582 childLayout = LayoutItem::New( control );
583 childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
584 #if defined(DEBUG_ENABLED)
585 auto desiredSize = control.GetNaturalSize();
586 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner desiredSize(%f,%f) (naturalSize)\n", desiredSize.width, desiredSize.height );
588 childControlDataImpl.SetLayout( *childLayout.Get() );
590 Vector3 size = child.GetTargetSize();
591 // If the size of the control is set explicitly make sure that the control size
592 // stays the same after the layout except it is over written with match parent specs.
595 childLayout->SetMinimumWidth( size.x );
600 childLayout->SetMinimumHeight( size.y );
602 // Default layout data will be generated by Add().
606 LayoutGroupPtr layoutGroup( dynamic_cast< LayoutGroup* >( childLayout.Get() ) );
609 // Set only in case of leaf children
610 childLayout->SetAnimateLayout( IsLayoutAnimated() );
614 Add( *childLayout.Get() );
618 void LayoutGroup::ChildRemovedFromOwner( Actor child )
620 Toolkit::Control control = Toolkit::Control::DownCast( child );
623 Internal::Control& childControlImpl = GetImplementation( control );
624 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
625 auto childLayout = childControlDataImpl.GetLayout();
628 Remove( *childLayout.Get() );
633 void LayoutGroup::ChildOrderChanged( Actor child )
635 Toolkit::Control childControl = Toolkit::Control::DownCast( child );
638 Internal::Control& childControlImpl = GetImplementation( childControl );
639 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
641 auto childLayout = childControlDataImpl.GetLayout();
644 Toolkit::Control control = Toolkit::Control::DownCast( GetOwner() );
645 unsigned int count = control.GetChildCount();
646 unsigned int index = static_cast< unsigned int >( childControl.GetProperty< int >( DevelActor::Property::SIBLING_ORDER ) );
648 // Find insertion position
649 while( ++index < count )
651 auto sibling = Toolkit::Control::DownCast( control.GetChildAt( index ) );
654 auto siblingLayout = DevelControl::GetLayout( sibling );
657 Internal::LayoutItem& siblingLayoutImpl = GetImplementation( siblingLayout );
658 Move( siblingLayoutImpl, *childLayout );
664 MoveBack( *childLayout );
669 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
671 DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n");
672 auto actor = Actor::DownCast( handle );
675 index == Actor::Property::LAYOUT_DIRECTION ||
676 index == Toolkit::Control::Property::PADDING ||
677 index == Toolkit::Control::Property::MARGIN
685 void LayoutGroup::OnAnimationStateChanged( bool animateLayout )
687 // Change children's animation state
688 for( auto&& child : mImpl->mChildren )
690 LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( child.child.Get() ) );
693 // Change state only in case of leaf children
694 child.child->SetAnimateLayout( animateLayout );
699 } // namespace Internal
700 } // namespace Toolkit