[Tizen] Store LayoutItem target size when set explictly
[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 #include <dali-toolkit/internal/layouting/size-negotiation-mapper.h>
31
32 namespace
33 {
34 #if defined(DEBUG_ENABLED)
35 Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
36 #endif
37 }
38
39 namespace Dali
40 {
41 namespace Toolkit
42 {
43 namespace Internal
44 {
45
46 LayoutGroup::LayoutGroup()
47 : mImpl( new LayoutGroup::Impl() ),
48   mSlotDelegate(this)
49 {
50 }
51
52 LayoutGroupPtr LayoutGroup::New( Handle& owner )
53 {
54   LayoutGroupPtr layoutPtr = new LayoutGroup();
55   return layoutPtr;
56 }
57
58 LayoutGroup::~LayoutGroup()
59 {
60   // An object with a unique_ptr to an opaque structure must define it's destructor in the translation unit
61   // where the opaque structure is defined. It cannot use the default method in the header file.
62 }
63
64 Toolkit::LayoutGroup::LayoutId LayoutGroup::Add( LayoutItem& child )
65 {
66   LayoutParent* oldParent = child.GetParent();
67   if( oldParent )
68   {
69     LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
70     if( parentGroup )
71     {
72       parentGroup->Remove( child );
73     }
74   }
75
76   Impl::ChildLayout childLayout;
77   childLayout.layoutId = mImpl->mNextLayoutId++;
78   childLayout.child = &child;
79   mImpl->mChildren.emplace_back( childLayout );
80
81   child.SetParent( this );
82
83   auto owner = child.GetOwner();
84
85   // If the owner does not have any LayoutItem child properties, add them
86   if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) )
87   {
88     // Set default properties for LayoutGroup and LayoutItem.
89     // Deriving classes can override OnChildAdd() to add their own default properties
90     GenerateDefaultChildPropertyValues( owner );
91   }
92
93   // Inform deriving classes that this child has been added
94   OnChildAdd( *childLayout.child.Get() );
95
96   // Now listen to future changes to the child properties.
97   DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
98
99   RequestLayout();
100
101   return childLayout.layoutId;
102 }
103
104 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
105 {
106   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
107   {
108     if( iter->layoutId == childId )
109     {
110       RemoveChild( *iter->child.Get() );
111       mImpl->mChildren.erase(iter);
112       break;
113     }
114   }
115   RequestLayout();
116 }
117
118 void LayoutGroup::Remove( LayoutItem& child )
119 {
120   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
121   {
122     if( iter->child.Get() == &child )
123     {
124       RemoveChild( *iter->child.Get() );
125       mImpl->mChildren.erase(iter);
126       break;
127     }
128   }
129   RequestLayout();
130 }
131
132 Toolkit::LayoutGroup::LayoutId LayoutGroup::Insert( LayoutItem& target, LayoutItem& child )
133 {
134   LayoutParent* oldParent = child.GetParent();
135   if( oldParent )
136   {
137     LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
138     if( parentGroup )
139     {
140       parentGroup->Remove( child );
141     }
142   }
143
144   // Find target position
145   std::vector< Impl::ChildLayout >::iterator position;
146   for( auto iter = mImpl->mChildren.begin(); iter != mImpl->mChildren.end(); ++iter )
147   {
148     if( iter->child.Get() == &target )
149     {
150       position = iter;
151       break;
152     }
153   }
154
155   Impl::ChildLayout childLayout;
156   childLayout.layoutId = mImpl->mNextLayoutId++;
157   childLayout.child = &child;
158   mImpl->mChildren.insert( position, childLayout );
159
160   child.SetParent( this );
161
162   auto owner = child.GetOwner();
163
164   // Inform deriving classes that this child has been added
165   OnChildAdd( *childLayout.child.Get() );
166
167   // Now listen to future changes to the child properties.
168   DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
169
170   RequestLayout();
171
172   return childLayout.layoutId;
173 }
174
175 Toolkit::LayoutGroup::LayoutId LayoutGroup::Move( LayoutItem& target, LayoutItem& child )
176 {
177   // Remove child from the previous position
178   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
179   {
180     if( iter->child.Get() == &child )
181     {
182       mImpl->mChildren.erase( iter );
183       break;
184     }
185   }
186
187   // Find target position
188   std::vector< Impl::ChildLayout >::iterator position;
189   for( auto iter = mImpl->mChildren.begin(); iter != mImpl->mChildren.end(); ++iter )
190   {
191     if( iter->child.Get() == &target )
192     {
193       position = iter;
194       break;
195     }
196   }
197
198   Impl::ChildLayout childLayout;
199   childLayout.layoutId = mImpl->mNextLayoutId++;
200   childLayout.child = &child;
201   mImpl->mChildren.insert( position, childLayout );
202
203   RequestLayout();
204
205   return childLayout.layoutId;
206 }
207
208 Toolkit::LayoutGroup::LayoutId LayoutGroup::MoveBack( LayoutItem& child )
209 {
210   // Remove child from the previous position
211   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
212   {
213     if( iter->child.Get() == &child )
214     {
215       mImpl->mChildren.erase( iter );
216       break;
217     }
218   }
219
220   Impl::ChildLayout childLayout;
221   childLayout.layoutId = mImpl->mNextLayoutId++;
222   childLayout.child = &child;
223   mImpl->mChildren.emplace_back( childLayout );
224
225   RequestLayout();
226
227   return childLayout.layoutId;
228 }
229
230 void LayoutGroup::RemoveAll()
231 {
232   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; )
233   {
234     RemoveChild( *iter->child.Get() );
235     iter = mImpl->mChildren.erase(iter);
236   }
237 }
238
239 unsigned int LayoutGroup::GetChildCount() const
240 {
241   return mImpl->mChildren.size();
242 }
243
244 LayoutItemPtr LayoutGroup::GetChildAt( unsigned int index ) const
245 {
246   DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
247   return mImpl->mChildren[ index ].child;
248 }
249
250 LayoutItemPtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
251 {
252   for( auto&& childLayout : mImpl->mChildren )
253   {
254     if( childLayout.layoutId == childId )
255     {
256       return childLayout.child;
257     }
258   }
259   return NULL;
260 }
261
262 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutItem& child ) const
263 {
264   for( auto&& childLayout : mImpl->mChildren )
265   {
266     if( childLayout.child.Get() == &child )
267     {
268       return childLayout.layoutId;
269     }
270   }
271   return Toolkit::LayoutGroup::UNKNOWN_ID;
272 }
273
274 void LayoutGroup::OnChildAdd( LayoutItem& child )
275 {
276 }
277
278 void LayoutGroup::OnChildRemove( LayoutItem& child )
279 {
280 }
281
282 void LayoutGroup::DoInitialize()
283 {
284 }
285
286 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
287 {
288 }
289
290 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
291 {
292   DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LayoutGroup::OnSetChildProperties property(" << handle.GetPropertyName(index) << ")\n" );
293
294   if ( ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) &&
295          ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
296        ||
297        ( index == Toolkit::Control::Property::MARGIN || index == Toolkit::Control::Property::PADDING ) )
298   {
299     // If any child properties are set, must perform relayout
300     for( auto&& child : mImpl->mChildren )
301     {
302       if( child.child->GetOwner() == handle )
303       {
304         child.child->RequestLayout();
305         break;
306       }
307     }
308   }
309 }
310
311 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
312 {
313   child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
314                      Toolkit::ChildLayoutData::WRAP_CONTENT );
315   child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
316                      Toolkit::ChildLayoutData::WRAP_CONTENT );
317 }
318
319 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
320 {
321   for( auto&& child : mImpl->mChildren )
322   {
323     //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
324     {
325       MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
326     }
327   }
328 }
329
330 void LayoutGroup::MeasureChild( LayoutItemPtr child,
331                                 MeasureSpec parentWidthMeasureSpec,
332                                 MeasureSpec parentHeightMeasureSpec )
333 {
334   DALI_LOG_TRACE_METHOD( gLogFilter );
335
336   auto childOwner = child->GetOwner();
337
338   auto control = Toolkit::Control::DownCast( childOwner );
339
340 #if defined( DEBUG_ENABLED )
341   if ( control )
342   {
343     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild(%s) natural size(%f, %f)\n",
344                    control.GetName().c_str(), control.GetNaturalSize().width, control.GetNaturalSize().height );
345   }
346 #endif
347
348
349   // Get last stored width and height specifications for the child
350   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
351   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
352   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChild child WIDTH_SPEC(%d) child HEIGHT SPEC(%d)\n", desiredWidth, desiredHeight );
353
354   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
355
356   const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
357                                                                  padding.start + padding.end,
358                                                                  desiredWidth);
359   const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
360                                                                   padding.top + padding.bottom,
361                                                                   desiredHeight);
362
363   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
364 }
365
366 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
367                                            MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
368                                            MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
369 {
370   auto childOwner = child->GetOwner();
371   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
372   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
373
374   auto padding = GetPadding(); // Padding of this layout's owner, not of the child being measured.
375
376   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins child WIDTH_SPEC(%d)\n",  desiredWidth );
377
378   MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
379                                                            LayoutLength( padding.start + padding.end ) +
380                                                            widthUsed, desiredWidth );
381
382   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::MeasureChildWithMargins child HEIGHT_SPEC(%d)\n",  desiredHeight );
383
384   MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
385                                                             LayoutLength( padding.top + padding.bottom )+
386                                                             heightUsed, desiredHeight );
387
388   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
389 }
390
391
392 MeasureSpec LayoutGroup::GetChildMeasureSpec(
393   MeasureSpec  measureSpec,
394   LayoutLength padding,
395   LayoutLength childDimension )
396 {
397   auto specMode = measureSpec.GetMode();
398   LayoutLength specSize = measureSpec.GetSize();
399
400   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension(%f)\n", childDimension.AsInteger() );
401
402   LayoutLength size = std::max( LayoutLength(0), specSize - padding ); // reduce available size by the owners padding
403
404   LayoutLength resultSize = 0;
405   MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
406
407   switch( specMode ) // Parents specMode
408   {
409     // Parent has imposed an exact size on us
410     case MeasureSpec::Mode::EXACTLY:
411     {
412       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::EXACTLY\n");
413       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
414       {
415         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension MATCH_PARENT\n");
416
417         // Child wants to be our size. So be it.
418         resultSize = size;
419         resultMode = MeasureSpec::Mode::EXACTLY;
420       }
421       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
422       {
423         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension WRAP_CONTENT\n");
424
425         // Child wants to determine its own size. It can't be
426         // bigger than us.
427         resultSize = size;
428         resultMode = MeasureSpec::Mode::AT_MOST;
429       }
430       else
431       {
432         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec childDimension UNSPECIFIED\n");
433         resultSize = childDimension;
434         resultMode = MeasureSpec::Mode::EXACTLY;
435       }
436
437       break;
438     }
439
440       // Parent has imposed a maximum size on us
441     case MeasureSpec::Mode::AT_MOST:
442     {
443       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::AT_MOST\n");
444       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
445       {
446         // Child wants to be our size, but our size is not fixed.
447         // Constrain child to not be bigger than us.
448         resultSize = size;
449         resultMode = MeasureSpec::Mode::AT_MOST;
450       }
451       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
452       {
453         // Child wants to determine its own size. It can't be
454         // bigger than us.
455         resultSize = size;
456         resultMode = MeasureSpec::Mode::AT_MOST;
457       }
458       else
459       {
460         // Child wants a specific size... so be it
461         resultSize = childDimension + padding;
462         resultMode = MeasureSpec::Mode::EXACTLY;
463       }
464
465       break;
466     }
467
468       // Parent asked to see how big we want to be
469     case MeasureSpec::Mode::UNSPECIFIED:
470     {
471       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec MeasureSpec::Mode::UNSPECIFIED\n");
472
473       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
474       {
475         // Child wants to be our size... find out how big it should be
476         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
477         resultMode = MeasureSpec::Mode::UNSPECIFIED;
478       }
479       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
480       {
481         // Child wants to determine its own size.... find out how big
482         // it should be
483         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
484         resultMode = MeasureSpec::Mode::UNSPECIFIED;
485       }
486       else
487       {
488         // Child wants a specific size... let him have it
489         resultSize = childDimension + padding;
490         resultMode = MeasureSpec::Mode::EXACTLY;
491       }
492       break;
493     }
494   }
495
496   DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LayoutGroup::GetChildMeasureSpec resultSize(" << resultSize << ")\n" );
497
498   //noinspection ResourceType
499   return MeasureSpec( resultSize, resultMode );
500 }
501
502
503 void LayoutGroup::OnInitialize()
504 {
505   auto control = Toolkit::Control::DownCast( GetOwner() );
506
507   if( control )
508   {
509     // Take ownership of existing children
510     for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
511     {
512       ChildAddedToOwnerImpl( control.GetChildAt( childIndex ) );
513     }
514
515     DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
516     DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
517     DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
518
519     if( control.GetParent() )
520     {
521       auto parent = Toolkit::Control::DownCast( control.GetParent() );
522       if( parent )
523       {
524         auto parentLayout = Toolkit::LayoutGroup::DownCast( DevelControl::GetLayout( parent ) );
525         if( parentLayout )
526         {
527           Internal::LayoutGroup& parentLayoutImpl = GetImplementation( parentLayout );
528
529           unsigned int count = parent.GetChildCount();
530           unsigned int index = static_cast< unsigned int >( control.GetProperty< int >( DevelActor::Property::SIBLING_ORDER ) );
531
532           // Find insertion position
533           while( ++index < count )
534           {
535             auto sibling = Toolkit::Control::DownCast( parent.GetChildAt( index ) );
536             if( sibling )
537             {
538               auto siblingLayout = DevelControl::GetLayout( sibling );
539               if( siblingLayout )
540               {
541                 Internal::LayoutItem& siblingLayoutImpl = GetImplementation( siblingLayout );
542                 parentLayoutImpl.Insert( siblingLayoutImpl, *this );
543                 break;
544               }
545             }
546           }
547
548           if( index >= count )
549           {
550             parentLayoutImpl.Add( *this );
551           }
552         }
553       }
554     }
555
556     RequestLayout( Dali::Toolkit::LayoutTransitionData::Type::ON_OWNER_SET );
557   }
558 }
559
560 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
561 {
562   DoRegisterChildProperties( containerType );
563 }
564
565 void LayoutGroup::OnUnparent()
566 {
567   // Remove children
568   RemoveAll();
569
570   auto control = Toolkit::Control::DownCast( GetOwner() );
571   if( control )
572   {
573     DevelActor::ChildAddedSignal( control ).Disconnect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
574     DevelActor::ChildRemovedSignal( control ).Disconnect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
575     DevelHandle::PropertySetSignal( control ).Disconnect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
576   }
577 }
578
579 void LayoutGroup::RemoveChild( LayoutItem& item )
580 {
581   item.SetParent( nullptr );
582   OnChildRemove( item );
583 }
584
585 void LayoutGroup::ChildAddedToOwner( Actor child )
586 {
587   ChildAddedToOwnerImpl( child );
588   RequestLayout( Dali::Toolkit::LayoutTransitionData::Type::ON_CHILD_ADD, child, Actor() );
589 }
590
591 void LayoutGroup::ChildAddedToOwnerImpl( Actor child )
592 {
593   LayoutItemPtr childLayout;
594   Toolkit::Control control = Toolkit::Control::DownCast( child );
595
596 #if defined(DEBUG_ENABLED)
597   auto parent = Toolkit::Control::DownCast( GetOwner() );
598   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner child control(%s) owner control(%s)\n",
599                                               control?control.GetName().c_str():"Invalid",
600                                               parent?parent.GetName().c_str():"Invalid" );
601 #endif
602
603   if( control ) // Can only support adding Controls, not Actors to layout
604   {
605     Internal::Control& childControlImpl = GetImplementation( control );
606     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
607     childLayout = childControlDataImpl.GetLayout();
608
609     if( ! childLayout )
610     {
611       // If the child doesn't already have a layout, then create a LayoutItem or LayoutGroup for it.
612       // If control behaviour flag set to Layout then set a LayoutGroup.
613       if( DevelControl::IsLayoutingRequired( control ) )
614       {
615         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner Creating default LayoutGroup for control:%s\n",
616                                                     control?control.GetName().c_str():"Invalid" );
617         childLayout = LayoutGroup::New( control );
618       }
619       else
620       {
621         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner Creating default LayoutItem for control:%s\n",
622                                                     control?control.GetName().c_str():"Invalid" );
623         childLayout = LayoutItem::New( control );
624         childLayout->SetAnimateLayout( IsLayoutAnimated() ); // forces animation inheritance.
625       }
626
627       DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner child control:" <<  control.GetName() <<
628                        " desiredWidth: " <<  control.GetNaturalSize().width <<
629                        " desiredHeight:"  << control.GetNaturalSize().height );
630
631       childControlDataImpl.SetLayout( *childLayout.Get() );
632
633       Vector3 size = child.GetTargetSize();
634       // If the size of the control is set explicitly make sure that the control size
635       // stays the same after the layout except it is over written with match parent specs.
636
637       auto childControl = Toolkit::Control::DownCast(childLayout->GetOwner());
638
639       bool setWidthExplictly = ( childControl.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) > 0  );
640       if ( size.x != 0 || setWidthExplictly )
641       {
642         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner SetTargetWidth(%f)\n", size.x );
643         childLayout->SetTargetWidth( size.x );
644       }
645
646       bool setHeightExplcitly = ( childControl.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION ) > 0 );
647       if ( size.y != 0 || setHeightExplcitly )
648       {
649         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner SetTargetHeight(%f)\n", size.y);
650         childLayout->SetTargetHeight( size.y );
651       }
652       // Default layout data will be generated by Add().
653     }
654     else
655     {
656       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::ChildAddedToOwner child(%s) already has a Layout\n", control.GetName().c_str() );
657       LayoutGroupPtr layoutGroup( dynamic_cast< LayoutGroup* >( childLayout.Get() ) );
658       if( !layoutGroup )
659       {
660         // Set only in case of leaf children
661         childLayout->SetAnimateLayout( IsLayoutAnimated() );
662       }
663     }
664
665     Add( *childLayout.Get() );
666   }
667 }
668
669 void LayoutGroup::ChildRemovedFromOwner( Actor child )
670 {
671   Toolkit::Control control = Toolkit::Control::DownCast( child );
672   if( control )
673   {
674     Internal::Control& childControlImpl = GetImplementation( control );
675     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
676     auto childLayout = childControlDataImpl.GetLayout();
677     if( childLayout )
678     {
679       Remove( *childLayout.Get() );
680       RequestLayout( Dali::Toolkit::LayoutTransitionData::Type::ON_CHILD_REMOVE, child, Actor() );
681     }
682   }
683 }
684
685 void LayoutGroup::ChildOrderChanged( Actor child )
686 {
687   Toolkit::Control childControl = Toolkit::Control::DownCast( child );
688   if( childControl )
689   {
690     Internal::Control& childControlImpl = GetImplementation( childControl );
691     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
692
693     auto childLayout = childControlDataImpl.GetLayout();
694     if( childLayout )
695     {
696       Toolkit::Control control = Toolkit::Control::DownCast( GetOwner() );
697       unsigned int count = control.GetChildCount();
698       unsigned int index = static_cast< unsigned int >( childControl.GetProperty< int >( DevelActor::Property::SIBLING_ORDER ) );
699
700       // Find insertion position
701       while( ++index < count )
702       {
703         auto sibling = Toolkit::Control::DownCast( control.GetChildAt( index ) );
704         if( sibling )
705         {
706           auto siblingLayout = DevelControl::GetLayout( sibling );
707           if( siblingLayout )
708           {
709             Internal::LayoutItem& siblingLayoutImpl = GetImplementation( siblingLayout );
710             Move( siblingLayoutImpl, *childLayout );
711             return;
712           }
713         }
714       }
715
716       MoveBack( *childLayout );
717     }
718   }
719 }
720
721 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
722 {
723   DALI_LOG_INFO( gLogFilter, Debug::Concise, "LayoutGroup::OnOwnerPropertySet\n");
724   auto actor = Actor::DownCast( handle );
725   if( actor &&
726       (
727         index == Actor::Property::LAYOUT_DIRECTION  ||
728         index == Toolkit::Control::Property::PADDING  ||
729         index == Toolkit::Control::Property::MARGIN
730       )
731     )
732   {
733     RequestLayout();
734   }
735 }
736
737 void LayoutGroup::OnAnimationStateChanged( bool animateLayout )
738 {
739   // Change children's animation state
740   for( auto&& child : mImpl->mChildren )
741   {
742     LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( child.child.Get() ) );
743     if( ! parentGroup )
744     {
745       // Change state only in case of leaf children
746       child.child->SetAnimateLayout( animateLayout );
747     }
748   }
749 }
750
751 void LayoutGroup::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
752 {
753   auto childCount = GetChildCount();
754
755   DALI_LOG_STREAM( gLogFilter, Debug::Verbose,
756                   "LayoutGroup::OnMeasure Actor Id:" <<  Actor::DownCast(GetOwner()).GetId() <<
757                   " Owner:" <<  Actor::DownCast(GetOwner()).GetName() <<
758                   " Child Count:" << childCount <<
759                   " MeasureSpecs( width:"<<widthMeasureSpec<<", height:"<<heightMeasureSpec );
760
761   auto widthMode = widthMeasureSpec.GetMode();
762   auto heightMode = heightMeasureSpec.GetMode();
763   LayoutLength widthSpecSize = widthMeasureSpec.GetSize();
764   LayoutLength heightSpecSize = heightMeasureSpec.GetSize();
765
766   bool exactWidth ( false );
767   bool exactHeight ( false );
768
769   // Layouting behaviour
770   // EXACT, width and height as provided.
771   // MATCH_PARENT, width and height that of parent
772   // WRAP_CONTENT, take width of widest child and height size of longest child (within given limit)
773   // UNSPECIFIED, take width of widest child and height size of longest child.
774
775   LayoutLength layoutWidth( 0 );
776   LayoutLength layoutHeight( 0 );
777
778   // If LayoutGroup has children then measure children to get max dimensions
779   if ( childCount > 0 )
780   {
781     for( unsigned int i=0; i<childCount; ++i )
782     {
783       auto childLayout = GetChildAt( i );
784       if( childLayout )
785       {
786         auto childControl = Toolkit::Control::DownCast(childLayout->GetOwner());
787
788         // If child control has children check if a ResizePolicy is set on it.  A LayoutItem could be a legacy container.
789         // A legacy container would need it's ResizePolicy to be applied as a MeasureSpec.
790
791         // Check below will be true for legacy containers and controls with layout required set.
792         // Other layouts will have their own OnMeasure (a checked requirement) hence not execute LayoutGroup::OnMeasure.
793         // Controls which have set layout required will not be legacy controls hence should not have a ResizePolicy set.
794         // Only need to map the resize policy the first time as the Layouting system will then set it to FIXED.
795         if( childControl.GetChildCount() > 0 && ! mImpl->mResizePolicyMapped )
796         {
797           // First pass, Static mappings that are not dependant on parent
798           SizeNegotiationMapper::SetLayoutParametersUsingResizePolicy( childControl, childLayout, Dimension::WIDTH );
799           SizeNegotiationMapper::SetLayoutParametersUsingResizePolicy( childControl, childLayout, Dimension::HEIGHT );
800           mImpl->mResizePolicyMapped = true;
801         }
802
803         // Second pass, if any mappings were not possible due to parent size dependancies then calculate an exact desired size for child
804         if( true == childLayout->IsResizePolicyRequired() ) // No need to test child count as this flag would only be set if control had children.
805         {
806           // Get last stored width and height specifications for the child
807           LayoutLength desiredWidth = childControl.GetProperty<float>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
808           LayoutLength desiredHeight = childControl.GetProperty<float>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
809
810           DALI_LOG_INFO( gLogFilter, Debug::General, "LayoutGroup::MeasureChild Initial desired size pre ResizePolicy(%f,%f)\n", desiredWidth.AsInteger(), desiredHeight.AsInteger() );
811
812           childLayout->SetResizePolicyRequired( false ); // clear flag incase in case of changes before next Measure
813           SizeNegotiationMapper::GetSizeofChildForParentDependentResizePolicy( childControl, widthMeasureSpec, heightMeasureSpec, desiredWidth, desiredHeight );
814
815           // Parent dependant ResizePolicies become exact sizes so are now set on the child before it's measured.
816           childControl.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, desiredWidth.AsInteger() );
817           childControl.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, desiredHeight.AsInteger()  );
818
819           DALI_LOG_INFO( gLogFilter, Debug::General, " LayoutGroup::OnMeasure ResizePolicy Required resulting size(%f,%f)\n",  desiredWidth.AsInteger(), desiredHeight.AsInteger() );
820         }
821
822         // Get size of child
823         MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
824         LayoutLength childWidth = childLayout->GetMeasuredWidth();
825         LayoutLength childHeight = childLayout->GetMeasuredHeight();
826
827         Extents childMargin = childLayout->GetMargin();
828         DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LayoutGroup::OnMeasure child " << childControl.GetName().c_str() << " width[" << childWidth << "] height[" << childHeight << "]\n" );
829
830         layoutWidth = std::max( layoutWidth, childWidth + childMargin.start + childMargin.end );
831         layoutHeight = std::max( layoutHeight, childHeight + childMargin.top + childMargin.bottom );
832         DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LayoutGroup::OnMeasure calculated child width[" << layoutWidth << "] height[" << layoutHeight << "]\n" );
833       }
834       else
835       {
836         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::OnMeasure Not a layout\n" );
837       }
838     }
839
840     Extents padding = GetPadding();
841     layoutWidth += padding.start + padding.end;
842     layoutHeight += padding.top + padding.bottom;
843   }
844   else
845   {
846     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::OnMeasure Getting default size as a leaf\n" );
847     // LayoutGroup does not contain any children so must be a leaf
848     layoutWidth = GetDefaultSize( GetSuggestedMinimumWidth(), widthMeasureSpec );
849     layoutHeight = GetDefaultSize( GetSuggestedMinimumHeight(), heightMeasureSpec );
850   }
851
852   // Can't exceed specified width
853   if( widthMode == MeasureSpec::Mode::EXACTLY )
854   {
855     exactWidth = true;
856   }
857   else if ( widthMode == MeasureSpec::Mode::AT_MOST )
858   {
859     layoutWidth = std::min( layoutWidth, widthSpecSize );
860   }
861
862   // Can't exceed specified height
863   if( heightMode == MeasureSpec::Mode::EXACTLY )
864   {
865     exactHeight = true;
866   }
867   else if ( heightMode == MeasureSpec::Mode::AT_MOST )
868   {
869     layoutHeight = std::min( layoutHeight, heightSpecSize );
870   }
871
872   layoutWidth = std::max( layoutWidth, GetSuggestedMinimumWidth() );
873   layoutHeight = std::max( layoutHeight, GetSuggestedMinimumHeight() );
874
875   if( exactWidth )
876   {
877     layoutWidth = widthSpecSize;
878   }
879
880   if( exactHeight )
881   {
882     layoutHeight = heightSpecSize;
883   }
884
885   DALI_LOG_STREAM( gLogFilter, Debug::General, "LayoutGroup::OnMeasure Measured size(" << layoutWidth << "," << layoutHeight << ") for : " << Actor::DownCast(GetOwner()).GetName() << " \n" );
886   SetMeasuredDimensions( MeasuredSize( layoutWidth ), MeasuredSize( layoutHeight ) );
887 }
888
889 void LayoutGroup::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
890 {
891   auto count = GetChildCount();
892
893   DALI_LOG_STREAM( gLogFilter, Debug::Verbose, "LayoutGroup OnLayout owner:" << ( ( Toolkit::Control::DownCast(GetOwner())) ? Toolkit::Control::DownCast(GetOwner()).GetName() : "invalid" )  << " childCount:" << count );
894
895   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
896   {
897     LayoutItemPtr childLayout = GetChildAt( childIndex );
898     if( childLayout != nullptr )
899     {
900
901       auto childOwner = childLayout->GetOwner();
902       LayoutLength childWidth = childLayout->GetMeasuredWidth();
903       LayoutLength childHeight = childLayout->GetMeasuredHeight();
904       Extents childMargin = childLayout->GetMargin();
905       auto control = Toolkit::Control::DownCast( childOwner );
906       Extents padding = GetPadding();
907
908       auto childPosition = control.GetProperty< Vector3 >( Actor::Property::POSITION );
909       auto anchorPoint = control.GetProperty< Vector3 >( Actor::Property::ANCHOR_POINT );
910
911       DALI_LOG_STREAM( gLogFilter, Debug::General, "LayoutGroup::OnLayout child[" << control.GetName() <<
912                        "] position(" << childPosition << ") child width[" << childWidth << "] height[" << childHeight << "]\n" );
913
914       // Margin and Padding only supported when child anchor point is TOP_LEFT.
915       int paddingAndMarginOffsetX = ( AnchorPoint::TOP_LEFT == anchorPoint ) ? ( padding.top + childMargin.top ) : 0;
916       int paddingAndMarginOffsetY = ( AnchorPoint::TOP_LEFT == anchorPoint ) ? ( padding.start + childMargin.start ) : 0;
917       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LayoutGroup::OnLayout paddingMargin offset(%d,%d)\n", paddingAndMarginOffsetX, paddingAndMarginOffsetY );
918
919       LayoutLength childLeft = childPosition.x + paddingAndMarginOffsetX;
920       LayoutLength childTop = childPosition.y + paddingAndMarginOffsetY;
921
922       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
923     }
924   }
925 }
926
927
928 } // namespace Internal
929 } // namespace Toolkit
930 } // namespace Dali