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/public-api/object/type-registry-helper.h>
19 #include <dali/devel-api/actors/actor-devel.h>
20 #include <dali/devel-api/object/handle-devel.h>
21 #include <dali-toolkit/devel-api/layouting/layout-group-impl.h>
22 #include <dali-toolkit/internal/layouting/layout-group-data-impl.h>
23 #include <dali-toolkit/public-api/controls/control-impl.h>
24 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
29 const char* MARGIN_SPECIFICATION_NAME( "marginSpec" );
39 LayoutGroup::LayoutGroup()
40 : mImpl( new LayoutGroup::Impl() ),
45 LayoutGroup::~LayoutGroup()
47 // An object with a unique_ptr to an opaque structure must define it's destructor in the translation unit
48 // where the opaque structure is defined. It cannot use the default method in the header file.
51 Toolkit::LayoutGroup::LayoutId LayoutGroup::Add( LayoutItem& child )
53 LayoutParent* oldParent = child.GetParent();
56 LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
59 parentGroup->Remove( child );
63 Impl::ChildLayout childLayout;
64 childLayout.layoutId = mImpl->mNextLayoutId++;
65 childLayout.child = &child;
66 mImpl->mChildren.emplace_back( childLayout );
68 auto owner = child.GetOwner();
70 // If the owner does not have any LayoutItem child properties, add them
71 if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) )
73 // Set default properties for LayoutGroup and LayoutItem.
74 // Deriving classes can override OnChildAdd() to add their own default properties
75 GenerateDefaultChildPropertyValues( owner );
78 // Inform deriving classes that this child has been added
79 OnChildAdd( *childLayout.child.Get() );
81 // Now listen to future changes to the child properties.
82 DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
86 return childLayout.layoutId;
89 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
91 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
93 if( iter->layoutId == childId )
95 OnChildRemove( *iter->child.Get() );
96 mImpl->mChildren.erase(iter);
103 void LayoutGroup::Remove( LayoutItem& child )
105 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
107 if( iter->child.Get() == &child )
109 OnChildRemove( *iter->child.Get() );
110 mImpl->mChildren.erase(iter);
117 void LayoutGroup::RemoveAll()
119 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
121 OnChildRemove( *iter->child.Get() );
122 iter = mImpl->mChildren.erase(iter);
126 unsigned int LayoutGroup::GetChildCount() const
128 return mImpl->mChildren.size();
131 LayoutItemPtr LayoutGroup::GetChildAt( unsigned int index ) const
133 DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
134 return mImpl->mChildren[ index ].child;
137 LayoutItemPtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
139 for( auto&& childLayout : mImpl->mChildren )
141 if( childLayout.layoutId == childId )
143 return childLayout.child;
149 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutItem& child ) const
151 for( auto&& childLayout : mImpl->mChildren )
153 if( childLayout.child.Get() == &child )
155 return childLayout.layoutId;
158 return Toolkit::LayoutGroup::UNKNOWN_ID;
161 void LayoutGroup::OnChildAdd( LayoutItem& child )
165 void LayoutGroup::OnChildRemove( LayoutItem& child )
169 void LayoutGroup::DoInitialize()
173 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
177 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
179 if ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) && ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
181 // If any child properties are set, must perform relayout
186 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
188 child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
189 Toolkit::ChildLayoutData::WRAP_CONTENT );
190 child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
191 Toolkit::ChildLayoutData::WRAP_CONTENT );
192 child.SetProperty( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Extents() );
195 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
197 for( auto&& child : mImpl->mChildren )
199 //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
201 MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
206 void LayoutGroup::MeasureChild( LayoutItemPtr child,
207 MeasureSpec parentWidthMeasureSpec,
208 MeasureSpec parentHeightMeasureSpec )
210 auto childOwner = child->GetOwner();
211 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
212 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
214 auto padding = GetPadding();
216 const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
217 padding.start + padding.end,
219 const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
220 padding.top + padding.bottom,
223 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
226 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
227 MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
228 MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
230 auto childOwner = child->GetOwner();
231 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
232 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
233 auto desiredMargin = childOwner.GetProperty<Extents>( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION );
234 auto padding = GetPadding();
236 MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
237 padding.start + padding.end +
238 desiredMargin.start + desiredMargin.end +
239 widthUsed, desiredWidth );
241 MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
242 padding.top + padding.bottom +
243 desiredMargin.top + desiredMargin.end +
244 heightUsed, desiredHeight );
246 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
250 MeasureSpec LayoutGroup::GetChildMeasureSpec(
251 MeasureSpec measureSpec,
252 LayoutLength padding,
253 LayoutLength childDimension )
255 auto specMode = measureSpec.GetMode();
256 LayoutLength specSize = measureSpec.GetSize();
258 auto size = std::max( LayoutLength(0), specSize - padding );
260 MeasureSpec::IntType resultSize = 0;
261 MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
265 // Parent has imposed an exact size on us
266 case MeasureSpec::Mode::EXACTLY:
269 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
271 // Child wants to be our size. So be it.
273 resultMode = MeasureSpec::Mode::EXACTLY;
275 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
277 // Child wants to determine its own size. It can't be
280 resultMode = MeasureSpec::Mode::AT_MOST;
284 resultSize = childDimension;
285 resultMode = MeasureSpec::Mode::EXACTLY;
291 // Parent has imposed a maximum size on us
292 case MeasureSpec::Mode::AT_MOST:
294 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
296 // Child wants to be our size, but our size is not fixed.
297 // Constrain child to not be bigger than us.
299 resultMode = MeasureSpec::Mode::AT_MOST;
301 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
303 // Child wants to determine its own size. It can't be
306 resultMode = MeasureSpec::Mode::AT_MOST;
310 // Child wants a specific size... so be it
311 resultSize = childDimension;
312 resultMode = MeasureSpec::Mode::EXACTLY;
318 // Parent asked to see how big we want to be
319 case MeasureSpec::Mode::UNSPECIFIED:
321 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
323 // Child wants to be our size... find out how big it should be
324 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
325 resultMode = MeasureSpec::Mode::UNSPECIFIED;
327 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
329 // Child wants to determine its own size.... find out how big
331 resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
332 resultMode = MeasureSpec::Mode::UNSPECIFIED;
336 // Child wants a specific size... let him have it
337 resultSize = childDimension;
338 resultMode = MeasureSpec::Mode::EXACTLY;
344 //noinspection ResourceType
345 return MeasureSpec( resultSize, resultMode );
349 void LayoutGroup::OnInitialize()
351 auto control = Toolkit::Control::DownCast( GetOwner() );
355 // Take ownership of existing children
356 for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
358 ChildAddedToOwner( control.GetChildAt( childIndex ) );
361 DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
362 DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
363 DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
367 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
369 auto typeInfo = TypeRegistry::Get().GetTypeInfo( containerType );
372 Property::IndexContainer indices;
373 typeInfo.GetChildPropertyIndices( indices );
375 if( std::find( indices.Begin(), indices.End(), Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION ) ==
378 ChildPropertyRegistration( typeInfo.GetName(), MARGIN_SPECIFICATION_NAME, Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Property::EXTENTS );
382 DoRegisterChildProperties( containerType );
385 void LayoutGroup::OnUnparent()
390 void LayoutGroup::ChildAddedToOwner( Actor child )
392 LayoutItemPtr childLayout;
393 Toolkit::Control control = Toolkit::Control::DownCast( child );
395 if( control ) // Can only support adding Controls, not Actors to layout
397 Internal::Control& childControlImpl = GetImplementation( control );
398 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
399 childLayout = childControlDataImpl.GetLayout();
403 // If the child doesn't already have a layout, then create a LayoutItem for it.
404 childLayout = LayoutItem::New( control );
405 childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
407 auto desiredSize = control.GetNaturalSize();
408 childControlDataImpl.SetLayout( *childLayout.Get() );
410 // HBoxLayout will apply default layout data for this object
411 child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, LayoutLength::IntType( desiredSize.width ) );
412 child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, LayoutLength::IntType( desiredSize.height ) );
413 child.SetProperty( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Extents() );
416 Add( *childLayout.Get() );
420 void LayoutGroup::ChildRemovedFromOwner( Actor child )
422 Toolkit::Control control = Toolkit::Control::DownCast( child );
425 Internal::Control& childControlImpl = GetImplementation( control );
426 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
427 auto childLayout = childControlDataImpl.GetLayout();
430 Remove( *childLayout.Get() );
435 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
437 auto actor = Actor::DownCast( handle );
438 if( actor && index == Actor::Property::LAYOUT_DIRECTION )
445 } // namespace Internal
446 } // namespace Toolkit