2 * Copyright (c) 2022 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 <dali/internal/event/actors/actor-sizer.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/internal/event/actors/actor-relayouter.h>
26 #include <dali/internal/event/animation/animation-impl.h>
27 #include <dali/internal/event/size-negotiation/relayout-controller-impl.h>
28 #include <dali/internal/update/manager/update-manager.h>
29 #include <dali/internal/update/nodes/node-declarations.h>
30 #include <dali/internal/update/nodes/node-messages.h>
31 #include <dali/internal/update/nodes/node.h>
33 #if defined(DEBUG_ENABLED)
34 extern Debug::Filter* gLogRelayoutFilter;
40 * @brief Extract a given dimension from a Vector2
42 * @param[in] values The values to extract from
43 * @param[in] dimension The dimension to extract
44 * @return Return the value for the dimension
46 constexpr float GetDimensionValue(const Dali::Vector2& values, const Dali::Dimension::Type dimension)
50 case Dali::Dimension::WIDTH:
54 case Dali::Dimension::HEIGHT:
67 * @brief Keep a static recursionstack vector to avoid creating temporary vectors every Relayout().
69 static Dali::Internal::ActorSizer::ActorDimensionStack gRecursionStack{};
73 namespace Dali::Internal
75 ActorSizer::ActorSizer(Internal::Actor& owner)
77 mRelayoutData(nullptr),
78 mTargetSize(Vector3::ZERO),
79 mAnimatedSize(Vector3::ZERO),
80 mUseAnimatedSize(AnimatedSizeFlag::CLEAR),
81 mInsideOnSizeSet(false)
85 ActorSizer::~ActorSizer()
87 // Delete optional relayout data
91 void ActorSizer::SetSizeModeFactor(const Vector3& factor)
95 mRelayoutData->sizeModeFactor = factor;
97 const Vector3& ActorSizer::GetSizeModeFactor() const
99 return mRelayoutData ? mRelayoutData->sizeModeFactor : Relayouter::DEFAULT_SIZE_MODE_FACTOR;
102 void ActorSizer::SetSize(const Vector3& size)
104 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
106 // TODO we cannot just ignore the given Z but that means rewrite the size negotiation!!
107 SetPreferredSize(size.GetVectorXY());
111 SetSizeInternal(size);
115 void ActorSizer::SetSizeInternal(const Vector3& size)
117 // dont allow recursive loop
118 DALI_ASSERT_ALWAYS(!mInsideOnSizeSet && "Cannot call SetSize from OnSizeSet");
119 // check that we have a node AND the new size width, height or depth is at least a little bit different from the old one
120 if(mTargetSize != size || mTargetSize != mOwner.GetCurrentSize())
124 // Update the preferred size after relayoutting
125 // It should be used in the next relayoutting
126 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH && mRelayoutData)
128 mRelayoutData->preferredSize.width = mAnimatedSize.width;
131 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT && mRelayoutData)
133 mRelayoutData->preferredSize.height = mAnimatedSize.height;
136 // node is being used in a separate thread; queue a message to set the value & base value
137 auto& node = mOwner.GetNode();
138 SceneGraph::NodeTransformPropertyMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::Bake, mTargetSize);
140 // Notification for derived classes
141 mInsideOnSizeSet = true;
142 mOwner.OnSizeSet(mTargetSize);
143 mInsideOnSizeSet = false;
145 // Raise a relayout request if the flag is not locked
146 if(mRelayoutData && !mRelayoutData->insideRelayout)
153 void ActorSizer::SetWidth(float width)
155 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
157 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
158 mRelayoutData->preferredSize.width = width;
162 mTargetSize.width = width;
164 // node is being used in a separate thread; queue a message to set the value & base value
165 auto& node = mOwner.GetNode();
166 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeX, width);
169 mUseAnimatedSize &= ~AnimatedSizeFlag::WIDTH;
173 void ActorSizer::SetHeight(float height)
175 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
177 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
178 mRelayoutData->preferredSize.height = height;
182 mTargetSize.height = height;
184 // node is being used in a separate thread; queue a message to set the value & base value
185 auto& node = mOwner.GetNode();
186 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeY, height);
189 mUseAnimatedSize &= ~AnimatedSizeFlag::HEIGHT;
193 void ActorSizer::SetDepth(float depth)
195 mTargetSize.depth = depth;
197 mUseAnimatedSize &= ~AnimatedSizeFlag::DEPTH;
199 // node is being used in a separate thread; queue a message to set the value & base value
200 auto& node = mOwner.GetNode();
201 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeZ, depth);
204 Vector3 ActorSizer::GetTargetSize() const
206 Vector3 size = mTargetSize;
208 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH)
210 // Should return animated size if size is animated
211 size.width = mAnimatedSize.width;
215 // Should return preferred size if size is fixed as set by SetSize
216 if(GetResizePolicy(Dimension::WIDTH) == ResizePolicy::FIXED)
218 size.width = GetPreferredSize().width;
222 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT)
224 size.height = mAnimatedSize.height;
228 if(GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::FIXED)
230 size.height = GetPreferredSize().height;
234 if(mUseAnimatedSize & AnimatedSizeFlag::DEPTH)
236 size.depth = mAnimatedSize.depth;
242 void ActorSizer::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
244 EnsureRelayouter().SetResizePolicy(policy, dimension, mTargetSize);
245 mOwner.OnSetResizePolicy(policy, dimension);
249 ResizePolicy::Type ActorSizer::GetResizePolicy(Dimension::Type dimension) const
251 return mRelayoutData ? mRelayoutData->GetResizePolicy(dimension) : ResizePolicy::DEFAULT;
254 void ActorSizer::SetSizeScalePolicy(SizeScalePolicy::Type policy)
257 mRelayoutData->sizeSetPolicy = policy;
261 SizeScalePolicy::Type ActorSizer::GetSizeScalePolicy() const
263 return mRelayoutData ? mRelayoutData->sizeSetPolicy : Relayouter::DEFAULT_SIZE_SCALE_POLICY;
266 Dimension::Type ActorSizer::GetDimensionDependency(Dimension::Type dimension) const
268 return mRelayoutData ? mRelayoutData->GetDimensionDependency(dimension) : Dimension::ALL_DIMENSIONS;
271 void ActorSizer::SetRelayoutEnabled(bool relayoutEnabled)
273 // If relayout data has not been allocated yet and the client is requesting
274 // to disable it, do nothing
275 if(mRelayoutData || relayoutEnabled)
279 DALI_ASSERT_DEBUG(mRelayoutData && "mRelayoutData not created");
281 mRelayoutData->relayoutEnabled = relayoutEnabled;
285 bool ActorSizer::IsRelayoutEnabled() const
287 // Assume that if relayout data has not been allocated yet then relayout is disabled
288 return mRelayoutData && mRelayoutData->relayoutEnabled;
291 void ActorSizer::SetLayoutDirty(bool dirty, Dimension::Type dimension)
293 EnsureRelayouter().SetLayoutDirty(dirty, dimension);
296 bool ActorSizer::IsLayoutDirty(Dimension::Type dimension) const
298 return mRelayoutData && mRelayoutData->IsLayoutDirty(dimension);
301 bool ActorSizer::RelayoutPossible(Dimension::Type dimension) const
303 return mRelayoutData && mRelayoutData->relayoutEnabled && !IsLayoutDirty(dimension);
306 bool ActorSizer::RelayoutRequired(Dimension::Type dimension) const
308 return mRelayoutData && mRelayoutData->relayoutEnabled && IsLayoutDirty(dimension);
311 ActorSizer::Relayouter& ActorSizer::EnsureRelayouter()
316 mRelayoutData = new Relayouter();
319 return *mRelayoutData;
322 bool ActorSizer::RelayoutDependentOnParent(Dimension::Type dimension)
324 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE. This
325 // will keep the existing behaviour, and return false.
327 // Check if actor is dependent on parent
328 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
330 if((dimension & (1 << i)))
332 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
333 if(resizePolicy == ResizePolicy::FILL_TO_PARENT || resizePolicy == ResizePolicy::SIZE_RELATIVE_TO_PARENT || resizePolicy == ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT)
343 bool ActorSizer::RelayoutDependentOnChildrenBase(Dimension::Type dimension)
345 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE.
346 // This means this will return true when there is no relayouting, but that seems
347 // counter-intuitive. Will keep current behaviour for now, as it is consistent.
349 // Check if actor is dependent on children
350 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
352 if((dimension & (1 << i)))
354 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
357 case ResizePolicy::FIT_TO_CHILDREN:
358 case ResizePolicy::USE_NATURAL_SIZE: // i.e. For things that calculate their size based on children
374 bool ActorSizer::RelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependentDimension)
376 return mRelayoutData && mRelayoutData->GetRelayoutDependentOnDimension(dimension, dependentDimension);
379 void ActorSizer::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
383 mRelayoutData->SetNegotiatedDimension(negotiatedDimension, dimension);
387 float ActorSizer::GetNegotiatedDimension(Dimension::Type dimension) const
389 return mRelayoutData ? mRelayoutData->GetNegotiatedDimension(dimension) : 0.0f;
392 void ActorSizer::SetPadding(const Vector2& padding, Dimension::Type dimension)
394 EnsureRelayouter().SetPadding(padding, dimension);
397 Vector2 ActorSizer::GetPadding(Dimension::Type dimension) const
399 return mRelayoutData ? mRelayoutData->GetPadding(dimension) : Relayouter::DEFAULT_DIMENSION_PADDING;
402 void ActorSizer::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
404 EnsureRelayouter().SetLayoutNegotiated(negotiated, dimension);
407 bool ActorSizer::IsLayoutNegotiated(Dimension::Type dimension) const
409 return mRelayoutData && mRelayoutData->IsLayoutNegotiated(dimension);
412 float ActorSizer::GetHeightForWidthBase(float width)
414 const Vector3 naturalSize = mOwner.GetNaturalSize();
415 return naturalSize.width > 0.0f ? naturalSize.height * width / naturalSize.width : width;
418 float ActorSizer::GetWidthForHeightBase(float height)
420 const Vector3 naturalSize = mOwner.GetNaturalSize();
421 return naturalSize.height > 0.0f ? naturalSize.width * height / naturalSize.height : height;
424 float ActorSizer::CalculateChildSizeBase(const Dali::Actor& child, Dimension::Type dimension)
426 // Fill to parent, taking size mode factor into account
427 switch(child.GetResizePolicy(dimension))
429 case ResizePolicy::FILL_TO_PARENT:
431 return GetLatestSize(dimension);
434 case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
436 Property::Value value = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
437 Vector3 childSizeModeFactor = value.Get<Vector3>();
438 return GetLatestSize(dimension) * GetDimensionValue(childSizeModeFactor, dimension);
441 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
443 Property::Value value = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
444 Vector3 childSizeModeFactor = value.Get<Vector3>();
445 return GetLatestSize(dimension) + GetDimensionValue(childSizeModeFactor, dimension);
450 return GetLatestSize(dimension);
455 float ActorSizer::GetLatestSize(Dimension::Type dimension) const
457 return IsLayoutNegotiated(dimension) ? GetNegotiatedDimension(dimension) : GetSize(dimension);
460 float ActorSizer::GetRelayoutSize(Dimension::Type dimension) const
462 Vector2 padding = GetPadding(dimension);
464 return GetLatestSize(dimension) + padding.x + padding.y;
467 float ActorSizer::NegotiateFromParent(Dimension::Type dimension)
469 Actor* parent = mOwner.GetParent();
472 Vector2 padding(GetPadding(dimension));
473 Vector2 parentPadding(parent->mSizer.GetPadding(dimension));
475 // Need to use actor API here to allow deriving actors to layout their children
476 return parent->CalculateChildSize(Dali::Actor(&mOwner), dimension) - parentPadding.x - parentPadding.y - padding.x - padding.y;
482 float ActorSizer::NegotiateFromChildren(Dimension::Type dimension)
484 float maxDimensionPoint = 0.0f;
486 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
488 ActorPtr child = mOwner.GetChildAt(i);
490 if(!child->RelayoutDependentOnParent(dimension))
492 // Calculate the min and max points that the children range across
493 float childPosition = GetDimensionValue(child->GetTargetPosition(), dimension);
494 float dimensionSize = child->mSizer.GetRelayoutSize(dimension);
495 maxDimensionPoint = std::max(maxDimensionPoint, childPosition + dimensionSize);
499 return maxDimensionPoint;
502 float ActorSizer::GetSize(Dimension::Type dimension) const
504 return GetDimensionValue(mTargetSize, dimension);
507 float ActorSizer::GetNaturalSize(Dimension::Type dimension) const
509 return GetDimensionValue(mOwner.GetNaturalSize(), dimension);
512 Vector2 ActorSizer::ApplySizeSetPolicy(const Vector2& size)
514 return mRelayoutData->ApplySizeSetPolicy(mOwner, size);
517 void ActorSizer::SetNegotiatedSize(RelayoutContainer& container)
519 // Do the set actor size
520 Vector2 negotiatedSize(GetLatestSize(Dimension::WIDTH), GetLatestSize(Dimension::HEIGHT));
522 // Adjust for size set policy
523 negotiatedSize = ApplySizeSetPolicy(negotiatedSize);
525 // Lock the flag to stop recursive relayouts on set size
526 mRelayoutData->insideRelayout = true;
527 SetSize(Vector3(negotiatedSize.width, negotiatedSize.height, 0.0f));
528 mRelayoutData->insideRelayout = false;
530 // Clear flags for all dimensions
531 SetLayoutDirty(false);
533 // Give deriving classes a chance to respond
534 mOwner.OnRelayout(negotiatedSize, container);
536 if(!mOwner.mOnRelayoutSignal.Empty())
538 Dali::Actor handle(&mOwner);
539 mOwner.mOnRelayoutSignal.Emit(handle);
542 mRelayoutData->relayoutRequested = false;
545 void ActorSizer::NegotiateSize(const Vector2& allocatedSize, RelayoutContainer& container)
547 // Force a size negotiation for actors that has assigned size during relayout
548 // This is required as otherwise the flags that force a relayout will not
549 // necessarilly be set. This will occur if the actor has already been laid out.
550 // The dirty flags are then cleared. Then if the actor is added back into the
551 // relayout container afterwards, the dirty flags would still be clear...
552 // causing a relayout to be skipped. Here we force any actors added to the
553 // container to be relayed out.
554 DALI_LOG_TIMER_START(NegSizeTimer1);
556 if(GetUseAssignedSize(Dimension::WIDTH))
558 SetLayoutNegotiated(false, Dimension::WIDTH);
560 if(GetUseAssignedSize(Dimension::HEIGHT))
562 SetLayoutNegotiated(false, Dimension::HEIGHT);
565 // Do the negotiation
566 NegotiateDimensions(allocatedSize);
568 // Set the actor size
569 SetNegotiatedSize(container);
571 // Negotiate down to children
572 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
574 ActorPtr child = mOwner.GetChildAt(i);
575 ActorSizer& childSizer = child->mSizer;
577 // Forces children that have already been laid out to be relayed out
578 // if they have assigned size during relayout.
579 if(childSizer.GetUseAssignedSize(Dimension::WIDTH))
581 childSizer.SetLayoutNegotiated(false, Dimension::WIDTH);
582 childSizer.SetLayoutDirty(true, Dimension::WIDTH);
585 if(childSizer.GetUseAssignedSize(Dimension::HEIGHT))
587 childSizer.SetLayoutNegotiated(false, Dimension::HEIGHT);
588 childSizer.SetLayoutDirty(true, Dimension::HEIGHT);
591 // Only relayout if required
592 if(childSizer.RelayoutRequired())
594 container.Add(Dali::Actor(child.Get()), mTargetSize.GetVectorXY());
598 // Reset the flag so that size negotiation will respect the actor's original resize policy
599 SetUseAssignedSize(false);
600 DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
603 void ActorSizer::SetUseAssignedSize(bool use, Dimension::Type dimension)
607 mRelayoutData->SetUseAssignedSize(use, dimension);
611 bool ActorSizer::GetUseAssignedSize(Dimension::Type dimension) const
613 return mRelayoutData && mRelayoutData->GetUseAssignedSize(dimension);
616 void ActorSizer::RelayoutRequest(Dimension::Type dimension)
618 Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
619 if(relayoutController)
621 Dali::Actor owner(&mOwner);
622 relayoutController->RequestRelayout(owner, dimension);
626 mRelayoutData->relayoutRequested = true;
631 void ActorSizer::SetPreferredSize(const Vector2& size)
635 // If valid width or height, then set the resize policy to FIXED
636 // A 0 width or height may also be required so if the resize policy has not been changed, i.e. is still set to DEFAULT,
637 // then change to FIXED as well
639 if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
641 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
644 if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
646 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
649 mRelayoutData->preferredSize = size;
650 mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
654 Vector2 ActorSizer::GetPreferredSize() const
656 return mRelayoutData ? Vector2(mRelayoutData->preferredSize) : Relayouter::DEFAULT_PREFERRED_SIZE;
659 void ActorSizer::SetMinimumSize(float size, Dimension::Type dimension)
661 EnsureRelayouter().SetMinimumSize(size, dimension);
665 float ActorSizer::GetMinimumSize(Dimension::Type dimension) const
667 return mRelayoutData ? mRelayoutData->GetMinimumSize(dimension) : 0.0f;
670 void ActorSizer::SetMaximumSize(float size, Dimension::Type dimension)
672 EnsureRelayouter().SetMaximumSize(size, dimension);
676 float ActorSizer::GetMaximumSize(Dimension::Type dimension) const
678 return mRelayoutData ? mRelayoutData->GetMaximumSize(dimension) : FLT_MAX;
681 void ActorSizer::OnAnimateSize(Animation& animation, Vector3 targetSize, bool relative)
683 mTargetSize = targetSize + mTargetSize * float(relative);
684 mAnimatedSize = mTargetSize;
685 mUseAnimatedSize = AnimatedSizeFlag::WIDTH | AnimatedSizeFlag::HEIGHT | AnimatedSizeFlag::DEPTH;
687 if(mRelayoutData && !mRelayoutData->relayoutRequested)
689 mRelayoutData->preferredSize.width = mAnimatedSize.width;
690 mRelayoutData->preferredSize.height = mAnimatedSize.height;
693 // Notify deriving classes
694 mOwner.OnSizeAnimation(animation, mTargetSize);
697 void ActorSizer::OnAnimateWidth(Animation& animation, float width, bool relative)
699 mTargetSize.width = width + float(relative) * mTargetSize.width;
700 mAnimatedSize.width = mTargetSize.width;
701 mUseAnimatedSize |= AnimatedSizeFlag::WIDTH;
703 if(mRelayoutData && !mRelayoutData->relayoutRequested)
705 mRelayoutData->preferredSize.width = mAnimatedSize.width;
708 // Notify deriving classes
709 mOwner.OnSizeAnimation(animation, mTargetSize);
712 void ActorSizer::OnAnimateHeight(Animation& animation, float height, bool relative)
714 mTargetSize.height = height + float(relative) * mTargetSize.height;
715 mAnimatedSize.height = mTargetSize.height;
716 mUseAnimatedSize |= AnimatedSizeFlag::HEIGHT;
718 if(mRelayoutData && !mRelayoutData->relayoutRequested)
720 mRelayoutData->preferredSize.height = mAnimatedSize.height;
723 // Notify deriving classes
724 mOwner.OnSizeAnimation(animation, mTargetSize);
727 void ActorSizer::OnAnimateDepth(Animation& animation, float depth, bool relative)
729 mTargetSize.depth = depth + float(relative) * mTargetSize.depth;
730 mAnimatedSize.depth = mTargetSize.depth;
731 mUseAnimatedSize |= AnimatedSizeFlag::DEPTH;
733 // Notify deriving classes
734 mOwner.OnSizeAnimation(animation, mTargetSize);
738 * @brief Extract a given dimension from a Vector3
740 * @param[in] values The values to extract from
741 * @param[in] dimension The dimension to extract
742 * @return Return the value for the dimension
744 float ActorSizer::GetDimensionValue(const Vector3& values, const Dimension::Type dimension) const
746 return ::GetDimensionValue(values.GetVectorXY(), dimension);
749 float ActorSizer::ClampDimension(float size, Dimension::Type dimension) const
751 const float minSize = GetMinimumSize(dimension);
752 const float maxSize = GetMaximumSize(dimension);
754 return std::max(minSize, std::min(size, maxSize));
757 void ActorSizer::NegotiateDimension(Dimension::Type dimension, const Vector2& allocatedSize, ActorDimensionStack& recursionStack)
759 // Check if it needs to be negotiated
760 if(IsLayoutDirty(dimension) && !IsLayoutNegotiated(dimension))
762 // Check that we havn't gotten into an infinite loop
763 ActorDimensionPair searchActor = ActorDimensionPair(&mOwner, dimension);
764 bool recursionFound = false;
765 for(auto& element : recursionStack)
767 if(element == searchActor)
769 recursionFound = true;
776 // Record the path that we have taken
777 recursionStack.emplace_back(&mOwner, dimension);
779 // Dimension dependency check
780 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
782 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
784 if(RelayoutDependentOnDimension(dimension, dimensionToCheck))
786 NegotiateDimension(dimensionToCheck, allocatedSize, recursionStack);
790 // Parent dependency check
791 Actor* parent = mOwner.GetParent();
792 if(parent && RelayoutDependentOnParent(dimension))
794 parent->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
797 // Children dependency check
798 if(mOwner.RelayoutDependentOnChildren(dimension))
800 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
802 ActorPtr child = mOwner.GetChildAt(i);
804 // Only relayout child first if it is not dependent on this actor
805 if(!child->RelayoutDependentOnParent(dimension))
807 child->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
812 // For deriving classes
813 mOwner.OnCalculateRelayoutSize(dimension);
815 // All dependencies checked, calculate the size and set negotiated flag
816 const float newSize = ClampDimension(CalculateSize(dimension, allocatedSize), dimension);
818 SetNegotiatedDimension(newSize, dimension);
819 SetLayoutNegotiated(true, dimension);
821 // For deriving classes
822 mOwner.OnLayoutNegotiated(newSize, dimension);
824 // This actor has been successfully processed, pop it off the recursion stack
825 recursionStack.pop_back();
829 // TODO: Break infinite loop
830 SetLayoutNegotiated(true, dimension);
835 void ActorSizer::NegotiateDimensions(const Vector2& allocatedSize)
837 // Negotiate all dimensions that require it
838 gRecursionStack.clear();
840 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
842 const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
845 NegotiateDimension(dimension, allocatedSize, gRecursionStack);
849 float ActorSizer::CalculateSize(Dimension::Type dimension, const Vector2& maximumSize)
851 switch(GetResizePolicy(dimension))
853 case ResizePolicy::USE_NATURAL_SIZE:
855 return GetNaturalSize(dimension);
858 case ResizePolicy::FIXED:
860 return ::GetDimensionValue(GetPreferredSize(), dimension);
863 case ResizePolicy::USE_ASSIGNED_SIZE:
865 return ::GetDimensionValue(maximumSize, dimension);
868 case ResizePolicy::FILL_TO_PARENT:
869 case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
870 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
872 return NegotiateFromParent(dimension);
875 case ResizePolicy::FIT_TO_CHILDREN:
877 return NegotiateFromChildren(dimension);
880 case ResizePolicy::DIMENSION_DEPENDENCY:
882 const Dimension::Type dimensionDependency = GetDimensionDependency(dimension);
885 if(dimension == Dimension::WIDTH && dimensionDependency == Dimension::HEIGHT)
887 // Use actor API to allow deriving actors to layout their content
888 return mOwner.GetWidthForHeight(GetNegotiatedDimension(Dimension::HEIGHT));
891 if(dimension == Dimension::HEIGHT && dimensionDependency == Dimension::WIDTH)
893 // Use actor API to allow deriving actors to layout their content
894 return mOwner.GetHeightForWidth(GetNegotiatedDimension(Dimension::WIDTH));
906 return 0.0f; // Default
909 } // namespace Dali::Internal