Fix crash when Layer added to LayoutGroup
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / layouting / flex-layout-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 "flex-layout-impl.h"
19
20 //EXTERNAL INCLUDES
21 #include <dali/integration-api/debug.h>
22 #include <dali/public-api/common/extents.h>
23 #include <dali/devel-api/actors/actor-devel.h>
24 #include <dali/devel-api/object/handle-devel.h>
25
26 //INTERNAL INCLUDES
27 #include <dali-toolkit/devel-api/layouting/layout-item.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/third-party/yoga/YGNode.h>
31
32 #if defined(DEBUG_ENABLED)
33 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
34 #endif
35
36 namespace Dali
37 {
38 namespace Toolkit
39 {
40 namespace Internal
41 {
42
43 FlexLayoutPtr FlexLayout::New()
44 {
45   FlexLayoutPtr layout( new FlexLayout() );
46   return layout;
47 }
48
49 void FlexLayout::SetFlexDirection( Dali::Toolkit::FlexLayout::FlexDirection flexDirection )
50 {
51   YGNodeStyleSetFlexDirection( mRoot, static_cast<YGFlexDirection>(flexDirection) );
52 }
53
54 Dali::Toolkit::FlexLayout::FlexDirection FlexLayout::GetFlexDirection() const
55 {
56   return static_cast<Dali::Toolkit::FlexLayout::FlexDirection>(YGNodeStyleGetFlexDirection( mRoot ));
57 }
58
59 void FlexLayout::SetFlexJustification( Dali::Toolkit::FlexLayout::Justification flexJustification )
60 {
61   YGNodeStyleSetJustifyContent( mRoot, static_cast<YGJustify>(flexJustification) );
62 }
63
64 Dali::Toolkit::FlexLayout::Justification FlexLayout::GetFlexJustification() const
65 {
66   return static_cast<Dali::Toolkit::FlexLayout::Justification>(YGNodeStyleGetJustifyContent( mRoot ));
67 }
68
69 void FlexLayout::SetFlexWrap( Dali::Toolkit::FlexLayout::WrapType wrapType )
70 {
71   YGNodeStyleSetFlexWrap( mRoot, static_cast<YGWrap>(wrapType) );
72 }
73
74 Dali::Toolkit::FlexLayout::WrapType FlexLayout::GetFlexWrap() const
75 {
76   return static_cast<Dali::Toolkit::FlexLayout::WrapType>(YGNodeStyleGetFlexWrap( mRoot ));
77 }
78
79 void FlexLayout::SetFlexAlignment( Dali::Toolkit::FlexLayout::Alignment::Type flexAlignment )
80 {
81   YGNodeStyleSetAlignContent( mRoot, static_cast<YGAlign>(flexAlignment) );
82 }
83
84 Dali::Toolkit::FlexLayout::Alignment::Type FlexLayout::GetFlexAlignment() const
85 {
86   return static_cast<Dali::Toolkit::FlexLayout::Alignment::Type>(YGNodeStyleGetAlignContent( mRoot ));
87 }
88
89 void FlexLayout::SetFlexItemsAlignment( Dali::Toolkit::FlexLayout::Alignment::Type flexAlignment )
90 {
91   YGNodeStyleSetAlignItems( mRoot, static_cast<YGAlign>(flexAlignment) );
92 }
93
94 Dali::Toolkit::FlexLayout::Alignment::Type FlexLayout::GetFlexItemsAlignment() const
95 {
96   return static_cast<Dali::Toolkit::FlexLayout::Alignment::Type>(YGNodeStyleGetAlignItems( mRoot ));
97 }
98
99 FlexLayout::FlexLayout()
100 : LayoutGroup(),
101   mRoot( nullptr )
102 {
103   mRoot = YGNodeNew();
104   YGNodeSetContext( mRoot, this );
105
106   // Set default style
107   YGNodeStyleSetFlexDirection( mRoot, YGFlexDirectionColumn );
108   YGNodeStyleSetFlexWrap( mRoot, YGWrapNoWrap );
109   YGNodeStyleSetJustifyContent( mRoot, YGJustifyFlexStart );
110   YGNodeStyleSetAlignContent( mRoot, YGAlignFlexStart );
111   YGNodeStyleSetAlignItems( mRoot, YGAlignFlexStart );
112 }
113
114 FlexLayout::~FlexLayout()
115 {
116   if( mRoot )
117   {
118     YGNodeFreeRecursive( mRoot );
119   }
120 }
121
122 void FlexLayout::DoInitialize()
123 {
124 }
125
126 void FlexLayout::DoRegisterChildProperties( const std::string& containerType )
127 {
128   auto typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( containerType );
129   if( typeInfo )
130   {
131     Property::IndexContainer indices;
132     typeInfo.GetChildPropertyIndices( indices );
133
134     if( std::find( indices.Begin(), indices.End(), Toolkit::FlexLayout::ChildProperty::FLEX ) ==
135         indices.End() )
136     {
137       ChildPropertyRegistration( typeInfo.GetName(), "flex", Toolkit::FlexLayout::ChildProperty::FLEX, Property::FLOAT );
138     }
139
140     if( std::find( indices.Begin(), indices.End(), Toolkit::FlexLayout::ChildProperty::ALIGN_SELF ) ==
141         indices.End() )
142     {
143       ChildPropertyRegistration( typeInfo.GetName(), "alignSelf", Toolkit::FlexLayout::ChildProperty::ALIGN_SELF, Property::INTEGER );
144     }
145   }
146 }
147
148 void FlexLayout::OnChildAdd( LayoutItem& child )
149 {
150   auto owner = child.GetOwner();
151   if(!DevelHandle::DoesCustomPropertyExist(owner, Toolkit::FlexLayout::ChildProperty::FLEX ))
152   {
153     owner.SetProperty( Toolkit::FlexLayout::ChildProperty::FLEX, 0 );
154   }
155   if(!DevelHandle::DoesCustomPropertyExist(owner, Toolkit::FlexLayout::ChildProperty::ALIGN_SELF ))
156   {
157     owner.SetProperty( Toolkit::FlexLayout::ChildProperty::ALIGN_SELF, YGAlignAuto );
158   }
159
160   YGNodeRef node = YGNodeNew();
161   YGNodeSetContext( node, &child );
162   YGNodeSetMeasureFunc( node, OnChildMeasure );
163   YGNodeMarkDirty( node );
164   YGNodeInsertChild( mRoot, node, GetChildCount()-1 );
165 }
166
167 void FlexLayout::OnChildRemove( LayoutItem& child )
168 {
169   auto count = GetChildCount();
170   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
171   {
172     LayoutItemPtr childLayout = GetChildAt( childIndex );
173     if( &child == childLayout.Get() )
174     {
175       YGNodeRef node = YGNodeGetChild( mRoot, childIndex );
176       YGNodeRemoveChild( mRoot, node );
177       break;
178     }
179   }
180 }
181
182 void FlexLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
183 {
184   auto actor = Actor::DownCast(GetOwner());
185   bool isLayoutRtl = actor ? actor.GetProperty<int>( Actor::Property::LAYOUT_DIRECTION ) == LayoutDirection::RIGHT_TO_LEFT: false;
186   Extents padding = GetPadding();
187   Extents margin = GetMargin();
188
189 #if defined(DEBUG_ENABLED)
190   std::ostringstream oss;
191   oss << "FlexLayout::OnMeasure  ";
192   if( actor )
193   {
194     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " Layout direction:" << actor.GetProperty( Actor::Property::LAYOUT_DIRECTION ).Get<int>() << " ";
195   }
196   oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
197   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
198 #endif
199
200   YGNodeStyleSetMargin( mRoot, YGEdgeLeft, margin.start );
201   YGNodeStyleSetMargin( mRoot, YGEdgeTop, margin.top );
202   YGNodeStyleSetMargin( mRoot, YGEdgeRight, margin.end );
203   YGNodeStyleSetMargin( mRoot, YGEdgeBottom, margin.bottom );
204   YGNodeStyleSetPadding( mRoot, YGEdgeLeft, padding.start );
205   YGNodeStyleSetPadding( mRoot, YGEdgeTop, padding.top );
206   YGNodeStyleSetPadding( mRoot, YGEdgeRight, padding.end );
207   YGNodeStyleSetPadding( mRoot, YGEdgeBottom, padding.bottom );
208
209   float width = YGUndefined;
210   float height = YGUndefined;
211
212   YGNodeStyleSetWidth( mRoot, YGUndefined );
213   YGNodeStyleSetHeight( mRoot, YGUndefined );
214   YGNodeStyleSetMinWidth( mRoot, YGUndefined );
215   YGNodeStyleSetMinHeight( mRoot, YGUndefined );
216   YGNodeStyleSetMaxWidth( mRoot, YGUndefined );
217   YGNodeStyleSetMaxHeight( mRoot, YGUndefined );
218
219   if( widthMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY )
220   {
221     width = widthMeasureSpec.GetSize();
222     YGNodeStyleSetWidth( mRoot, width );
223   }
224   else if( widthMeasureSpec.GetMode() == MeasureSpec::Mode::AT_MOST )
225   {
226     width = widthMeasureSpec.GetSize();
227     YGNodeStyleSetMaxWidth( mRoot, width );
228   }
229
230   if (heightMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY)
231   {
232     height = heightMeasureSpec.GetSize();
233     YGNodeStyleSetHeight( mRoot, height );
234   }
235   else if (widthMeasureSpec.GetMode() == MeasureSpec::Mode::AT_MOST)
236   {
237     height = heightMeasureSpec.GetSize();
238     YGNodeStyleSetMaxHeight( mRoot, height );
239   }
240
241   SetChildrenStyle();
242   YGNodeCalculateLayout( mRoot, width, height, isLayoutRtl ? YGDirectionRTL : YGDirectionLTR );
243   SetMeasuredDimensions( GetDefaultSize( YGNodeLayoutGetWidth(mRoot), widthMeasureSpec ),
244                          GetDefaultSize( YGNodeLayoutGetHeight(mRoot), heightMeasureSpec ) );
245 }
246
247 void FlexLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
248 {
249   auto owner = GetOwner();
250   auto actor = Actor::DownCast(owner);
251   bool isLayoutRtl = actor ? actor.GetProperty( Actor::Property::LAYOUT_DIRECTION ).Get<int>() == LayoutDirection::RIGHT_TO_LEFT: false;
252   auto width = right - left;
253   auto height = bottom - top;
254
255 #if defined(DEBUG_ENABLED)
256   std::ostringstream oss;
257   oss << "FlexLayout::OnLayout  ";
258   if( actor )
259   {
260     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " Layout direction:" << actor.GetProperty( Actor::Property::LAYOUT_DIRECTION ).Get<int>() << " ";
261   }
262   oss << "changed:" << (int)changed << " left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << " isLayoutRtl:" << (int)isLayoutRtl << std::endl;
263   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
264 #endif
265
266   YGNodeCalculateLayout( mRoot, width, height, isLayoutRtl ? YGDirectionRTL : YGDirectionLTR );
267
268   auto count = GetChildCount();
269   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
270   {
271     LayoutItemPtr childLayout = GetChildAt( childIndex );
272     if( childLayout != nullptr )
273     {
274       YGNodeRef node = YGNodeGetChild(mRoot, childIndex);
275       auto childLeft = YGNodeLayoutGetLeft( node ) + left;
276       auto childTop = YGNodeLayoutGetTop( node ) + top;
277       auto childWidth = YGNodeLayoutGetWidth( node );
278       auto childHeight = YGNodeLayoutGetHeight( node );
279       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
280     }
281   }
282 }
283
284 YGSize FlexLayout::OnChildMeasure( YGNodeRef node,
285                               float innerWidth,
286                               YGMeasureMode widthMode,
287                               float innerHeight,
288                               YGMeasureMode heightMode ) {
289   // TODO: this function should try to get use of LayoutGroup::GetChildMeasureSpec
290   // or LayoutGroup::MeasureChild somehow since it is fixed now
291   LayoutItem* childLayout = static_cast<LayoutItem*>(node->getContext());
292   auto childOwner = childLayout->GetOwner();
293   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
294   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
295
296   MeasureSpec::Mode measureWidthMode = MeasureSpec::Mode::AT_MOST;
297   MeasureSpec::Mode measureHeightMode = MeasureSpec::Mode::AT_MOST;
298   if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
299   {
300     if( innerWidth != YGUndefined)
301     {
302       desiredWidth = innerWidth;
303     }
304     measureWidthMode = MeasureSpec::Mode::EXACTLY;
305   }
306
307   if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
308   {
309     if( innerWidth != YGUndefined)
310     {
311       desiredHeight = innerHeight;
312     }
313     measureHeightMode = MeasureSpec::Mode::EXACTLY;
314   }
315
316   if( desiredWidth == Toolkit::ChildLayoutData::WRAP_CONTENT )
317   {
318     measureWidthMode = MeasureSpec::Mode::UNSPECIFIED;
319   }
320
321   if( desiredHeight == Toolkit::ChildLayoutData::WRAP_CONTENT )
322   {
323     measureHeightMode = MeasureSpec::Mode::UNSPECIFIED;
324   }
325
326   MeasureSpec widthMeasureSpec = MeasureSpec( desiredWidth, measureWidthMode );
327   MeasureSpec heightMeasureSpec = MeasureSpec( desiredHeight, measureHeightMode );
328   if( measureWidthMode == MeasureSpec::Mode::UNSPECIFIED ||
329       measureHeightMode == MeasureSpec::Mode::UNSPECIFIED )
330   {
331     // A measure just to get the size if the wrapped content
332     childLayout->Measure( widthMeasureSpec, heightMeasureSpec );
333     desiredWidth = childLayout->GetMeasuredWidth();
334     desiredHeight = childLayout->GetMeasuredHeight();
335     // Remove padding here since the second measure will add it back
336     Extents padding = childLayout->GetPadding();
337     desiredWidth = desiredWidth - padding.end - padding.start;
338     desiredHeight = desiredHeight - padding.bottom - padding.top;
339   }
340
341   // Safety check to avoid going out of boundary
342   if( (innerWidth != YGUndefined && innerWidth != 0) && innerWidth < desiredWidth )
343   {
344     desiredWidth = innerWidth;
345   }
346
347   if( (innerHeight != YGUndefined && innerHeight != 0) && innerHeight < desiredHeight )
348   {
349     desiredHeight = innerHeight;
350   }
351
352   // Measure for Yoga
353   MeasureSpec::Mode ygWidthMode = static_cast<MeasureSpec::Mode>(widthMode);
354   if( measureWidthMode == MeasureSpec::Mode::EXACTLY )
355   {
356     ygWidthMode = MeasureSpec::Mode::EXACTLY;
357   }
358
359   MeasureSpec::Mode ygHeightMode = static_cast<MeasureSpec::Mode>(heightMode);
360   if( measureHeightMode == MeasureSpec::Mode::EXACTLY )
361   {
362     ygHeightMode = MeasureSpec::Mode::EXACTLY;
363   }
364
365   MeasureSpec ygWidthMeasureSpec = MeasureSpec( desiredWidth, ygWidthMode );
366   MeasureSpec ygHeightMeasureSpec = MeasureSpec( desiredHeight, ygHeightMode );
367 #if defined(DEBUG_ENABLED)
368   auto actor = Actor::DownCast(childOwner);
369   std::ostringstream oss;
370   oss << "FlexLayout::OnChildMeasure  ";
371   if( actor )
372   {
373     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
374   }
375   oss << "innerWidth:" << ((innerWidth == YGUndefined) ? "YGUndefined " : "") << innerWidth <<
376          " innerHeight:" << ((innerHeight == YGUndefined) ? "YGUndefined " : "") << innerHeight <<
377          " desiredWidth:" << desiredWidth << " desiredHeight:" << desiredHeight <<
378          " widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec <<
379          " ygWidthMeasureSpec:" << ygWidthMeasureSpec << " ygHeightMeasureSpec:" << ygHeightMeasureSpec << std::endl;
380   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
381 #endif
382
383   if( measureWidthMode == MeasureSpec::Mode::UNSPECIFIED ||
384       measureHeightMode == MeasureSpec::Mode::UNSPECIFIED )
385   {
386     if( ygWidthMeasureSpec == widthMeasureSpec && ygHeightMeasureSpec == heightMeasureSpec )
387     {
388       return YGSize{
389         .width = childLayout->GetMeasuredWidth(),
390         .height = childLayout->GetMeasuredHeight(),
391       };
392     }
393   }
394
395   childLayout->Measure( ygWidthMeasureSpec, ygHeightMeasureSpec );
396   return YGSize{
397     .width = childLayout->GetMeasuredWidth(),
398     .height = childLayout->GetMeasuredHeight(),
399   };
400 }
401
402 void FlexLayout::SetChildrenStyle()
403 {
404   if( mRoot )
405   {
406     auto count = GetChildCount();
407     for( unsigned int childIndex = 0; childIndex < count; childIndex++)
408     {
409       LayoutItemPtr childLayout = GetChildAt( childIndex );
410       if( childLayout != nullptr )
411       {
412         Extents padding = childLayout->GetPadding();
413         Extents margin = childLayout->GetMargin();
414         auto childOwner = childLayout->GetOwner();
415         auto childActor = Actor::DownCast(childOwner);
416         auto flex = childOwner.GetProperty<float>( Toolkit::FlexLayout::ChildProperty::FLEX );
417         auto alignSelf = static_cast<YGAlign>( childOwner.GetProperty<int>( Toolkit::FlexLayout::ChildProperty::ALIGN_SELF ));
418
419         YGNodeRef childNode = YGNodeGetChild( mRoot, childIndex );
420         // Initialise the style of the child.
421         YGNodeStyleSetMargin( childNode, YGEdgeLeft, margin.start );
422         YGNodeStyleSetMargin( childNode, YGEdgeTop, margin.top );
423         YGNodeStyleSetMargin( childNode, YGEdgeRight, margin.end );
424         YGNodeStyleSetMargin( childNode, YGEdgeBottom, margin.bottom );
425
426         YGNodeStyleSetPadding( childNode, YGEdgeLeft, padding.start );
427         YGNodeStyleSetPadding( childNode, YGEdgeTop, padding.top );
428         YGNodeStyleSetPadding( childNode, YGEdgeRight, padding.end );
429         YGNodeStyleSetPadding( childNode, YGEdgeBottom, padding.bottom );
430
431         YGNodeStyleSetWidth( childNode, YGUndefined );
432         YGNodeStyleSetHeight( childNode, YGUndefined );
433         // TODO: check if we are supposed to use actor properties here, max/min is needed for stretch
434         YGNodeStyleSetMinWidth( childNode, childActor.GetMinimumSize().x );
435         YGNodeStyleSetMinHeight( childNode, childActor.GetMinimumSize().y );
436         if( childActor.GetMaximumSize().x == FLT_MAX )
437         {
438           YGNodeStyleSetMaxWidth( childNode, YGUndefined );
439         }
440         else
441         {
442           YGNodeStyleSetMaxWidth( childNode, childActor.GetMaximumSize().x );
443         }
444         if( childActor.GetMaximumSize().y == FLT_MAX )
445         {
446           YGNodeStyleSetMaxHeight( childNode, YGUndefined );
447         }
448         else
449         {
450           YGNodeStyleSetMaxHeight( childNode, childActor.GetMaximumSize().y );
451         }
452
453         YGNodeStyleSetFlex( childNode, flex );
454         YGNodeStyleSetAlignSelf( childNode, alignSelf );
455
456         // Have to do manually for nodes with custom measure function
457         // TODO: check the style is changed before marking the node
458         YGNodeMarkDirty( childNode );
459       }
460     }
461   }
462 }
463
464 } // namespace Internal
465 } // namespace Toolkit
466 } // namespace Dali