Merge "Exporting one constructor used by csharpbinder and making an API private....
[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\n");
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     RequestLayout();
196     for( auto&& child : mImpl->mChildren )
197     {
198       if( child.child->GetOwner() == handle )
199       {
200         child.child->SetLayoutRequested();
201         break;
202       }
203     }
204   }
205 }
206
207 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
208 {
209   child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
210                      Toolkit::ChildLayoutData::WRAP_CONTENT );
211   child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
212                      Toolkit::ChildLayoutData::WRAP_CONTENT );
213 }
214
215 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
216 {
217   for( auto&& child : mImpl->mChildren )
218   {
219     //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
220     {
221       MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
222     }
223   }
224 }
225
226 void LayoutGroup::MeasureChild( LayoutItemPtr child,
227                                 MeasureSpec parentWidthMeasureSpec,
228                                 MeasureSpec parentHeightMeasureSpec )
229 {
230   DALI_LOG_TRACE_METHOD( gLogFilter );
231
232   auto childOwner = child->GetOwner();
233
234   auto control = Toolkit::Control::DownCast( childOwner );
235
236 #if defined( DEBUG_ENABLED )
237   if ( control )
238   {
239     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild natural size(%f, %f)\n",  control.GetNaturalSize().width, control.GetNaturalSize().height );
240   }
241 #endif
242
243   // Get last stored width and height specifications for the child
244   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
245   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
246   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild desiredWidth(%d) desiredHeight(%d)\n", desiredWidth, desiredHeight );
247
248   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
249
250   const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
251                                                                  padding.start + padding.end,
252                                                                  desiredWidth);
253   const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
254                                                                   padding.top + padding.bottom,
255                                                                   desiredHeight);
256
257   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
258 }
259
260 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
261                                            MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
262                                            MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
263 {
264   auto childOwner = child->GetOwner();
265   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
266   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
267
268   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
269
270   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredWidth(%d)\n",  desiredWidth );
271
272   MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
273                                                            padding.start + padding.end +
274                                                            widthUsed, desiredWidth );
275
276   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins desiredHeight(%d)\n",  desiredHeight );
277
278   MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
279                                                             padding.top + padding.bottom +
280                                                             heightUsed, desiredHeight );
281
282   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
283 }
284
285
286 MeasureSpec LayoutGroup::GetChildMeasureSpec(
287   MeasureSpec  measureSpec,
288   LayoutLength padding,
289   LayoutLength childDimension )
290 {
291   auto specMode = measureSpec.GetMode();
292   LayoutLength specSize = measureSpec.GetSize();
293
294   auto size = std::max( LayoutLength(0), specSize - padding ); // reduce available size by the owners padding
295
296   MeasureSpec::IntType resultSize = 0;
297   MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
298
299   switch( specMode )
300   {
301     // Parent has imposed an exact size on us
302     case MeasureSpec::Mode::EXACTLY:
303     {
304       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::EXACTLY\n");
305       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
306       {
307         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension MATCH_PARENT\n");
308
309         // Child wants to be our size. So be it.
310         resultSize = size;
311         resultMode = MeasureSpec::Mode::EXACTLY;
312       }
313       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
314       {
315         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension WRAP_CONTENT\n");
316
317         // Child wants to determine its own size. It can't be
318         // bigger than us.
319         resultSize = size;
320         resultMode = MeasureSpec::Mode::AT_MOST;
321       }
322       else
323       {
324         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension UNSPECIFIED\n");
325         resultSize = childDimension;
326         resultMode = MeasureSpec::Mode::EXACTLY;
327       }
328
329       break;
330     }
331
332       // Parent has imposed a maximum size on us
333     case MeasureSpec::Mode::AT_MOST:
334     {
335       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::AT_MOST\n");
336       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
337       {
338         // Child wants to be our size, but our size is not fixed.
339         // Constrain child to not be bigger than us.
340         resultSize = size;
341         resultMode = MeasureSpec::Mode::AT_MOST;
342       }
343       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
344       {
345         // Child wants to determine its own size. It can't be
346         // bigger than us.
347         resultSize = size;
348         resultMode = MeasureSpec::Mode::AT_MOST;
349       }
350       else
351       {
352         // Child wants a specific size... so be it
353         resultSize = childDimension + padding;
354         resultMode = MeasureSpec::Mode::EXACTLY;
355       }
356
357       break;
358     }
359
360       // Parent asked to see how big we want to be
361     case MeasureSpec::Mode::UNSPECIFIED:
362     {
363       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::UNSPECIFIED\n");
364
365       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
366       {
367         // Child wants to be our size... find out how big it should be
368         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
369         resultMode = MeasureSpec::Mode::UNSPECIFIED;
370       }
371       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
372       {
373         // Child wants to determine its own size.... find out how big
374         // it should be
375         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
376         resultMode = MeasureSpec::Mode::UNSPECIFIED;
377       }
378       else
379       {
380         // Child wants a specific size... let him have it
381         resultSize = childDimension + padding;
382         resultMode = MeasureSpec::Mode::EXACTLY;
383       }
384       break;
385     }
386   }
387
388   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec resultSize(%u)\n", resultSize );
389
390
391   //noinspection ResourceType
392   return MeasureSpec( resultSize, resultMode );
393 }
394
395
396 void LayoutGroup::OnInitialize()
397 {
398   auto control = Toolkit::Control::DownCast( GetOwner() );
399
400   if( control )
401   {
402     // Take ownership of existing children
403     for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
404     {
405       ChildAddedToOwner( control.GetChildAt( childIndex ) );
406     }
407
408     DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
409     DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
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       // Default layout data will be generated by Add().
474     }
475
476     Add( *childLayout.Get() );
477   }
478 }
479
480 void LayoutGroup::ChildRemovedFromOwner( Actor child )
481 {
482   Toolkit::Control control = Toolkit::Control::DownCast( child );
483   if( control )
484   {
485     Internal::Control& childControlImpl = GetImplementation( control );
486     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
487     auto childLayout = childControlDataImpl.GetLayout();
488     if( childLayout )
489     {
490       Remove( *childLayout.Get() );
491     }
492   }
493 }
494
495 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
496 {
497   DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n");
498   auto actor = Actor::DownCast( handle );
499   if( actor &&
500       (
501         index == Actor::Property::LAYOUT_DIRECTION  ||
502         index == Toolkit::Control::Property::PADDING  ||
503         index == Toolkit::Control::Property::MARGIN
504       )
505     )
506   {
507     RequestLayout();
508   }
509 }
510
511 } // namespace Internal
512 } // namespace Toolkit
513 } // namespace Dali