9df05c6cd009cc882de98037263235b9bdbe99a2
[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() ; ++iter )
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");
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   }
195 }
196
197 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
198 {
199   child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
200                      Toolkit::ChildLayoutData::WRAP_CONTENT );
201   child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
202                      Toolkit::ChildLayoutData::WRAP_CONTENT );
203 }
204
205 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
206 {
207   for( auto&& child : mImpl->mChildren )
208   {
209     //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
210     {
211       MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
212     }
213   }
214 }
215
216 void LayoutGroup::MeasureChild( LayoutItemPtr child,
217                                 MeasureSpec parentWidthMeasureSpec,
218                                 MeasureSpec parentHeightMeasureSpec )
219 {
220   DALI_LOG_TRACE_METHOD( gLogFilter );
221
222   auto childOwner = child->GetOwner();
223
224   auto control = Toolkit::Control::DownCast( childOwner );
225
226 #if defined( DEBUG_ENABLED )
227   if ( control )
228   {
229     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins naturalSizewidth(%f)\n",  control.GetNaturalSize().width );
230   }
231 #endif
232
233   // Get last stored width and height specifications for the child
234   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
235   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
236
237   // The size of the control could have changed due to padding being altered, the XXX_SPECIFICATIONs will not have been updated.
238   // So GetNaturalSize is called, if the control's natural size includes padding then th size will be updated.
239   if ( control )
240   {
241     auto desiredSize = control.GetNaturalSize();  // Get's child control's size which could include new padding values.
242
243     // Check if WIDTH_SPECIFICATION was a size value, if so then get update to child controls's width.
244     if ( desiredWidth > 0  )
245     {
246       desiredWidth = desiredSize.width;
247       childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, LayoutLength::IntType( desiredWidth ) );
248     }
249
250     // Check if HEIGHT_SPECIFICATION was a size value, if so then get update to child controls's height.
251     if ( desiredHeight > 0)
252     {
253       desiredHeight = desiredSize.height;
254       childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, LayoutLength::IntType( desiredHeight ) );
255     }
256   }
257
258   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild desiredWidth(%d)\n",  desiredWidth );
259
260   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
261
262   const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
263                                                                  padding.start + padding.end,
264                                                                  desiredWidth);
265   const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
266                                                                   padding.top + padding.bottom,
267                                                                   desiredHeight);
268
269   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
270 }
271
272 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
273                                            MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
274                                            MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
275 {
276   auto childOwner = child->GetOwner();
277   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
278   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
279
280   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
281
282
283   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredWidth(%d)\n",  desiredWidth );
284
285   MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
286                                                            padding.start + padding.end +
287                                                            widthUsed, desiredWidth );
288
289   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredHeight(%d)\n",  desiredHeight );
290
291   MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
292                                                             padding.top + padding.bottom +
293                                                             heightUsed, desiredHeight );
294
295   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
296 }
297
298
299 MeasureSpec LayoutGroup::GetChildMeasureSpec(
300   MeasureSpec  measureSpec,
301   LayoutLength padding,
302   LayoutLength childDimension )
303 {
304   auto specMode = measureSpec.GetMode();
305   LayoutLength specSize = measureSpec.GetSize();
306
307   auto size = std::max( LayoutLength(0), specSize - padding ); // reduce available size by the owners padding
308
309   MeasureSpec::IntType resultSize = 0;
310   MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
311
312   switch( specMode )
313   {
314     // Parent has imposed an exact size on us
315     case MeasureSpec::Mode::EXACTLY:
316     {
317       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::EXACTLY\n");
318       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
319       {
320         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension MATCH_PARENT\n");
321
322         // Child wants to be our size. So be it.
323         resultSize = size;
324         resultMode = MeasureSpec::Mode::EXACTLY;
325       }
326       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
327       {
328         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension WRAP_CONTENT\n");
329
330         // Child wants to determine its own size. It can't be
331         // bigger than us.
332         resultSize = size;
333         resultMode = MeasureSpec::Mode::AT_MOST;
334       }
335       else
336       {
337         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension UNSPECIFIED\n");
338         resultSize = childDimension;
339         resultMode = MeasureSpec::Mode::EXACTLY;
340       }
341
342       break;
343     }
344
345       // Parent has imposed a maximum size on us
346     case MeasureSpec::Mode::AT_MOST:
347     {
348       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::AT_MOST\n");
349       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
350       {
351         // Child wants to be our size, but our size is not fixed.
352         // Constrain child to not be bigger than us.
353         resultSize = size;
354         resultMode = MeasureSpec::Mode::AT_MOST;
355       }
356       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
357       {
358         // Child wants to determine its own size. It can't be
359         // bigger than us.
360         resultSize = size;
361         resultMode = MeasureSpec::Mode::AT_MOST;
362       }
363       else
364       {
365         // Child wants a specific size... so be it
366         resultSize = childDimension + padding;
367         resultMode = MeasureSpec::Mode::EXACTLY;
368       }
369
370       break;
371     }
372
373       // Parent asked to see how big we want to be
374     case MeasureSpec::Mode::UNSPECIFIED:
375     {
376       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::UNSPECIFIED\n");
377
378       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
379       {
380         // Child wants to be our size... find out how big it should be
381         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
382         resultMode = MeasureSpec::Mode::UNSPECIFIED;
383       }
384       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
385       {
386         // Child wants to determine its own size.... find out how big
387         // it should be
388         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
389         resultMode = MeasureSpec::Mode::UNSPECIFIED;
390       }
391       else
392       {
393         // Child wants a specific size... let him have it
394         resultSize = childDimension + padding;
395         resultMode = MeasureSpec::Mode::EXACTLY;
396       }
397       break;
398     }
399   }
400
401   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec resultSize(%u)\n", resultSize );
402
403
404   //noinspection ResourceType
405   return MeasureSpec( resultSize, resultMode );
406 }
407
408
409 void LayoutGroup::OnInitialize()
410 {
411   auto control = Toolkit::Control::DownCast( GetOwner() );
412
413   if( control )
414   {
415     // Take ownership of existing children
416     for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
417     {
418       ChildAddedToOwner( control.GetChildAt( childIndex ) );
419     }
420
421     DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
422     DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
423     DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
424   }
425 }
426
427 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
428 {
429   DoRegisterChildProperties( containerType );
430 }
431
432 void LayoutGroup::OnUnparent()
433 {
434   RemoveAll();
435 }
436
437 void LayoutGroup::ChildAddedToOwner( Actor child )
438 {
439   LayoutItemPtr childLayout;
440   Toolkit::Control control = Toolkit::Control::DownCast( child );
441   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner(%s)\n", control.GetName().c_str() );
442
443   if( control ) // Can only support adding Controls, not Actors to layout
444   {
445     Internal::Control& childControlImpl = GetImplementation( control );
446     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
447     childLayout = childControlDataImpl.GetLayout();
448
449     if( ! childLayout )
450     {
451       // If the child doesn't already have a layout, then create a LayoutItem for it.
452       childLayout = LayoutItem::New( control );
453       childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
454
455       auto desiredSize = control.GetNaturalSize();
456       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner desiredSize(%f,%f) (naturalSize)\n", desiredSize.width, desiredSize.height );
457
458       childControlDataImpl.SetLayout( *childLayout.Get() );
459
460       // Default layout data for this object
461       child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, LayoutLength::IntType( desiredSize.width ) );
462       child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, LayoutLength::IntType( desiredSize.height ) );
463     }
464
465     Add( *childLayout.Get() );
466   }
467 }
468
469 void LayoutGroup::ChildRemovedFromOwner( Actor child )
470 {
471   Toolkit::Control control = Toolkit::Control::DownCast( child );
472   if( control )
473   {
474     Internal::Control& childControlImpl = GetImplementation( control );
475     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
476     auto childLayout = childControlDataImpl.GetLayout();
477     if( childLayout )
478     {
479       Remove( *childLayout.Get() );
480     }
481   }
482 }
483
484 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
485 {
486   DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n");
487   auto actor = Actor::DownCast( handle );
488   if( actor &&
489       (
490         index == Actor::Property::LAYOUT_DIRECTION  ||
491         index == Toolkit::Control::Property::PADDING  ||
492         index == Toolkit::Control::Property::MARGIN
493       )
494     )
495   {
496     RequestLayout();
497   }
498 }
499
500 } // namespace Internal
501 } // namespace Toolkit
502 } // namespace Dali