844e35a1ab92eb043724b58500e085d916a96ee4
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / layouting / layout-group-impl.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 // CLASS HEADER
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>
25
26
27 namespace
28 {
29 const char* MARGIN_SPECIFICATION_NAME( "marginSpec" );
30 }
31
32 namespace Dali
33 {
34 namespace Toolkit
35 {
36 namespace Internal
37 {
38
39 LayoutGroup::LayoutGroup()
40 : mImpl( new LayoutGroup::Impl() ),
41   mSlotDelegate(this)
42 {
43 }
44
45 LayoutGroup::~LayoutGroup()
46 {
47 }
48
49 Toolkit::LayoutGroup::LayoutId LayoutGroup::Add( LayoutBase& child )
50 {
51   LayoutParent* oldParent = child.GetParent();
52   if( oldParent )
53   {
54     LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
55     if( parentGroup )
56     {
57       parentGroup->Remove( child );
58     }
59   }
60
61   Impl::ChildLayout childLayout;
62   childLayout.layoutId = mImpl->mNextLayoutId++;
63   childLayout.child = &child;
64   mImpl->mChildren.emplace_back( childLayout );
65
66   auto owner = child.GetOwner();
67
68   // If the owner does not have any LayoutBase child properties, add them
69   if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutBase::ChildProperty::WIDTH_SPECIFICATION ) )
70   {
71     // Set default properties for LayoutGroup and LayoutBase.
72     // Deriving classes can override OnChildAdd() to add their own default properties
73     GenerateDefaultChildPropertyValues( owner );
74   }
75
76   // Inform deriving classes that this child has been added
77   OnChildAdd( *childLayout.child.Get() );
78
79   // Now listen to future changes to the child properties.
80   DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
81
82   RequestLayout();
83
84   return childLayout.layoutId;
85 }
86
87 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
88 {
89   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
90   {
91     if( iter->layoutId == childId )
92     {
93       OnChildRemove( *iter->child.Get() );
94       mImpl->mChildren.erase(iter);
95       break;
96     }
97   }
98   RequestLayout();
99 }
100
101 void LayoutGroup::Remove( LayoutBase& child )
102 {
103   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
104   {
105     if( iter->child.Get() == &child )
106     {
107       OnChildRemove( *iter->child.Get() );
108       mImpl->mChildren.erase(iter);
109       break;
110     }
111   }
112   RequestLayout();
113 }
114
115 void LayoutGroup::RemoveAll()
116 {
117   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
118   {
119     OnChildRemove( *iter->child.Get() );
120     iter = mImpl->mChildren.erase(iter);
121   }
122 }
123
124 unsigned int LayoutGroup::GetChildCount() const
125 {
126   return mImpl->mChildren.size();
127 }
128
129 LayoutBasePtr LayoutGroup::GetChildAt( unsigned int index ) const
130 {
131   DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
132   return mImpl->mChildren[ index ].child;
133 }
134
135 LayoutBasePtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
136 {
137   for( auto&& childLayout : mImpl->mChildren )
138   {
139     if( childLayout.layoutId == childId )
140     {
141       return childLayout.child;
142     }
143   }
144   return NULL;
145 }
146
147 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutBase& child ) const
148 {
149   for( auto&& childLayout : mImpl->mChildren )
150   {
151     if( childLayout.child.Get() == &child )
152     {
153       return childLayout.layoutId;
154     }
155   }
156   return Toolkit::LayoutGroup::UNKNOWN_ID;
157 }
158
159 void LayoutGroup::OnChildAdd( LayoutBase& child )
160 {
161 }
162
163 void LayoutGroup::OnChildRemove( LayoutBase& child )
164 {
165 }
166
167 void LayoutGroup::DoInitialize()
168 {
169 }
170
171 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
172 {
173 }
174
175 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
176 {
177   if ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) && ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
178   {
179     // If any child properties are set, must perform relayout
180     RequestLayout();
181   }
182 }
183
184 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
185 {
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() );
191 }
192
193 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
194 {
195   for( auto&& child : mImpl->mChildren )
196   {
197     //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
198     {
199       MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
200     }
201   }
202 }
203
204 void LayoutGroup::MeasureChild( LayoutBasePtr child,
205                                 MeasureSpec parentWidthMeasureSpec,
206                                 MeasureSpec parentHeightMeasureSpec )
207 {
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 );
211
212   auto padding = GetPadding();
213
214   const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
215                                                                  padding.start + padding.end,
216                                                                  desiredWidth);
217   const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
218                                                                   padding.top + padding.bottom,
219                                                                   desiredHeight);
220
221   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
222 }
223
224 void LayoutGroup::MeasureChildWithMargins( LayoutBasePtr child,
225                                            MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
226                                            MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
227 {
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();
233
234   MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
235                                                            padding.start + padding.end +
236                                                            desiredMargin.start + desiredMargin.end +
237                                                            widthUsed, desiredWidth );
238
239   MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
240                                                             padding.top + padding.bottom +
241                                                             desiredMargin.top + desiredMargin.end +
242                                                             heightUsed, desiredHeight );
243
244   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
245 }
246
247
248 MeasureSpec LayoutGroup::GetChildMeasureSpec(
249   MeasureSpec  measureSpec,
250   LayoutLength padding,
251   LayoutLength childDimension )
252 {
253   auto specMode = measureSpec.GetMode();
254   LayoutLength specSize = measureSpec.GetSize();
255
256   auto size = std::max( LayoutLength(0), specSize - padding );
257
258   MeasureSpec::IntType resultSize = 0;
259   MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
260
261   switch( specMode )
262   {
263     // Parent has imposed an exact size on us
264     case MeasureSpec::Mode::EXACTLY:
265     {
266
267       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
268       {
269         // Child wants to be our size. So be it.
270         resultSize = size;
271         resultMode = MeasureSpec::Mode::EXACTLY;
272       }
273       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
274       {
275         // Child wants to determine its own size. It can't be
276         // bigger than us.
277         resultSize = size;
278         resultMode = MeasureSpec::Mode::AT_MOST;
279       }
280       else
281       {
282         resultSize = childDimension;
283         resultMode = MeasureSpec::Mode::EXACTLY;
284       }
285
286       break;
287     }
288
289       // Parent has imposed a maximum size on us
290     case MeasureSpec::Mode::AT_MOST:
291     {
292       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
293       {
294         // Child wants to be our size, but our size is not fixed.
295         // Constrain child to not be bigger than us.
296         resultSize = size;
297         resultMode = MeasureSpec::Mode::AT_MOST;
298       }
299       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
300       {
301         // Child wants to determine its own size. It can't be
302         // bigger than us.
303         resultSize = size;
304         resultMode = MeasureSpec::Mode::AT_MOST;
305       }
306       else
307       {
308         // Child wants a specific size... so be it
309         resultSize = childDimension;
310         resultMode = MeasureSpec::Mode::EXACTLY;
311       }
312
313       break;
314     }
315
316       // Parent asked to see how big we want to be
317     case MeasureSpec::Mode::UNSPECIFIED:
318     {
319       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
320       {
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;
324       }
325       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
326       {
327         // Child wants to determine its own size.... find out how big
328         // it should be
329         resultSize = LayoutBase::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
330         resultMode = MeasureSpec::Mode::UNSPECIFIED;
331       }
332       else
333       {
334         // Child wants a specific size... let him have it
335         resultSize = childDimension;
336         resultMode = MeasureSpec::Mode::EXACTLY;
337       }
338       break;
339     }
340   }
341
342   //noinspection ResourceType
343   return MeasureSpec( resultSize, resultMode );
344 }
345
346
347 void LayoutGroup::OnInitialize()
348 {
349   auto control = Toolkit::Control::DownCast( GetOwner() );
350
351   if( control )
352   {
353     // Take ownership of existing children
354     for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
355     {
356       ChildAddedToOwner( control.GetChildAt( childIndex ) );
357     }
358
359     DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
360     DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
361     DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
362   }
363 }
364
365 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
366 {
367   auto typeInfo = TypeRegistry::Get().GetTypeInfo( containerType );
368   if( typeInfo )
369   {
370     Property::IndexContainer indices;
371     typeInfo.GetChildPropertyIndices( indices );
372
373     if( std::find( indices.Begin(), indices.End(), Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION ) ==
374         indices.End() )
375     {
376       ChildPropertyRegistration( typeInfo.GetName(), MARGIN_SPECIFICATION_NAME, Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Property::EXTENTS );
377     }
378   }
379
380   DoRegisterChildProperties( containerType );
381 }
382
383 void LayoutGroup::OnUnparent()
384 {
385   RemoveAll();
386 }
387
388 void LayoutGroup::ChildAddedToOwner( Actor child )
389 {
390   LayoutBasePtr childLayout;
391   Toolkit::Control control = Toolkit::Control::DownCast( child );
392
393   if( control ) // Can only support adding Controls, not Actors to layout
394   {
395     Internal::Control& childControlImpl = GetImplementation( control );
396     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
397     childLayout = childControlDataImpl.GetLayout();
398
399     if( ! childLayout )
400     {
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?
404
405       auto desiredSize = control.GetNaturalSize();
406       childControlDataImpl.SetLayout( *childLayout.Get() );
407
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() );
412     }
413
414     Add( *childLayout.Get() );
415   }
416 }
417
418 void LayoutGroup::ChildRemovedFromOwner( Actor child )
419 {
420   Toolkit::Control control = Toolkit::Control::DownCast( child );
421   if( control )
422   {
423     Internal::Control& childControlImpl = GetImplementation( control );
424     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
425     auto childLayout = childControlDataImpl.GetLayout();
426     if( childLayout )
427     {
428       Remove( *childLayout.Get() );
429     }
430   }
431 }
432
433 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
434 {
435   auto actor = Actor::DownCast( handle );
436   if( actor && index == Actor::Property::LAYOUT_DIRECTION )
437   {
438     RequestLayout();
439   }
440 }
441
442
443 } // namespace Internal
444 } // namespace Toolkit
445 } // namespace Dali