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