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()
49 Toolkit::LayoutGroup::LayoutId LayoutGroup::Add( LayoutBase& child )
51 LayoutParent* oldParent = child.GetParent();
54 LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
57 parentGroup->Remove( child );
61 Impl::ChildLayout childLayout;
62 childLayout.layoutId = mImpl->mNextLayoutId++;
63 childLayout.child = &child;
64 mImpl->mChildren.emplace_back( childLayout );
66 auto owner = child.GetOwner();
68 // If the owner does not have any LayoutBase child properties, add them
69 if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutBase::ChildProperty::WIDTH_SPECIFICATION ) )
71 // Set default properties for LayoutGroup and LayoutBase.
72 // Deriving classes can override OnChildAdd() to add their own default properties
73 GenerateDefaultChildPropertyValues( owner );
76 // Inform deriving classes that this child has been added
77 OnChildAdd( *childLayout.child.Get() );
79 // Now listen to future changes to the child properties.
80 DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
84 return childLayout.layoutId;
87 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
89 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
91 if( iter->layoutId == childId )
93 OnChildRemove( *iter->child.Get() );
94 mImpl->mChildren.erase(iter);
101 void LayoutGroup::Remove( LayoutBase& child )
103 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
105 if( iter->child.Get() == &child )
107 OnChildRemove( *iter->child.Get() );
108 mImpl->mChildren.erase(iter);
115 void LayoutGroup::RemoveAll()
117 for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
119 OnChildRemove( *iter->child.Get() );
120 iter = mImpl->mChildren.erase(iter);
124 unsigned int LayoutGroup::GetChildCount() const
126 return mImpl->mChildren.size();
129 LayoutBasePtr LayoutGroup::GetChildAt( unsigned int index ) const
131 DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
132 return mImpl->mChildren[ index ].child;
135 LayoutBasePtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
137 for( auto&& childLayout : mImpl->mChildren )
139 if( childLayout.layoutId == childId )
141 return childLayout.child;
147 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutBase& child ) const
149 for( auto&& childLayout : mImpl->mChildren )
151 if( childLayout.child.Get() == &child )
153 return childLayout.layoutId;
156 return Toolkit::LayoutGroup::UNKNOWN_ID;
159 void LayoutGroup::OnChildAdd( LayoutBase& child )
163 void LayoutGroup::OnChildRemove( LayoutBase& child )
167 void LayoutGroup::DoInitialize()
171 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
175 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
177 if ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) && ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
179 // If any child properties are set, must perform relayout
184 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
186 child.SetProperty( Toolkit::LayoutBase::ChildProperty::WIDTH_SPECIFICATION,
187 Toolkit::ChildLayoutData::WRAP_CONTENT );
188 child.SetProperty( Toolkit::LayoutBase::ChildProperty::HEIGHT_SPECIFICATION,
189 Toolkit::ChildLayoutData::WRAP_CONTENT );
190 child.SetProperty( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Extents() );
193 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
195 for( auto&& child : mImpl->mChildren )
197 //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
199 MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
204 void LayoutGroup::MeasureChild( LayoutBasePtr child,
205 MeasureSpec parentWidthMeasureSpec,
206 MeasureSpec parentHeightMeasureSpec )
208 auto childOwner = child->GetOwner();
209 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutBase::ChildProperty::WIDTH_SPECIFICATION );
210 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutBase::ChildProperty::HEIGHT_SPECIFICATION );
212 auto padding = GetPadding();
214 const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
215 padding.start + padding.end,
217 const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
218 padding.top + padding.bottom,
221 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
224 void LayoutGroup::MeasureChildWithMargins( LayoutBasePtr child,
225 MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
226 MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
228 auto childOwner = child->GetOwner();
229 auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutBase::ChildProperty::WIDTH_SPECIFICATION );
230 auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutBase::ChildProperty::HEIGHT_SPECIFICATION );
231 auto desiredMargin = childOwner.GetProperty<Extents>( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION );
232 auto padding = GetPadding();
234 MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
235 padding.start + padding.end +
236 desiredMargin.start + desiredMargin.end +
237 widthUsed, desiredWidth );
239 MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
240 padding.top + padding.bottom +
241 desiredMargin.top + desiredMargin.end +
242 heightUsed, desiredHeight );
244 child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
248 MeasureSpec LayoutGroup::GetChildMeasureSpec(
249 MeasureSpec measureSpec,
250 LayoutLength padding,
251 LayoutLength childDimension )
253 auto specMode = measureSpec.GetMode();
254 LayoutLength specSize = measureSpec.GetSize();
256 auto size = std::max( LayoutLength(0), specSize - padding );
258 MeasureSpec::IntType resultSize = 0;
259 MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
263 // Parent has imposed an exact size on us
264 case MeasureSpec::Mode::EXACTLY:
267 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
269 // Child wants to be our size. So be it.
271 resultMode = MeasureSpec::Mode::EXACTLY;
273 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
275 // Child wants to determine its own size. It can't be
278 resultMode = MeasureSpec::Mode::AT_MOST;
282 resultSize = childDimension;
283 resultMode = MeasureSpec::Mode::EXACTLY;
289 // Parent has imposed a maximum size on us
290 case MeasureSpec::Mode::AT_MOST:
292 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
294 // Child wants to be our size, but our size is not fixed.
295 // Constrain child to not be bigger than us.
297 resultMode = MeasureSpec::Mode::AT_MOST;
299 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
301 // Child wants to determine its own size. It can't be
304 resultMode = MeasureSpec::Mode::AT_MOST;
308 // Child wants a specific size... so be it
309 resultSize = childDimension;
310 resultMode = MeasureSpec::Mode::EXACTLY;
316 // Parent asked to see how big we want to be
317 case MeasureSpec::Mode::UNSPECIFIED:
319 if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
321 // Child wants to be our size... find out how big it should be
322 resultSize = LayoutBase::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
323 resultMode = MeasureSpec::Mode::UNSPECIFIED;
325 else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
327 // Child wants to determine its own size.... find out how big
329 resultSize = LayoutBase::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
330 resultMode = MeasureSpec::Mode::UNSPECIFIED;
334 // Child wants a specific size... let him have it
335 resultSize = childDimension;
336 resultMode = MeasureSpec::Mode::EXACTLY;
342 //noinspection ResourceType
343 return MeasureSpec( resultSize, resultMode );
347 void LayoutGroup::OnInitialize()
349 auto control = Toolkit::Control::DownCast( GetOwner() );
353 // Take ownership of existing children
354 for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
356 ChildAddedToOwner( control.GetChildAt( childIndex ) );
359 DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
360 DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
361 DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
365 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
367 auto typeInfo = TypeRegistry::Get().GetTypeInfo( containerType );
370 Property::IndexContainer indices;
371 typeInfo.GetChildPropertyIndices( indices );
373 if( std::find( indices.Begin(), indices.End(), Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION ) ==
376 ChildPropertyRegistration( typeInfo.GetName(), MARGIN_SPECIFICATION_NAME, Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Property::EXTENTS );
380 DoRegisterChildProperties( containerType );
383 void LayoutGroup::OnUnparent()
388 void LayoutGroup::ChildAddedToOwner( Actor child )
390 LayoutBasePtr childLayout;
391 Toolkit::Control control = Toolkit::Control::DownCast( child );
393 if( control ) // Can only support adding Controls, not Actors to layout
395 Internal::Control& childControlImpl = GetImplementation( control );
396 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
397 childLayout = childControlDataImpl.GetLayout();
401 // If the child doesn't already have a layout, then create a LayoutBase for it.
402 childLayout = LayoutBase::New( control );
403 childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
405 auto desiredSize = control.GetNaturalSize();
406 childControlDataImpl.SetLayout( *childLayout.Get() );
408 // HBoxLayout will apply default layout data for this object
409 child.SetProperty( Toolkit::LayoutBase::ChildProperty::WIDTH_SPECIFICATION, LayoutLength::IntType( desiredSize.width ) );
410 child.SetProperty( Toolkit::LayoutBase::ChildProperty::HEIGHT_SPECIFICATION, LayoutLength::IntType( desiredSize.height ) );
411 child.SetProperty( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Extents() );
414 Add( *childLayout.Get() );
418 void LayoutGroup::ChildRemovedFromOwner( Actor child )
420 Toolkit::Control control = Toolkit::Control::DownCast( child );
423 Internal::Control& childControlImpl = GetImplementation( control );
424 Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
425 auto childLayout = childControlDataImpl.GetLayout();
428 Remove( *childLayout.Get() );
433 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
435 auto actor = Actor::DownCast( handle );
436 if( actor && index == Actor::Property::LAYOUT_DIRECTION )
443 } // namespace Internal
444 } // namespace Toolkit