Add child property support for FlexLayout
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / layouting / flex-node.cpp
1 /*
2  * Copyright (c) 2019 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-node.h"
19
20 //EXTERNAL INCLUDES
21 #include <dali/integration-api/debug.h>
22 #include <dali/public-api/actors/actor.h>
23 #include <dali/public-api/object/weak-handle.h>
24
25 //INTERNAL INCLUDES
26 #include <dali-toolkit/third-party/yoga/Yoga.h>
27
28 #if defined(DEBUG_ENABLED)
29 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_FLEX" );
30 #endif
31
32 namespace Dali
33 {
34 namespace Toolkit
35 {
36 namespace Flex
37 {
38
39 namespace
40 {
41 // Common callback function that is registered when AddChild is called.
42 // Calls MeasureNode which in turns calls the actual callback passed in AddChild not the common callback.
43 YGSize MeasureChild(YGNodeRef child, float width, YGMeasureMode measureModeWidth, float height, YGMeasureMode measureModeHeight)
44 {
45   DALI_LOG_INFO( gLogFilter, Debug::General, "MeasureChild\n" );
46   // Get the Node from the YGNodeRef
47   Toolkit::Flex::Node* childNode =  static_cast<Toolkit::Flex::Node*>(YGNodeGetContext(child));
48
49   YGSize childSize{ 1, 1 }; // Initialise variable.
50
51   DALI_ASSERT_DEBUG( childNode );
52
53   // Call measure function assigned to this Node
54   Toolkit::Flex::SizeTuple nodeSize = childNode->MeasureNode( width, measureModeWidth, height, measureModeHeight );
55   childSize.width = nodeSize.width;
56   childSize.height = nodeSize.height;
57   DALI_LOG_INFO( gLogFilter, Debug::General, "MeasureChild, childNode valid %f,%f\n", childSize.width, childSize.height );
58
59   return childSize;
60 }
61
62 } // unamed namespace
63
64 struct Node;
65
66 using NodePtr = std::unique_ptr<Node>;
67
68 using  FlexNodeVector = std::vector< NodePtr>;
69
70 struct Node::Impl
71 {
72   YGNodeRef mYogaNode;
73   MeasureCallback mMeasureCallback;
74   WeakHandle< Dali::Actor > mActor;
75   FlexNodeVector mChildNodes;
76 };
77
78 Node::Node() : mImpl( new Impl )
79 {
80   mImpl->mYogaNode = YGNodeNew();
81   YGNodeSetContext( mImpl->mYogaNode, this );
82   mImpl->mMeasureCallback = NULL;
83   DALI_LOG_INFO( gLogFilter, Debug::General, "Node()  Context [%p] set to mYogaNode[%p]\n", this, mImpl->mYogaNode );
84
85   // Set default style
86   YGNodeStyleSetFlexDirection( mImpl->mYogaNode, YGFlexDirectionColumn );
87   YGNodeStyleSetFlexWrap( mImpl->mYogaNode, YGWrapNoWrap );
88   YGNodeStyleSetJustifyContent( mImpl->mYogaNode, YGJustifyFlexStart );
89   YGNodeStyleSetAlignContent( mImpl->mYogaNode, YGAlignFlexStart );
90   YGNodeStyleSetAlignItems( mImpl->mYogaNode, YGAlignFlexStart );
91 }
92
93 Node::~Node()
94 {
95   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Destructor() >> \n");
96   if( mImpl->mYogaNode )
97   {
98     YGNodeFreeRecursive( mImpl->mYogaNode );
99     mImpl->mYogaNode = nullptr;
100   }
101   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Destructor() <<\n");
102 }
103
104 Node* Node::AddChild( Actor child, Extents margin, MeasureCallback measureFunction, int index )
105 {
106   if( child )
107   {
108     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "AddChild[%s] to node[%p] at index:%d\n", child.GetProperty< std::string >( Dali::Actor::Property::NAME ).c_str(), mImpl->mYogaNode, index );
109
110     NodePtr childNode( new Node() );
111
112     // Store measure function passed in so can call it when the MeasureChild function is called.
113     childNode->mImpl->mMeasureCallback = measureFunction;
114
115     childNode->mImpl->mActor = child;
116     Vector2 minumumSize = child.GetProperty< Vector2 >( Actor::Property::MINIMUM_SIZE );
117     Vector2 maximumSize = child.GetProperty< Vector2 >( Actor::Property::MAXIMUM_SIZE );
118
119     YGNodeStyleSetMaxWidth( childNode->mImpl->mYogaNode, maximumSize.width );
120     YGNodeStyleSetMaxHeight( childNode->mImpl->mYogaNode, maximumSize.height );
121     YGNodeStyleSetMinWidth( childNode->mImpl->mYogaNode, minumumSize.width );
122     YGNodeStyleSetMinHeight( childNode->mImpl->mYogaNode, minumumSize.height );
123
124     YGNodeStyleSetMargin( childNode->mImpl->mYogaNode, YGEdgeLeft, margin.start );
125     YGNodeStyleSetMargin( childNode->mImpl->mYogaNode, YGEdgeTop, margin.top );
126     YGNodeStyleSetMargin( childNode->mImpl->mYogaNode, YGEdgeRight, margin.end );
127     YGNodeStyleSetMargin( childNode->mImpl->mYogaNode, YGEdgeBottom, margin.bottom );
128
129     YGNodeSetMeasureFunc( childNode->mImpl->mYogaNode, &MeasureChild );
130
131     YGNodeInsertChild( mImpl->mYogaNode, childNode->mImpl->mYogaNode, index );
132
133     Node* result = childNode.get();
134     mImpl->mChildNodes.emplace_back( std::move(childNode) );
135
136     return result;;
137   }
138   return nullptr;
139 }
140
141 void Node::RemoveChild( Actor child )
142 {
143   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "RemoveChild child:[%s] from internal nodeCount[%d] childCount[%d]\n", child.GetProperty< std::string >( Dali::Actor::Property::NAME ).c_str(), YGNodeGetChildCount( mImpl->mYogaNode ), mImpl->mChildNodes.size()  );
144
145   auto iterator = std::find_if( mImpl->mChildNodes.begin(),mImpl->mChildNodes.end(),
146                                 [&child]( NodePtr& childNode ){ return childNode->mImpl->mActor.GetHandle() == child;});
147
148   if( iterator != mImpl->mChildNodes.end() )
149   {
150       YGNodeRemoveChild( mImpl->mYogaNode, (*iterator)->mImpl->mYogaNode );
151       mImpl->mChildNodes.erase(iterator);
152   }
153
154   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "RemoveChild internal nodeCount[%d] childCount[%d]\n", YGNodeGetChildCount( mImpl->mYogaNode ), mImpl->mChildNodes.size()  );
155 }
156
157 SizeTuple Node::MeasureNode( float width, int widthMode, float height, int heightMode)
158 {
159   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MeasureNode\n" );
160
161   // Execute callback registered with AddChild
162   Toolkit::Flex::SizeTuple nodeSize{8,8}; // Default size set to 8,8 to aid bug detection.
163   if( mImpl->mMeasureCallback && mImpl->mActor.GetHandle() )
164   {
165     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MeasureNode MeasureCallback executing on %s\n", mImpl->mActor.GetHandle().GetProperty< std::string >( Dali::Actor::Property::NAME ).c_str() );
166     nodeSize = mImpl->mMeasureCallback( mImpl->mActor.GetHandle(), width, widthMode, height, heightMode );
167   }
168   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MeasureNode nodeSize width:%f height:%f\n", nodeSize.width, nodeSize.height );
169   return nodeSize;
170 }
171
172 void Node::CalculateLayout(float availableWidth, float availableHeight, bool isRTL)
173 {
174   DALI_LOG_INFO( gLogFilter, Debug::General, "CalculateLayout availableSize(%f,%f)\n", availableWidth, availableHeight );
175   YGNodeCalculateLayout( mImpl->mYogaNode, availableWidth, availableHeight, isRTL ? YGDirectionRTL : YGDirectionLTR );
176 }
177
178 Dali::Vector4 Node::GetNodeFrame( int index ) const
179 {
180   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetNodeFrame[%d]\n", index );
181   YGNodeRef childNode = YGNodeGetChild( mImpl->mYogaNode, index );
182   Dali::Vector4 frame = Vector4::ZERO;
183   if(childNode)
184   {
185     frame.x = YGNodeLayoutGetLeft( childNode );
186     frame.y = YGNodeLayoutGetTop( childNode );
187     frame.z = frame.x + YGNodeLayoutGetWidth( childNode );
188     frame.w = frame.y + YGNodeLayoutGetHeight( childNode );
189     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetNodeFrame Node index[%d] child ptr[%p] GetYogaNodeFrame left:%f top:%f right:%f bottom:%f\n",
190                    index, childNode, frame.x , frame.y, frame.z, frame.w);
191   }
192   else
193   {
194     frame.x = YGNodeLayoutGetLeft( mImpl->mYogaNode );
195     frame.y = YGNodeLayoutGetTop( mImpl->mYogaNode );
196     frame.z = frame.x + YGNodeLayoutGetWidth( mImpl->mYogaNode );
197     frame.w = frame.y + YGNodeLayoutGetHeight( mImpl->mYogaNode );
198     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetNodeFrame Root index[%d] root ptr[%p] GetYogaNodeFrame left:%f top:%f right:%f bottom:%f\n",
199                    index, mImpl->mYogaNode, frame.x , frame.y, frame.z, frame.w);
200   }
201
202   return frame;
203 }
204 void Node::SetFlexDirection( Dali::Toolkit::Flex::FlexDirection flexDirection )
205 {
206   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex direction[%d]\n", flexDirection );
207
208   YGNodeStyleSetFlexDirection( mImpl->mYogaNode, static_cast<YGFlexDirection>(flexDirection) );
209 }
210
211 Dali::Toolkit::Flex::FlexDirection Node::GetFlexDirection() const
212 {
213   return static_cast<Dali::Toolkit::Flex::FlexDirection>(YGNodeStyleGetFlexDirection( mImpl->mYogaNode ));
214 }
215
216 void Node::SetFlexJustification( Dali::Toolkit::Flex::Justification flexJustification )
217 {
218   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex justification[%d]\n", flexJustification )
219
220   YGNodeStyleSetJustifyContent( mImpl->mYogaNode, static_cast<YGJustify>(flexJustification) );
221 }
222
223 Dali::Toolkit::Flex::Justification Node::GetFlexJustification() const
224 {
225   return static_cast<Dali::Toolkit::Flex::Justification>(YGNodeStyleGetJustifyContent( mImpl->mYogaNode ));
226 }
227
228 Dali::Toolkit::Flex::WrapType Node::GetFlexWrap() const
229 {
230   return static_cast<Dali::Toolkit::Flex::WrapType>(YGNodeStyleGetFlexWrap( mImpl->mYogaNode ));
231 }
232
233 void Node::SetFlexAlignment(Dali::Toolkit::Flex::Alignment flexAlignment )
234 {
235   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex alignment[%d]\n", flexAlignment )
236
237   YGNodeStyleSetAlignContent( mImpl->mYogaNode , static_cast<YGAlign>(flexAlignment) );
238 }
239
240 Dali::Toolkit::Flex::Alignment Node::GetFlexAlignment() const
241 {
242   return static_cast<Dali::Toolkit::Flex::Alignment>(YGNodeStyleGetAlignContent( mImpl->mYogaNode ));
243 }
244
245 void Node::SetFlexItemsAlignment(Dali::Toolkit::Flex::Alignment flexAlignment )
246 {
247   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex items alignment[%d] on mYogaNode[%p]\n", flexAlignment, mImpl->mYogaNode )
248
249   YGNodeStyleSetAlignItems( mImpl->mYogaNode, static_cast<YGAlign>(flexAlignment) );
250 }
251
252 Dali::Toolkit::Flex::Alignment Node::GetFlexItemsAlignment() const
253 {
254   return static_cast<Dali::Toolkit::Flex::Alignment>( YGNodeStyleGetAlignItems( mImpl->mYogaNode ));
255 }
256
257 void Node::SetFlexAlignmentSelf( Dali::Toolkit::Flex::Alignment flexAlignmentSelf )
258 {
259   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex alignment self [%d] on mYogaNode[%p]\n", flexAlignmentSelf, mImpl->mYogaNode )
260
261   YGNodeStyleSetAlignSelf( mImpl->mYogaNode, static_cast<YGAlign>(flexAlignmentSelf) );
262 }
263
264 Dali::Toolkit::Flex::Alignment Node::GetFlexAlignmentSelf() const
265 {
266   return static_cast<Dali::Toolkit::Flex::Alignment>(YGNodeStyleGetAlignSelf( mImpl->mYogaNode ));
267 }
268
269 void Node::SetFlexPositionType( Dali::Toolkit::Flex::PositionType flexPositionType )
270 {
271   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex position type [%d] on mYogaNode[%p]\n", flexPositionType, mImpl->mYogaNode )
272
273   YGNodeStyleSetPositionType( mImpl->mYogaNode, static_cast<YGPositionType>(flexPositionType) );
274 }
275
276 Dali::Toolkit::Flex::PositionType Node::GetFlexPositionType() const
277 {
278   return static_cast<Dali::Toolkit::Flex::PositionType>(YGNodeStyleGetPositionType( mImpl->mYogaNode ));
279 }
280
281 void Node::SetFlexAspectRatio( float flexAspectRatio )
282 {
283   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex aspect ratio [%d] on mYogaNode[%p]\n", flexAspectRatio, mImpl->mYogaNode )
284
285   YGNodeStyleSetAspectRatio( mImpl->mYogaNode, static_cast<float>(flexAspectRatio) );
286 }
287
288 float Node::GetFlexAspectRatio() const
289 {
290   return static_cast<float>(YGNodeStyleGetAspectRatio( mImpl->mYogaNode ));
291 }
292
293 void Node::SetFlexBasis( float flexBasis )
294 {
295   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex basis [%d] on mYogaNode[%p]\n", flexBasis, mImpl->mYogaNode )
296
297   YGNodeStyleSetFlexBasis( mImpl->mYogaNode, static_cast<float>(flexBasis) );
298 }
299
300 float Node::GetFlexBasis() const
301 {
302   return static_cast<float>(YGNodeStyleGetFlexBasis( mImpl->mYogaNode ).value);
303 }
304
305 void Node::SetFlexShrink( float flexShrink )
306 {
307   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex shrink [%d] on mYogaNode[%p]\n", flexShrink, mImpl->mYogaNode )
308
309   YGNodeStyleSetFlexShrink( mImpl->mYogaNode, static_cast<float>(flexShrink) );
310 }
311
312 float Node::GetFlexShrink() const
313 {
314   return static_cast<float>(YGNodeStyleGetFlexShrink( mImpl->mYogaNode ));
315 }
316
317 void Node::SetFlexGrow( float flexGrow )
318 {
319   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex grow [%d] on mYogaNode[%p]\n", flexGrow, mImpl->mYogaNode )
320
321   YGNodeStyleSetFlexGrow( mImpl->mYogaNode, static_cast<float>(flexGrow) );
322 }
323
324 float Node::GetFlexGrow() const
325 {
326   return static_cast<float>(YGNodeStyleGetFlexGrow( mImpl->mYogaNode ));
327 }
328
329 float Node::GetFlexWidth() const
330 {
331   float flexWidth = YGNodeLayoutGetWidth( mImpl->mYogaNode );
332   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Get flex mYogaNode[%p] width[%f]\n", mImpl->mYogaNode, flexWidth)
333
334   return flexWidth;
335 }
336
337 float Node::GetFlexHeight() const
338 {
339   float flexHeight = YGNodeLayoutGetHeight( mImpl->mYogaNode );
340   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Get flex mYogaNode[%p] height[%f]\n", mImpl->mYogaNode, flexHeight)
341
342   return flexHeight;
343 }
344
345 void Node::SetMargin( Extents margin )
346 {
347   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex margin\n")
348
349   YGNodeStyleSetMargin( mImpl->mYogaNode, YGEdgeLeft, margin.start );
350   YGNodeStyleSetMargin( mImpl->mYogaNode, YGEdgeTop, margin.top );
351   YGNodeStyleSetMargin( mImpl->mYogaNode, YGEdgeRight, margin.end );
352   YGNodeStyleSetMargin( mImpl->mYogaNode, YGEdgeBottom, margin.bottom );
353 }
354
355 void Node::SetPadding( Extents padding )
356 {
357   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set padding\n")
358
359   YGNodeStyleSetPadding( mImpl->mYogaNode, YGEdgeLeft, padding.start );
360   YGNodeStyleSetPadding( mImpl->mYogaNode, YGEdgeTop, padding.top );
361   YGNodeStyleSetPadding( mImpl->mYogaNode, YGEdgeRight, padding.end );
362   YGNodeStyleSetPadding( mImpl->mYogaNode, YGEdgeBottom, padding.bottom );
363 }
364
365 void Node::SetFlexWrap( Dali::Toolkit::Flex::WrapType wrapType )
366 {
367   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Set flex wrap[%d] on mYogaNode[%p]\n", wrapType, mImpl->mYogaNode )
368
369   YGNodeStyleSetFlexWrap( mImpl->mYogaNode, static_cast<YGWrap>(wrapType) );
370 }
371
372 } // Flex
373 } // namespace Toolkit
374 } // namespace Dali