2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "flex-layout-impl.h"
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>
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>
32 #if defined(DEBUG_ENABLED)
33 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
43 FlexLayoutPtr FlexLayout::New()
45 FlexLayoutPtr layout( new FlexLayout() );
49 void FlexLayout::SetFlexDirection( Dali::Toolkit::FlexLayout::FlexDirection flexDirection )
51 YGNodeStyleSetFlexDirection( mRoot, static_cast<YGFlexDirection>(flexDirection) );
54 Dali::Toolkit::FlexLayout::FlexDirection FlexLayout::GetFlexDirection() const
56 return static_cast<Dali::Toolkit::FlexLayout::FlexDirection>(YGNodeStyleGetFlexDirection( mRoot ));
59 void FlexLayout::SetFlexJustification( Dali::Toolkit::FlexLayout::Justification flexJustification )
61 YGNodeStyleSetJustifyContent( mRoot, static_cast<YGJustify>(flexJustification) );
64 Dali::Toolkit::FlexLayout::Justification FlexLayout::GetFlexJustification() const
66 return static_cast<Dali::Toolkit::FlexLayout::Justification>(YGNodeStyleGetJustifyContent( mRoot ));
69 void FlexLayout::SetFlexWrap( Dali::Toolkit::FlexLayout::WrapType wrapType )
71 YGNodeStyleSetFlexWrap( mRoot, static_cast<YGWrap>(wrapType) );
74 Dali::Toolkit::FlexLayout::WrapType FlexLayout::GetFlexWrap() const
76 return static_cast<Dali::Toolkit::FlexLayout::WrapType>(YGNodeStyleGetFlexWrap( mRoot ));
79 void FlexLayout::SetFlexAlignment( Dali::Toolkit::FlexLayout::Alignment::Type flexAlignment )
81 YGNodeStyleSetAlignContent( mRoot, static_cast<YGAlign>(flexAlignment) );
84 Dali::Toolkit::FlexLayout::Alignment::Type FlexLayout::GetFlexAlignment() const
86 return static_cast<Dali::Toolkit::FlexLayout::Alignment::Type>(YGNodeStyleGetAlignContent( mRoot ));
89 void FlexLayout::SetFlexItemsAlignment( Dali::Toolkit::FlexLayout::Alignment::Type flexAlignment )
91 YGNodeStyleSetAlignItems( mRoot, static_cast<YGAlign>(flexAlignment) );
94 Dali::Toolkit::FlexLayout::Alignment::Type FlexLayout::GetFlexItemsAlignment() const
96 return static_cast<Dali::Toolkit::FlexLayout::Alignment::Type>(YGNodeStyleGetAlignItems( mRoot ));
99 FlexLayout::FlexLayout()
104 YGNodeSetContext( mRoot, this );
107 YGNodeStyleSetFlexDirection( mRoot, YGFlexDirectionColumn );
108 YGNodeStyleSetFlexWrap( mRoot, YGWrapNoWrap );
109 YGNodeStyleSetJustifyContent( mRoot, YGJustifyFlexStart );
110 YGNodeStyleSetAlignContent( mRoot, YGAlignFlexStart );
111 YGNodeStyleSetAlignItems( mRoot, YGAlignFlexStart );
114 FlexLayout::~FlexLayout()
118 YGNodeFreeRecursive( mRoot );
122 void FlexLayout::DoInitialize()
126 void FlexLayout::DoRegisterChildProperties( const std::string& containerType )
128 auto typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( containerType );
131 Property::IndexContainer indices;
132 typeInfo.GetChildPropertyIndices( indices );
134 if( std::find( indices.Begin(), indices.End(), Toolkit::FlexLayout::ChildProperty::FLEX ) ==
137 ChildPropertyRegistration( typeInfo.GetName(), "flex", Toolkit::FlexLayout::ChildProperty::FLEX, Property::FLOAT );
140 if( std::find( indices.Begin(), indices.End(), Toolkit::FlexLayout::ChildProperty::ALIGN_SELF ) ==
143 ChildPropertyRegistration( typeInfo.GetName(), "alignSelf", Toolkit::FlexLayout::ChildProperty::ALIGN_SELF, Property::INTEGER );
148 void FlexLayout::OnChildAdd( LayoutItem& child )
150 auto owner = child.GetOwner();
151 if(!DevelHandle::DoesCustomPropertyExist(owner, Toolkit::FlexLayout::ChildProperty::FLEX ))
153 owner.SetProperty( Toolkit::FlexLayout::ChildProperty::FLEX, 0 );
155 if(!DevelHandle::DoesCustomPropertyExist(owner, Toolkit::FlexLayout::ChildProperty::ALIGN_SELF ))
157 owner.SetProperty( Toolkit::FlexLayout::ChildProperty::ALIGN_SELF, YGAlignAuto );
160 YGNodeRef node = YGNodeNew();
161 YGNodeSetContext( node, &child );
162 YGNodeSetMeasureFunc( node, OnChildMeasure );
163 YGNodeMarkDirty( node );
164 YGNodeInsertChild( mRoot, node, GetChildCount()-1 );
167 void FlexLayout::OnChildRemove( LayoutItem& child )
169 auto count = GetChildCount();
170 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
172 LayoutItemPtr childLayout = GetChildAt( childIndex );
173 if( &child == childLayout.Get() )
175 YGNodeRef node = YGNodeGetChild( mRoot, childIndex );
176 YGNodeRemoveChild( mRoot, node );
182 void FlexLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
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();
189 #if defined(DEBUG_ENABLED)
190 std::ostringstream oss;
191 oss << "FlexLayout::OnMeasure ";
194 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " Layout direction:" << actor.GetProperty( Actor::Property::LAYOUT_DIRECTION ).Get<int>() << " ";
196 oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
197 DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
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 );
209 float width = YGUndefined;
210 float height = YGUndefined;
212 YGNodeStyleSetWidth( mRoot, YGUndefined );
213 YGNodeStyleSetHeight( mRoot, YGUndefined );
214 YGNodeStyleSetMinWidth( mRoot, YGUndefined );
215 YGNodeStyleSetMinHeight( mRoot, YGUndefined );
216 YGNodeStyleSetMaxWidth( mRoot, YGUndefined );
217 YGNodeStyleSetMaxHeight( mRoot, YGUndefined );
219 if( widthMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY )
221 width = widthMeasureSpec.GetSize();
222 YGNodeStyleSetWidth( mRoot, width );
224 else if( widthMeasureSpec.GetMode() == MeasureSpec::Mode::AT_MOST )
226 width = widthMeasureSpec.GetSize();
227 YGNodeStyleSetMaxWidth( mRoot, width );
230 if (heightMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY)
232 height = heightMeasureSpec.GetSize();
233 YGNodeStyleSetHeight( mRoot, height );
235 else if (widthMeasureSpec.GetMode() == MeasureSpec::Mode::AT_MOST)
237 height = heightMeasureSpec.GetSize();
238 YGNodeStyleSetMaxHeight( mRoot, height );
242 YGNodeCalculateLayout( mRoot, width, height, isLayoutRtl ? YGDirectionRTL : YGDirectionLTR );
243 SetMeasuredDimensions( GetDefaultSize( YGNodeLayoutGetWidth(mRoot), widthMeasureSpec ),
244 GetDefaultSize( YGNodeLayoutGetHeight(mRoot), heightMeasureSpec ) );
247 void FlexLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
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;
255 #if defined(DEBUG_ENABLED)
256 std::ostringstream oss;
257 oss << "FlexLayout::OnLayout ";
260 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " Layout direction:" << actor.GetProperty( Actor::Property::LAYOUT_DIRECTION ).Get<int>() << " ";
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() );
266 YGNodeCalculateLayout( mRoot, width, height, isLayoutRtl ? YGDirectionRTL : YGDirectionLTR );
268 auto count = GetChildCount();
269 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
271 LayoutItemPtr childLayout = GetChildAt( childIndex );
272 if( childLayout != nullptr )
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 );
284 YGSize FlexLayout::OnChildMeasure( YGNodeRef node,
286 YGMeasureMode widthMode,
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 );
296 MeasureSpec::Mode measureWidthMode = MeasureSpec::Mode::AT_MOST;
297 MeasureSpec::Mode measureHeightMode = MeasureSpec::Mode::AT_MOST;
298 if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
300 if( innerWidth != YGUndefined)
302 desiredWidth = innerWidth;
304 measureWidthMode = MeasureSpec::Mode::EXACTLY;
307 if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
309 if( innerWidth != YGUndefined)
311 desiredHeight = innerHeight;
313 measureHeightMode = MeasureSpec::Mode::EXACTLY;
316 if( desiredWidth == Toolkit::ChildLayoutData::WRAP_CONTENT )
318 measureWidthMode = MeasureSpec::Mode::UNSPECIFIED;
321 if( desiredHeight == Toolkit::ChildLayoutData::WRAP_CONTENT )
323 measureHeightMode = MeasureSpec::Mode::UNSPECIFIED;
326 MeasureSpec widthMeasureSpec = MeasureSpec( desiredWidth, measureWidthMode );
327 MeasureSpec heightMeasureSpec = MeasureSpec( desiredHeight, measureHeightMode );
328 if( measureWidthMode == MeasureSpec::Mode::UNSPECIFIED ||
329 measureHeightMode == MeasureSpec::Mode::UNSPECIFIED )
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;
341 // Safety check to avoid going out of boundary
342 if( (innerWidth != YGUndefined && innerWidth != 0) && innerWidth < desiredWidth )
344 desiredWidth = innerWidth;
347 if( (innerHeight != YGUndefined && innerHeight != 0) && innerHeight < desiredHeight )
349 desiredHeight = innerHeight;
353 MeasureSpec::Mode ygWidthMode = static_cast<MeasureSpec::Mode>(widthMode);
354 if( measureWidthMode == MeasureSpec::Mode::EXACTLY )
356 ygWidthMode = MeasureSpec::Mode::EXACTLY;
359 MeasureSpec::Mode ygHeightMode = static_cast<MeasureSpec::Mode>(heightMode);
360 if( measureHeightMode == MeasureSpec::Mode::EXACTLY )
362 ygHeightMode = MeasureSpec::Mode::EXACTLY;
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 ";
373 oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << " ";
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() );
383 if( measureWidthMode == MeasureSpec::Mode::UNSPECIFIED ||
384 measureHeightMode == MeasureSpec::Mode::UNSPECIFIED )
386 if( ygWidthMeasureSpec == widthMeasureSpec && ygHeightMeasureSpec == heightMeasureSpec )
389 .width = childLayout->GetMeasuredWidth(),
390 .height = childLayout->GetMeasuredHeight(),
395 childLayout->Measure( ygWidthMeasureSpec, ygHeightMeasureSpec );
397 .width = childLayout->GetMeasuredWidth(),
398 .height = childLayout->GetMeasuredHeight(),
402 void FlexLayout::SetChildrenStyle()
406 auto count = GetChildCount();
407 for( unsigned int childIndex = 0; childIndex < count; childIndex++)
409 LayoutItemPtr childLayout = GetChildAt( childIndex );
410 if( childLayout != nullptr )
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 ));
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 );
426 YGNodeStyleSetPadding( childNode, YGEdgeLeft, padding.start );
427 YGNodeStyleSetPadding( childNode, YGEdgeTop, padding.top );
428 YGNodeStyleSetPadding( childNode, YGEdgeRight, padding.end );
429 YGNodeStyleSetPadding( childNode, YGEdgeBottom, padding.bottom );
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 )
438 YGNodeStyleSetMaxWidth( childNode, YGUndefined );
442 YGNodeStyleSetMaxWidth( childNode, childActor.GetMaximumSize().x );
444 if( childActor.GetMaximumSize().y == FLT_MAX )
446 YGNodeStyleSetMaxHeight( childNode, YGUndefined );
450 YGNodeStyleSetMaxHeight( childNode, childActor.GetMaximumSize().y );
453 YGNodeStyleSetFlex( childNode, flex );
454 YGNodeStyleSetAlignSelf( childNode, alignSelf );
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 );
464 } // namespace Internal
465 } // namespace Toolkit