d9ff6ffdc9e12e500ed6719da575275e7e264fb9
[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-toolkit/devel-api/layouting/layout-group-impl.h>
19
20 // EXTERNAL INCLUDES
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>
25
26 // INTERNAL INCLUDES
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>
30
31 namespace
32 {
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_LAYOUT" );
35 #endif
36 }
37
38 namespace Dali
39 {
40 namespace Toolkit
41 {
42 namespace Internal
43 {
44
45 LayoutGroup::LayoutGroup()
46 : mImpl( new LayoutGroup::Impl() ),
47   mSlotDelegate(this)
48 {
49 }
50
51 LayoutGroup::~LayoutGroup()
52 {
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.
55 }
56
57 Toolkit::LayoutGroup::LayoutId LayoutGroup::Add( LayoutItem& child )
58 {
59   LayoutParent* oldParent = child.GetParent();
60   if( oldParent )
61   {
62     LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
63     if( parentGroup )
64     {
65       parentGroup->Remove( child );
66     }
67   }
68
69   Impl::ChildLayout childLayout;
70   childLayout.layoutId = mImpl->mNextLayoutId++;
71   childLayout.child = &child;
72   mImpl->mChildren.emplace_back( childLayout );
73
74   auto owner = child.GetOwner();
75
76   // If the owner does not have any LayoutItem child properties, add them
77   if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) )
78   {
79     // Set default properties for LayoutGroup and LayoutItem.
80     // Deriving classes can override OnChildAdd() to add their own default properties
81     GenerateDefaultChildPropertyValues( owner );
82   }
83
84   // Inform deriving classes that this child has been added
85   OnChildAdd( *childLayout.child.Get() );
86
87   // Now listen to future changes to the child properties.
88   DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
89
90   RequestLayout();
91
92   return childLayout.layoutId;
93 }
94
95 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
96 {
97   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
98   {
99     if( iter->layoutId == childId )
100     {
101       OnChildRemove( *iter->child.Get() );
102       mImpl->mChildren.erase(iter);
103       break;
104     }
105   }
106   RequestLayout();
107 }
108
109 void LayoutGroup::Remove( LayoutItem& child )
110 {
111   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
112   {
113     if( iter->child.Get() == &child )
114     {
115       OnChildRemove( *iter->child.Get() );
116       mImpl->mChildren.erase(iter);
117       break;
118     }
119   }
120   RequestLayout();
121 }
122
123 void LayoutGroup::RemoveAll()
124 {
125   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; )
126   {
127     OnChildRemove( *iter->child.Get() );
128     iter = mImpl->mChildren.erase(iter);
129   }
130 }
131
132 unsigned int LayoutGroup::GetChildCount() const
133 {
134   return mImpl->mChildren.size();
135 }
136
137 LayoutItemPtr LayoutGroup::GetChildAt( unsigned int index ) const
138 {
139   DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
140   return mImpl->mChildren[ index ].child;
141 }
142
143 LayoutItemPtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
144 {
145   for( auto&& childLayout : mImpl->mChildren )
146   {
147     if( childLayout.layoutId == childId )
148     {
149       return childLayout.child;
150     }
151   }
152   return NULL;
153 }
154
155 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutItem& child ) const
156 {
157   for( auto&& childLayout : mImpl->mChildren )
158   {
159     if( childLayout.child.Get() == &child )
160     {
161       return childLayout.layoutId;
162     }
163   }
164   return Toolkit::LayoutGroup::UNKNOWN_ID;
165 }
166
167 void LayoutGroup::OnChildAdd( LayoutItem& child )
168 {
169 }
170
171 void LayoutGroup::OnChildRemove( LayoutItem& child )
172 {
173 }
174
175 void LayoutGroup::DoInitialize()
176 {
177 }
178
179 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
180 {
181 }
182
183 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
184 {
185   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::OnSetChildProperties\n");
186
187   if ( ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) &&
188          ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
189        ||
190        ( index == Toolkit::Control::Property::MARGIN || index == Toolkit::Control::Property::PADDING ) )
191   {
192     // If any child properties are set, must perform relayout
193     RequestLayout();
194     for( auto&& child : mImpl->mChildren )
195     {
196       if( child.child->GetOwner() == handle )
197       {
198         child.child->SetLayoutRequested();
199         break;
200       }
201     }
202   }
203 }
204
205 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
206 {
207   child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
208                      Toolkit::ChildLayoutData::WRAP_CONTENT );
209   child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
210                      Toolkit::ChildLayoutData::WRAP_CONTENT );
211 }
212
213 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
214 {
215   for( auto&& child : mImpl->mChildren )
216   {
217     //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
218     {
219       MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
220     }
221   }
222 }
223
224 void LayoutGroup::MeasureChild( LayoutItemPtr child,
225                                 MeasureSpec parentWidthMeasureSpec,
226                                 MeasureSpec parentHeightMeasureSpec )
227 {
228   DALI_LOG_TRACE_METHOD( gLogFilter );
229
230   auto childOwner = child->GetOwner();
231
232   auto control = Toolkit::Control::DownCast( childOwner );
233
234 #if defined( DEBUG_ENABLED )
235   if ( control )
236   {
237     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild natural size(%f, %f)\n",  control.GetNaturalSize().width, control.GetNaturalSize().height );
238   }
239 #endif
240
241   // Get last stored width and height specifications for the child
242   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
243   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
244   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild desiredWidth(%d) desiredHeight(%d)\n", desiredWidth, desiredHeight );
245
246   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
247
248   const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
249                                                                  padding.start + padding.end,
250                                                                  desiredWidth);
251   const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
252                                                                   padding.top + padding.bottom,
253                                                                   desiredHeight);
254
255   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
256 }
257
258 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
259                                            MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
260                                            MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
261 {
262   auto childOwner = child->GetOwner();
263   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
264   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
265
266   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
267
268   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredWidth(%d)\n",  desiredWidth );
269
270   MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
271                                                            padding.start + padding.end +
272                                                            widthUsed, desiredWidth );
273
274   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredHeight(%d)\n",  desiredHeight );
275
276   MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
277                                                             padding.top + padding.bottom +
278                                                             heightUsed, desiredHeight );
279
280   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
281 }
282
283
284 MeasureSpec LayoutGroup::GetChildMeasureSpec(
285   MeasureSpec  measureSpec,
286   LayoutLength padding,
287   LayoutLength childDimension )
288 {
289   auto specMode = measureSpec.GetMode();
290   LayoutLength specSize = measureSpec.GetSize();
291
292   auto size = std::max( LayoutLength(0), specSize - padding ); // reduce available size by the owners padding
293
294   MeasureSpec::IntType resultSize = 0;
295   MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
296
297   switch( specMode )
298   {
299     // Parent has imposed an exact size on us
300     case MeasureSpec::Mode::EXACTLY:
301     {
302       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::EXACTLY\n");
303       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
304       {
305         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension MATCH_PARENT\n");
306
307         // Child wants to be our size. So be it.
308         resultSize = size;
309         resultMode = MeasureSpec::Mode::EXACTLY;
310       }
311       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
312       {
313         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension WRAP_CONTENT\n");
314
315         // Child wants to determine its own size. It can't be
316         // bigger than us.
317         resultSize = size;
318         resultMode = MeasureSpec::Mode::AT_MOST;
319       }
320       else
321       {
322         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension UNSPECIFIED\n");
323         resultSize = childDimension;
324         resultMode = MeasureSpec::Mode::EXACTLY;
325       }
326
327       break;
328     }
329
330       // Parent has imposed a maximum size on us
331     case MeasureSpec::Mode::AT_MOST:
332     {
333       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::AT_MOST\n");
334       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
335       {
336         // Child wants to be our size, but our size is not fixed.
337         // Constrain child to not be bigger than us.
338         resultSize = size;
339         resultMode = MeasureSpec::Mode::AT_MOST;
340       }
341       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
342       {
343         // Child wants to determine its own size. It can't be
344         // bigger than us.
345         resultSize = size;
346         resultMode = MeasureSpec::Mode::AT_MOST;
347       }
348       else
349       {
350         // Child wants a specific size... so be it
351         resultSize = childDimension + padding;
352         resultMode = MeasureSpec::Mode::EXACTLY;
353       }
354
355       break;
356     }
357
358       // Parent asked to see how big we want to be
359     case MeasureSpec::Mode::UNSPECIFIED:
360     {
361       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::UNSPECIFIED\n");
362
363       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
364       {
365         // Child wants to be our size... find out how big it should be
366         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
367         resultMode = MeasureSpec::Mode::UNSPECIFIED;
368       }
369       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
370       {
371         // Child wants to determine its own size.... find out how big
372         // it should be
373         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
374         resultMode = MeasureSpec::Mode::UNSPECIFIED;
375       }
376       else
377       {
378         // Child wants a specific size... let him have it
379         resultSize = childDimension + padding;
380         resultMode = MeasureSpec::Mode::EXACTLY;
381       }
382       break;
383     }
384   }
385
386   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec resultSize(%u)\n", resultSize );
387
388
389   //noinspection ResourceType
390   return MeasureSpec( resultSize, resultMode );
391 }
392
393
394 void LayoutGroup::OnInitialize()
395 {
396   auto control = Toolkit::Control::DownCast( GetOwner() );
397
398   if( control )
399   {
400     // Take ownership of existing children
401     for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
402     {
403       ChildAddedToOwner( control.GetChildAt( childIndex ) );
404     }
405
406     DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
407     DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
408     DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
409   }
410 }
411
412 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
413 {
414   DoRegisterChildProperties( containerType );
415 }
416
417 void LayoutGroup::OnUnparent()
418 {
419   RemoveAll();
420 }
421
422 void LayoutGroup::ChildAddedToOwner( Actor child )
423 {
424   LayoutItemPtr childLayout;
425   Toolkit::Control control = Toolkit::Control::DownCast( child );
426   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner(%s)\n", control.GetName().c_str() );
427
428   if( control ) // Can only support adding Controls, not Actors to layout
429   {
430     Internal::Control& childControlImpl = GetImplementation( control );
431     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
432     childLayout = childControlDataImpl.GetLayout();
433
434     if( ! childLayout )
435     {
436       // If the child doesn't already have a layout, then create a LayoutItem for it.
437       childLayout = LayoutItem::New( control );
438       childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
439 #if defined(DEBUG_ENABLED)
440       auto desiredSize = control.GetNaturalSize();
441       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner desiredSize(%f,%f) (naturalSize)\n", desiredSize.width, desiredSize.height );
442 #endif
443       childControlDataImpl.SetLayout( *childLayout.Get() );
444
445       // Default layout data will be generated by Add().
446     }
447
448     Add( *childLayout.Get() );
449   }
450 }
451
452 void LayoutGroup::ChildRemovedFromOwner( Actor child )
453 {
454   Toolkit::Control control = Toolkit::Control::DownCast( child );
455   if( control )
456   {
457     Internal::Control& childControlImpl = GetImplementation( control );
458     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
459     auto childLayout = childControlDataImpl.GetLayout();
460     if( childLayout )
461     {
462       Remove( *childLayout.Get() );
463     }
464   }
465 }
466
467 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
468 {
469   DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n");
470   auto actor = Actor::DownCast( handle );
471   if( actor &&
472       (
473         index == Actor::Property::LAYOUT_DIRECTION  ||
474         index == Toolkit::Control::Property::PADDING  ||
475         index == Toolkit::Control::Property::MARGIN
476       )
477     )
478   {
479     RequestLayout();
480   }
481 }
482
483 } // namespace Internal
484 } // namespace Toolkit
485 } // namespace Dali