Add SpaceEvenly to flex justification
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / flex-container / flex-container-impl.cpp
1 /*
2  * Copyright (c) 2021 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
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/flex-container/flex-container-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/actors/actor-devel.h>
23 #include <dali/devel-api/scripting/scripting.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/object/ref-object.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/public-api/object/type-registry.h>
28 #include <dali/public-api/size-negotiation/relayout-container.h>
29 #include <sstream>
30
31 #include <dali-toolkit/devel-api/controls/control-devel.h>
32
33 using namespace Dali;
34
35 namespace
36 {
37 #if defined(DEBUG_ENABLED)
38 // debugging support, very useful when new features are added or bugs are hunted down
39 // currently not called from code so compiler will optimize these away, kept here for future debugging
40
41 #define FLEX_CONTAINER_TAG "DALI Toolkit::FlexContainer "
42 #define FC_LOG(fmt, args, ...) Debug::LogMessage(Debug::DebugInfo, FLEX_CONTAINER_TAG fmt, ##args)
43 // #define FLEX_CONTAINER_DEBUG 1
44
45 #if defined(FLEX_CONTAINER_DEBUG)
46 void PrintNodes(Toolkit::Internal::FlexContainer::FlexItemNodeContainer itemNodes)
47 {
48   // Print the style property and layout of all the children
49   for(unsigned int i = 0; i < itemNodes.size(); ++i)
50   {
51     FC_LOG("Item %d style: \n", i);
52     YGNodePrint(itemNodes[i].node, (YGPrintOptions)(YGPrintOptionsStyle | YGPrintOptionsChildren));
53     FC_LOG("\n");
54     FC_LOG("Item %d layout: \n", i);
55     YGNodePrint(itemNodes[i].node, (YGPrintOptions)(YGPrintOptionsLayout | YGPrintOptionsChildren));
56     FC_LOG("\n");
57   }
58 }
59
60 #endif // defined(FLEX_CONTAINER_DEBUG)
61 #endif // defined(DEBUG_ENABLED)
62
63 } // namespace
64
65 namespace Dali
66 {
67 namespace Toolkit
68 {
69 namespace Internal
70 {
71 namespace
72 {
73 // Type registration
74 BaseHandle Create()
75 {
76   return Toolkit::FlexContainer::New();
77 }
78
79 // Setup properties, signals and actions using the type-registry.
80 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::FlexContainer, Toolkit::Control, Create);
81
82 DALI_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "contentDirection", INTEGER, CONTENT_DIRECTION)
83 DALI_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "flexDirection", INTEGER, FLEX_DIRECTION)
84 DALI_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "flexWrap", INTEGER, FLEX_WRAP)
85 DALI_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "justifyContent", INTEGER, JUSTIFY_CONTENT)
86 DALI_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "alignItems", INTEGER, ALIGN_ITEMS)
87 DALI_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "alignContent", INTEGER, ALIGN_CONTENT)
88 DALI_CHILD_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "flex", FLOAT, FLEX)
89 DALI_CHILD_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "alignSelf", INTEGER, ALIGN_SELF)
90 DALI_CHILD_PROPERTY_REGISTRATION(Toolkit, FlexContainer, "flexMargin", VECTOR4, FLEX_MARGIN)
91
92 DALI_TYPE_REGISTRATION_END()
93
94 const Scripting::StringEnum ALIGN_SELF_STRING_TABLE[] =
95   {
96     {"auto", Toolkit::FlexContainer::ALIGN_AUTO},
97     {"flexStart", Toolkit::FlexContainer::ALIGN_FLEX_START},
98     {"center", Toolkit::FlexContainer::ALIGN_CENTER},
99     {"flexEnd", Toolkit::FlexContainer::ALIGN_FLEX_END},
100     {"stretch", Toolkit::FlexContainer::ALIGN_STRETCH}};
101 const unsigned int ALIGN_SELF_STRING_TABLE_COUNT = sizeof(ALIGN_SELF_STRING_TABLE) / sizeof(ALIGN_SELF_STRING_TABLE[0]);
102
103 const Scripting::StringEnum CONTENT_DIRECTION_STRING_TABLE[] =
104   {
105     {"inherit", Toolkit::FlexContainer::INHERIT},
106     {"LTR", Toolkit::FlexContainer::LTR},
107     {"RTL", Toolkit::FlexContainer::RTL}};
108 const unsigned int CONTENT_DIRECTION_STRING_TABLE_COUNT = sizeof(CONTENT_DIRECTION_STRING_TABLE) / sizeof(CONTENT_DIRECTION_STRING_TABLE[0]);
109
110 const Scripting::StringEnum FLEX_DIRECTION_STRING_TABLE[] =
111   {
112     {"column", Toolkit::FlexContainer::COLUMN},
113     {"columnReverse", Toolkit::FlexContainer::COLUMN_REVERSE},
114     {"row", Toolkit::FlexContainer::ROW},
115     {"rowReverse", Toolkit::FlexContainer::ROW_REVERSE}};
116 const unsigned int FLEX_DIRECTION_STRING_TABLE_COUNT = sizeof(FLEX_DIRECTION_STRING_TABLE) / sizeof(FLEX_DIRECTION_STRING_TABLE[0]);
117
118 const Scripting::StringEnum FLEX_WRAP_STRING_TABLE[] =
119   {
120     {"noWrap", Toolkit::FlexContainer::NO_WRAP},
121     {"wrap", Toolkit::FlexContainer::WRAP}};
122 const unsigned int FLEX_WRAP_STRING_TABLE_COUNT = sizeof(FLEX_WRAP_STRING_TABLE) / sizeof(FLEX_WRAP_STRING_TABLE[0]);
123
124 const Scripting::StringEnum JUSTIFY_CONTENT_STRING_TABLE[] =
125   {
126     {"flexStart", Toolkit::FlexContainer::JUSTIFY_FLEX_START},
127     {"center", Toolkit::FlexContainer::JUSTIFY_CENTER},
128     {"flexEnd", Toolkit::FlexContainer::JUSTIFY_FLEX_END},
129     {"spaceBetween", Toolkit::FlexContainer::JUSTIFY_SPACE_BETWEEN},
130     {"spaceAround", Toolkit::FlexContainer::JUSTIFY_SPACE_AROUND},
131     {"spaceEvenly", Toolkit::FlexContainer::JUSTIFY_SPACE_EVENLY}};
132 const unsigned int JUSTIFY_CONTENT_STRING_TABLE_COUNT = sizeof(JUSTIFY_CONTENT_STRING_TABLE) / sizeof(JUSTIFY_CONTENT_STRING_TABLE[0]);
133
134 const Scripting::StringEnum ALIGN_ITEMS_STRING_TABLE[] =
135   {
136     {"flexStart", Toolkit::FlexContainer::ALIGN_FLEX_START},
137     {"center", Toolkit::FlexContainer::ALIGN_CENTER},
138     {"flexEnd", Toolkit::FlexContainer::ALIGN_FLEX_END},
139     {"stretch", Toolkit::FlexContainer::ALIGN_STRETCH}};
140 const unsigned int ALIGN_ITEMS_STRING_TABLE_COUNT = sizeof(ALIGN_ITEMS_STRING_TABLE) / sizeof(ALIGN_ITEMS_STRING_TABLE[0]);
141
142 const Scripting::StringEnum ALIGN_CONTENT_STRING_TABLE[] =
143   {
144     {"flexStart", Toolkit::FlexContainer::ALIGN_FLEX_START},
145     {"center", Toolkit::FlexContainer::ALIGN_CENTER},
146     {"flexEnd", Toolkit::FlexContainer::ALIGN_FLEX_END},
147     {"stretch", Toolkit::FlexContainer::ALIGN_STRETCH}};
148 const unsigned int ALIGN_CONTENT_STRING_TABLE_COUNT = sizeof(ALIGN_CONTENT_STRING_TABLE) / sizeof(ALIGN_CONTENT_STRING_TABLE[0]);
149
150 } // Unnamed namespace
151
152 Toolkit::FlexContainer FlexContainer::New()
153 {
154   // Create the implementation, temporarily owned by this handle on stack
155   IntrusivePtr<FlexContainer> impl = new FlexContainer();
156
157   // Pass ownership to CustomActor handle
158   Toolkit::FlexContainer handle(*impl);
159
160   // Second-phase init of the implementation
161   // This can only be done after the CustomActor connection has been made...
162   impl->Initialize();
163
164   return handle;
165 }
166
167 FlexContainer::~FlexContainer()
168 {
169   YGNodeFree(mRootNode.node);
170
171   for(unsigned int i = 0; i < mChildrenNodes.size(); i++)
172   {
173     YGNodeFree(mChildrenNodes[i].node);
174   }
175
176   mChildrenNodes.clear();
177 }
178
179 void FlexContainer::SetContentDirection(Toolkit::FlexContainer::ContentDirection contentDirection)
180 {
181   if(mContentDirection != contentDirection)
182   {
183     Dali::CustomActor ownerActor(GetOwner());
184
185     if(Toolkit::FlexContainer::INHERIT != contentDirection)
186     {
187       mContentDirection = contentDirection;
188
189       ownerActor.SetProperty(Dali::Actor::Property::INHERIT_LAYOUT_DIRECTION, false);
190
191       if(Toolkit::FlexContainer::LTR == contentDirection)
192       {
193         ownerActor.SetProperty(Dali::Actor::Property::LAYOUT_DIRECTION, Dali::LayoutDirection::LEFT_TO_RIGHT);
194       }
195       else
196       {
197         ownerActor.SetProperty(Dali::Actor::Property::LAYOUT_DIRECTION, Dali::LayoutDirection::RIGHT_TO_LEFT);
198       }
199     }
200     else
201     {
202       ownerActor.SetProperty(Dali::Actor::Property::INHERIT_LAYOUT_DIRECTION, true);
203
204       Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>(ownerActor.GetParent().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
205
206       if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
207       {
208         mContentDirection = Toolkit::FlexContainer::RTL;
209       }
210       else
211       {
212         mContentDirection = Toolkit::FlexContainer::LTR;
213       }
214     }
215
216     RelayoutRequest();
217   }
218 }
219
220 Toolkit::FlexContainer::ContentDirection FlexContainer::GetContentDirection()
221 {
222   return mContentDirection;
223 }
224
225 void FlexContainer::SetFlexDirection(Toolkit::FlexContainer::FlexDirection flexDirection)
226 {
227   if(mFlexDirection != flexDirection)
228   {
229     mFlexDirection = flexDirection;
230     YGNodeStyleSetFlexDirection(mRootNode.node, static_cast<YGFlexDirection>(flexDirection));
231
232     RelayoutRequest();
233   }
234 }
235
236 Toolkit::FlexContainer::FlexDirection FlexContainer::GetFlexDirection()
237 {
238   return mFlexDirection;
239 }
240
241 void FlexContainer::SetFlexWrap(Toolkit::FlexContainer::WrapType flexWrap)
242 {
243   if(mFlexWrap != flexWrap)
244   {
245     mFlexWrap = flexWrap;
246     YGNodeStyleSetFlexWrap(mRootNode.node, static_cast<YGWrap>(flexWrap));
247
248     RelayoutRequest();
249   }
250 }
251
252 Toolkit::FlexContainer::WrapType FlexContainer::GetFlexWrap()
253 {
254   return mFlexWrap;
255 }
256
257 void FlexContainer::SetJustifyContent(Toolkit::FlexContainer::Justification justifyContent)
258 {
259   if(mJustifyContent != justifyContent)
260   {
261     mJustifyContent = justifyContent;
262     YGNodeStyleSetJustifyContent(mRootNode.node, static_cast<YGJustify>(justifyContent));
263
264     RelayoutRequest();
265   }
266 }
267
268 Toolkit::FlexContainer::Justification FlexContainer::GetJustifyContent()
269 {
270   return mJustifyContent;
271 }
272
273 void FlexContainer::SetAlignItems(Toolkit::FlexContainer::Alignment alignItems)
274 {
275   if(mAlignItems != alignItems)
276   {
277     mAlignItems = alignItems;
278     YGNodeStyleSetAlignItems(mRootNode.node, static_cast<YGAlign>(alignItems));
279
280     RelayoutRequest();
281   }
282 }
283
284 Toolkit::FlexContainer::Alignment FlexContainer::GetAlignItems()
285 {
286   return mAlignItems;
287 }
288
289 void FlexContainer::SetAlignContent(Toolkit::FlexContainer::Alignment alignContent)
290 {
291   if(mAlignContent != alignContent)
292   {
293     mAlignContent = alignContent;
294     YGNodeStyleSetAlignContent(mRootNode.node, static_cast<YGAlign>(alignContent));
295
296     RelayoutRequest();
297   }
298 }
299
300 Toolkit::FlexContainer::Alignment FlexContainer::GetAlignContent()
301 {
302   return mAlignContent;
303 }
304
305 void FlexContainer::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
306 {
307   Toolkit::FlexContainer flexContainer = Toolkit::FlexContainer::DownCast(Dali::BaseHandle(object));
308
309   if(flexContainer)
310   {
311     FlexContainer& flexContainerImpl(GetImpl(flexContainer));
312     switch(index)
313     {
314       case Toolkit::FlexContainer::Property::CONTENT_DIRECTION:
315       {
316         Toolkit::FlexContainer::ContentDirection contentDirection(Toolkit::FlexContainer::INHERIT);
317
318         if(value.GetType() == Property::INTEGER)
319         {
320           flexContainerImpl.SetContentDirection(static_cast<Toolkit::FlexContainer::ContentDirection>(value.Get<int>()));
321         }
322         else if(Scripting::GetEnumeration<Toolkit::FlexContainer::ContentDirection>(value.Get<std::string>().c_str(),
323                                                                                     CONTENT_DIRECTION_STRING_TABLE,
324                                                                                     CONTENT_DIRECTION_STRING_TABLE_COUNT,
325                                                                                     contentDirection))
326         {
327           flexContainerImpl.SetContentDirection(contentDirection);
328         }
329         break;
330       }
331       case Toolkit::FlexContainer::Property::FLEX_DIRECTION:
332       {
333         Toolkit::FlexContainer::FlexDirection flexDirection(Toolkit::FlexContainer::COLUMN);
334
335         if(value.GetType() == Property::INTEGER)
336         {
337           flexContainerImpl.SetFlexDirection(static_cast<Toolkit::FlexContainer::FlexDirection>(value.Get<int>()));
338         }
339         else if(Scripting::GetEnumeration<Toolkit::FlexContainer::FlexDirection>(value.Get<std::string>().c_str(),
340                                                                                  FLEX_DIRECTION_STRING_TABLE,
341                                                                                  FLEX_DIRECTION_STRING_TABLE_COUNT,
342                                                                                  flexDirection))
343         {
344           flexContainerImpl.SetFlexDirection(flexDirection);
345         }
346         break;
347       }
348       case Toolkit::FlexContainer::Property::FLEX_WRAP:
349       {
350         Toolkit::FlexContainer::WrapType flexWrap(Toolkit::FlexContainer::NO_WRAP);
351
352         if(value.GetType() == Property::INTEGER)
353         {
354           flexContainerImpl.SetFlexWrap(static_cast<Toolkit::FlexContainer::WrapType>(value.Get<int>()));
355         }
356         else if(Scripting::GetEnumeration<Toolkit::FlexContainer::WrapType>(value.Get<std::string>().c_str(),
357                                                                             FLEX_WRAP_STRING_TABLE,
358                                                                             FLEX_WRAP_STRING_TABLE_COUNT,
359                                                                             flexWrap))
360         {
361           flexContainerImpl.SetFlexWrap(flexWrap);
362         }
363         break;
364       }
365       case Toolkit::FlexContainer::Property::JUSTIFY_CONTENT:
366       {
367         Toolkit::FlexContainer::Justification justifyContent(Toolkit::FlexContainer::JUSTIFY_FLEX_START);
368
369         if(value.GetType() == Property::INTEGER)
370         {
371           flexContainerImpl.SetJustifyContent(static_cast<Toolkit::FlexContainer::Justification>(value.Get<int>()));
372         }
373         else if(Scripting::GetEnumeration<Toolkit::FlexContainer::Justification>(value.Get<std::string>().c_str(),
374                                                                                  JUSTIFY_CONTENT_STRING_TABLE,
375                                                                                  JUSTIFY_CONTENT_STRING_TABLE_COUNT,
376                                                                                  justifyContent))
377         {
378           flexContainerImpl.SetJustifyContent(justifyContent);
379         }
380         break;
381       }
382       case Toolkit::FlexContainer::Property::ALIGN_ITEMS:
383       {
384         Toolkit::FlexContainer::Alignment alignItems(Toolkit::FlexContainer::ALIGN_STRETCH);
385
386         if(value.GetType() == Property::INTEGER)
387         {
388           flexContainerImpl.SetAlignItems(static_cast<Toolkit::FlexContainer::Alignment>(value.Get<int>()));
389         }
390         else if(Scripting::GetEnumeration<Toolkit::FlexContainer::Alignment>(value.Get<std::string>().c_str(),
391                                                                              ALIGN_ITEMS_STRING_TABLE,
392                                                                              ALIGN_ITEMS_STRING_TABLE_COUNT,
393                                                                              alignItems))
394         {
395           flexContainerImpl.SetAlignItems(alignItems);
396         }
397         break;
398       }
399       case Toolkit::FlexContainer::Property::ALIGN_CONTENT:
400       {
401         Toolkit::FlexContainer::Alignment alignContent(Toolkit::FlexContainer::ALIGN_FLEX_START);
402
403         if(value.GetType() == Property::INTEGER)
404         {
405           flexContainerImpl.SetAlignContent(static_cast<Toolkit::FlexContainer::Alignment>(value.Get<int>()));
406         }
407         else if(Scripting::GetEnumeration<Toolkit::FlexContainer::Alignment>(value.Get<std::string>().c_str(),
408                                                                              ALIGN_CONTENT_STRING_TABLE,
409                                                                              ALIGN_CONTENT_STRING_TABLE_COUNT,
410                                                                              alignContent))
411         {
412           flexContainerImpl.SetAlignContent(alignContent);
413         }
414         break;
415       }
416     }
417   }
418 }
419
420 Property::Value FlexContainer::GetProperty(BaseObject* object, Property::Index index)
421 {
422   Property::Value value;
423
424   Toolkit::FlexContainer flexContainer = Toolkit::FlexContainer::DownCast(Dali::BaseHandle(object));
425
426   if(flexContainer)
427   {
428     FlexContainer& flexContainerImpl(GetImpl(flexContainer));
429     switch(index)
430     {
431       case Toolkit::FlexContainer::Property::CONTENT_DIRECTION:
432       {
433         value = flexContainerImpl.GetContentDirection();
434         break;
435       }
436       case Toolkit::FlexContainer::Property::FLEX_DIRECTION:
437       {
438         value = flexContainerImpl.GetFlexDirection();
439         break;
440       }
441       case Toolkit::FlexContainer::Property::FLEX_WRAP:
442       {
443         value = flexContainerImpl.GetFlexWrap();
444         break;
445       }
446       case Toolkit::FlexContainer::Property::JUSTIFY_CONTENT:
447       {
448         value = flexContainerImpl.GetJustifyContent();
449         break;
450       }
451       case Toolkit::FlexContainer::Property::ALIGN_ITEMS:
452       {
453         value = flexContainerImpl.GetAlignItems();
454         break;
455       }
456       case Toolkit::FlexContainer::Property::ALIGN_CONTENT:
457       {
458         value = flexContainerImpl.GetAlignContent();
459         break;
460       }
461     }
462   }
463
464   return value;
465 }
466
467 void FlexContainer::OnChildAdd(Actor& child)
468 {
469   // Create a new node for the child.
470   FlexItemNode childNode;
471   childNode.actor = child;
472   childNode.node  = YGNodeNew();
473
474   mChildrenNodes.push_back(childNode);
475   YGNodeInsertChild(mRootNode.node, childNode.node, mChildrenNodes.size() - 1);
476
477   Control::OnChildAdd(child);
478 }
479
480 void FlexContainer::OnChildRemove(Actor& child)
481 {
482   for(unsigned int i = 0; i < mChildrenNodes.size(); i++)
483   {
484     if(mChildrenNodes[i].actor.GetHandle() == child)
485     {
486       YGNodeRemoveChild(mRootNode.node, mChildrenNodes[i].node);
487       YGNodeFree(mChildrenNodes[i].node);
488
489       mChildrenNodes.erase(mChildrenNodes.begin() + i);
490
491       // Relayout the container only if instances were found
492       RelayoutRequest();
493       break;
494     }
495   }
496
497   Control::OnChildRemove(child);
498 }
499
500 void FlexContainer::OnRelayout(const Vector2& size, RelayoutContainer& container)
501 {
502   for(unsigned int i = 0; i < mChildrenNodes.size(); i++)
503   {
504     Actor child = mChildrenNodes[i].actor.GetHandle();
505     if(child)
506     {
507       // Anchor actor to top left of the container
508       if(child.GetProperty(Actor::Property::POSITION_USES_ANCHOR_POINT).Get<bool>())
509       {
510         child.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
511       }
512       child.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
513
514       float negotiatedWidth  = child.GetRelayoutSize(Dimension::WIDTH);
515       float negotiatedHeight = child.GetRelayoutSize(Dimension::HEIGHT);
516
517       if(negotiatedWidth > 0)
518       {
519         YGNodeStyleSetWidth(mChildrenNodes[i].node, negotiatedWidth);
520       }
521       if(negotiatedHeight > 0)
522       {
523         YGNodeStyleSetHeight(mChildrenNodes[i].node, negotiatedHeight);
524       }
525     }
526   }
527
528   // Relayout the container
529   RelayoutChildren();
530 #if defined(FLEX_CONTAINER_DEBUG)
531   PrintNodes(mChildrenNodes);
532 #endif
533
534   for(unsigned int i = 0; i < mChildrenNodes.size(); i++)
535   {
536     Actor child = mChildrenNodes[i].actor.GetHandle();
537     if(child)
538     {
539       if(child.GetPropertyType(Toolkit::FlexContainer::ChildProperty::FLEX) != Property::NONE)
540       {
541         // Only Set to USE_ASSIGNED_SIZE if the child actor is flexible.
542
543         if(child.GetResizePolicy(Dimension::WIDTH) != ResizePolicy::USE_ASSIGNED_SIZE)
544         {
545           child.SetResizePolicy(ResizePolicy::USE_ASSIGNED_SIZE, Dimension::WIDTH);
546         }
547         if(child.GetResizePolicy(Dimension::HEIGHT) != ResizePolicy::USE_ASSIGNED_SIZE)
548         {
549           child.SetResizePolicy(ResizePolicy::USE_ASSIGNED_SIZE, Dimension::HEIGHT);
550         }
551       }
552       container.Add(child, Vector2(YGNodeLayoutGetWidth(mChildrenNodes[i].node), YGNodeLayoutGetHeight(mChildrenNodes[i].node)));
553     }
554   }
555 }
556
557 bool FlexContainer::RelayoutDependentOnChildren(Dimension::Type dimension)
558 {
559   return true;
560 }
561
562 void FlexContainer::OnSizeSet(const Vector3& size)
563 {
564   if(mRootNode.node)
565   {
566     Actor self = Self();
567     YGNodeStyleSetWidth(mRootNode.node, size.x);
568     YGNodeStyleSetHeight(mRootNode.node, size.y);
569
570     RelayoutRequest();
571   }
572
573   Control::OnSizeSet(size);
574 }
575
576 void FlexContainer::OnLayoutDirectionChanged(Dali::Actor actor, Dali::LayoutDirection::Type type)
577 {
578   Toolkit::FlexContainer                   flexContainer = Toolkit::FlexContainer::DownCast(actor);
579   Toolkit::FlexContainer::ContentDirection direction;
580
581   if(type == Dali::LayoutDirection::RIGHT_TO_LEFT)
582   {
583     direction = Toolkit::FlexContainer::RTL;
584   }
585   else
586   {
587     direction = Toolkit::FlexContainer::LTR;
588   }
589
590   Toolkit::Internal::FlexContainer& flexContainerImpl = GetImpl(flexContainer);
591
592   if(flexContainerImpl.mContentDirection != direction)
593   {
594     Dali::CustomActor ownerActor(flexContainerImpl.GetOwner());
595     flexContainerImpl.mContentDirection = direction;
596
597     flexContainerImpl.RelayoutRequest();
598   }
599 }
600
601 void FlexContainer::ComputeLayout()
602 {
603   if(mRootNode.node)
604   {
605     for(unsigned int i = 0; i < mChildrenNodes.size(); i++)
606     {
607       YGNodeRef childNode  = mChildrenNodes[i].node;
608       Actor     childActor = mChildrenNodes[i].actor.GetHandle();
609
610       // Intialize the style of the child.
611       YGNodeStyleSetMinWidth(childNode, childActor.GetProperty<Vector2>(Actor::Property::MINIMUM_SIZE).x);
612       YGNodeStyleSetMinHeight(childNode, childActor.GetProperty<Vector2>(Actor::Property::MINIMUM_SIZE).y);
613       YGNodeStyleSetMaxWidth(childNode, childActor.GetProperty<Vector2>(Actor::Property::MAXIMUM_SIZE).x);
614       YGNodeStyleSetMaxHeight(childNode, childActor.GetProperty<Vector2>(Actor::Property::MAXIMUM_SIZE).y);
615
616       // Check child properties on the child for how to layout it.
617       // These properties should be dynamically registered to the child which
618       // would be added to FlexContainer.
619
620       if(childActor.GetPropertyType(Toolkit::FlexContainer::ChildProperty::FLEX) != Property::NONE)
621       {
622         YGNodeStyleSetFlex(childNode, childActor.GetProperty(Toolkit::FlexContainer::ChildProperty::FLEX).Get<float>());
623       }
624
625       Toolkit::FlexContainer::Alignment alignSelf(Toolkit::FlexContainer::ALIGN_AUTO);
626       if(childActor.GetPropertyType(Toolkit::FlexContainer::ChildProperty::ALIGN_SELF) != Property::NONE)
627       {
628         Property::Value alignSelfPropertyValue = childActor.GetProperty(Toolkit::FlexContainer::ChildProperty::ALIGN_SELF);
629         if(alignSelfPropertyValue.GetType() == Property::INTEGER)
630         {
631           alignSelf = static_cast<Toolkit::FlexContainer::Alignment>(alignSelfPropertyValue.Get<int>());
632         }
633         else if(alignSelfPropertyValue.GetType() == Property::STRING)
634         {
635           std::string value = alignSelfPropertyValue.Get<std::string>();
636           Scripting::GetEnumeration<Toolkit::FlexContainer::Alignment>(value.c_str(),
637                                                                        ALIGN_SELF_STRING_TABLE,
638                                                                        ALIGN_SELF_STRING_TABLE_COUNT,
639                                                                        alignSelf);
640         }
641         YGNodeStyleSetAlignSelf(childNode, static_cast<YGAlign>(alignSelf));
642       }
643
644       if(childActor.GetPropertyType(Toolkit::FlexContainer::ChildProperty::FLEX_MARGIN) != Property::NONE)
645       {
646         Vector4 flexMargin = childActor.GetProperty(Toolkit::FlexContainer::ChildProperty::FLEX_MARGIN).Get<Vector4>();
647         YGNodeStyleSetMargin(childNode, YGEdgeLeft, flexMargin.x);
648         YGNodeStyleSetMargin(childNode, YGEdgeTop, flexMargin.y);
649         YGNodeStyleSetMargin(childNode, YGEdgeRight, flexMargin.z);
650         YGNodeStyleSetMargin(childNode, YGEdgeBottom, flexMargin.w);
651       }
652     }
653
654     // Calculate the layout
655     YGDirection nodeLayoutDirection = YGDirectionInherit;
656     switch(mContentDirection)
657     {
658       case Dali::Toolkit::FlexContainer::LTR:
659       {
660         nodeLayoutDirection = YGDirectionLTR;
661         break;
662       }
663
664       case Dali::Toolkit::FlexContainer::RTL:
665       {
666         nodeLayoutDirection = YGDirectionRTL;
667         break;
668       }
669
670       case Dali::Toolkit::FlexContainer::INHERIT:
671       {
672         nodeLayoutDirection = YGDirectionInherit;
673         break;
674       }
675     }
676
677 #if defined(FLEX_CONTAINER_DEBUG)
678     YGNodePrint(mRootNode.node, (YGPrintOptions)(YGPrintOptionsLayout | YGPrintOptionsStyle | YGPrintOptionsChildren));
679 #endif
680     YGNodeCalculateLayout(mRootNode.node, Self().GetProperty<Vector2>(Actor::Property::MAXIMUM_SIZE).x, Self().GetProperty<Vector2>(Actor::Property::MAXIMUM_SIZE).y, nodeLayoutDirection);
681 #if defined(FLEX_CONTAINER_DEBUG)
682     YGNodePrint(mRootNode.node, (YGPrintOptions)(YGPrintOptionsLayout | YGPrintOptionsStyle | YGPrintOptionsChildren));
683 #endif
684   }
685 }
686
687 void FlexContainer::RelayoutChildren()
688 {
689   ComputeLayout();
690
691   // Set size and position of children according to the layout calculation
692   for(unsigned int i = 0; i < mChildrenNodes.size(); i++)
693   {
694     Dali::Actor child = mChildrenNodes[i].actor.GetHandle();
695     if(child)
696     {
697       child.SetProperty(Actor::Property::POSITION_X, YGNodeLayoutGetLeft(mChildrenNodes[i].node));
698       child.SetProperty(Actor::Property::POSITION_Y, YGNodeLayoutGetTop(mChildrenNodes[i].node));
699     }
700   }
701 }
702
703 Actor FlexContainer::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
704 {
705   Actor nextFocusableActor;
706
707   // First check whether there is any items in the container
708   if(mChildrenNodes.size() > 0)
709   {
710     if(!currentFocusedActor || currentFocusedActor == Self())
711     {
712       // Nothing is currently focused, so the first child in the container should be focused.
713       nextFocusableActor = mChildrenNodes[0].actor.GetHandle();
714     }
715     else
716     {
717       // Check whether the current focused actor is within flex container
718       int currentFocusedActorIndex = -1;
719       for(unsigned int index = 0; index < mChildrenNodes.size(); index++)
720       {
721         if(currentFocusedActor == mChildrenNodes[index].actor.GetHandle())
722         {
723           currentFocusedActorIndex = index;
724           break;
725         }
726       }
727
728       if(currentFocusedActorIndex > -1)
729       {
730         int previousCheckedActorIndex = -1;
731         int nextFocusedActorIndex     = currentFocusedActorIndex;
732         switch(direction)
733         {
734           case Toolkit::Control::KeyboardFocus::LEFT:
735           case Toolkit::Control::KeyboardFocus::UP:
736           {
737             // Search the next focusable actor in the backward direction
738             do
739             {
740               nextFocusedActorIndex--;
741               if(nextFocusedActorIndex < 0)
742               {
743                 nextFocusedActorIndex = loopEnabled ? mChildrenNodes.size() - 1 : 0;
744               }
745               if(nextFocusedActorIndex != previousCheckedActorIndex && nextFocusedActorIndex != currentFocusedActorIndex)
746               {
747                 previousCheckedActorIndex = nextFocusedActorIndex;
748               }
749               else
750               {
751                 break;
752               }
753             } while(!mChildrenNodes[nextFocusedActorIndex].actor.GetHandle().GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE));
754             break;
755           }
756           case Toolkit::Control::KeyboardFocus::RIGHT:
757           case Toolkit::Control::KeyboardFocus::DOWN:
758           {
759             // Search the next focusable actor in the forward direction
760             do
761             {
762               nextFocusedActorIndex++;
763               if(nextFocusedActorIndex > static_cast<int>(mChildrenNodes.size() - 1))
764               {
765                 nextFocusedActorIndex = loopEnabled ? 0 : mChildrenNodes.size() - 1;
766               }
767               if(nextFocusedActorIndex != previousCheckedActorIndex && nextFocusedActorIndex != currentFocusedActorIndex)
768               {
769                 previousCheckedActorIndex = nextFocusedActorIndex;
770               }
771               else
772               {
773                 break;
774               }
775             } while(!mChildrenNodes[nextFocusedActorIndex].actor.GetHandle().GetProperty<bool>(Actor::Property::KEYBOARD_FOCUSABLE));
776             break;
777           }
778           default:
779           {
780             break;
781           }
782         }
783
784         if(nextFocusedActorIndex != currentFocusedActorIndex)
785         {
786           nextFocusableActor = mChildrenNodes[nextFocusedActorIndex].actor.GetHandle();
787         }
788         else
789         {
790           // No focusble child in the container
791           nextFocusableActor = Actor();
792         }
793       }
794       else
795       {
796         // The current focused actor is not within flex container, so the first child in the container should be focused.
797         nextFocusableActor = mChildrenNodes[0].actor.GetHandle();
798       }
799     }
800   }
801
802   return nextFocusableActor;
803 }
804
805 FlexContainer::FlexContainer()
806 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
807   mContentDirection(Toolkit::FlexContainer::INHERIT),
808   mFlexDirection(Toolkit::FlexContainer::COLUMN),
809   mFlexWrap(Toolkit::FlexContainer::NO_WRAP),
810   mJustifyContent(Toolkit::FlexContainer::JUSTIFY_FLEX_START),
811   mAlignItems(Toolkit::FlexContainer::ALIGN_STRETCH),
812   mAlignContent(Toolkit::FlexContainer::ALIGN_FLEX_START)
813 {
814   SetKeyboardNavigationSupport(true);
815 }
816
817 void FlexContainer::OnInitialize()
818 {
819   // Initialize the node for the flex container itself
820   Dali::Actor self = Self();
821   self.LayoutDirectionChangedSignal().Connect(this, &FlexContainer::OnLayoutDirectionChanged);
822
823   mRootNode.actor = self;
824   mRootNode.node  = YGNodeNew();
825   YGNodeSetContext(mRootNode.node, &mChildrenNodes);
826
827   // Set default style
828   YGNodeStyleSetFlexDirection(mRootNode.node, static_cast<YGFlexDirection>(mFlexDirection));
829   YGNodeStyleSetFlexWrap(mRootNode.node, static_cast<YGWrap>(mFlexWrap));
830   YGNodeStyleSetJustifyContent(mRootNode.node, static_cast<YGJustify>(mJustifyContent));
831   YGNodeStyleSetAlignItems(mRootNode.node, static_cast<YGAlign>(mAlignItems));
832   YGNodeStyleSetAlignContent(mRootNode.node, static_cast<YGAlign>(mAlignContent));
833
834   // Make self as keyboard focusable and focus group
835   self.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
836   SetAsKeyboardFocusGroup(true);
837
838   DevelControl::SetAccessibilityConstructor(self, [](Dali::Actor actor) {
839     return std::unique_ptr<Dali::Accessibility::Accessible>(
840       new DevelControl::AccessibleImpl(actor, Dali::Accessibility::Role::FILLER));
841   });
842 }
843
844 } // namespace Internal
845
846 } // namespace Toolkit
847
848 } // namespace Dali