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 mTargetSizeDirtyFlag(false),
82 mInsideOnSizeSet(false)
86 ActorSizer::~ActorSizer()
88 // Delete optional relayout data
92 void ActorSizer::SetSizeModeFactor(const Vector3& factor)
96 mRelayoutData->sizeModeFactor = factor;
98 const Vector3& ActorSizer::GetSizeModeFactor() const
100 return mRelayoutData ? mRelayoutData->sizeModeFactor : Relayouter::DEFAULT_SIZE_MODE_FACTOR;
103 void ActorSizer::SetSize(const Vector3& size)
105 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
107 // TODO we cannot just ignore the given Z but that means rewrite the size negotiation!!
108 SetPreferredSize(size.GetVectorXY());
112 SetSizeInternal(size);
116 void ActorSizer::SetSizeInternal(const Vector3& size)
118 // dont allow recursive loop
119 DALI_ASSERT_ALWAYS(!mInsideOnSizeSet && "Cannot call SetSize from OnSizeSet");
120 // 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
121 if(mTargetSize != size || mTargetSizeDirtyFlag)
123 mTargetSizeDirtyFlag = false;
126 // Update the preferred size after relayoutting
127 // It should be used in the next relayoutting
128 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH && mRelayoutData)
130 mRelayoutData->preferredSize.width = mAnimatedSize.width;
133 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT && mRelayoutData)
135 mRelayoutData->preferredSize.height = mAnimatedSize.height;
138 // node is being used in a separate thread; queue a message to set the value & base value
139 auto& node = mOwner.GetNode();
140 SceneGraph::NodeTransformPropertyMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::Bake, mTargetSize);
142 // Notification for derived classes
143 mInsideOnSizeSet = true;
144 mOwner.OnSizeSet(mTargetSize);
145 mInsideOnSizeSet = false;
147 // Raise a relayout request if the flag is not locked
148 if(mRelayoutData && !mRelayoutData->insideRelayout)
155 void ActorSizer::SetWidth(float width)
157 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
159 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
160 mRelayoutData->preferredSize.width = width;
164 mTargetSize.width = width;
166 // node is being used in a separate thread; queue a message to set the value & base value
167 auto& node = mOwner.GetNode();
168 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeX, width);
171 mUseAnimatedSize &= ~AnimatedSizeFlag::WIDTH;
175 void ActorSizer::SetHeight(float height)
177 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
179 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
180 mRelayoutData->preferredSize.height = height;
184 mTargetSize.height = height;
186 // node is being used in a separate thread; queue a message to set the value & base value
187 auto& node = mOwner.GetNode();
188 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeY, height);
191 mUseAnimatedSize &= ~AnimatedSizeFlag::HEIGHT;
195 void ActorSizer::SetDepth(float depth)
197 mTargetSize.depth = depth;
199 mUseAnimatedSize &= ~AnimatedSizeFlag::DEPTH;
201 // node is being used in a separate thread; queue a message to set the value & base value
202 auto& node = mOwner.GetNode();
203 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeZ, depth);
206 Vector3 ActorSizer::GetTargetSize() const
208 Vector3 size = mTargetSize;
210 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH)
212 // Should return animated size if size is animated
213 size.width = mAnimatedSize.width;
217 // Should return preferred size if size is fixed as set by SetSize
218 if(GetResizePolicy(Dimension::WIDTH) == ResizePolicy::FIXED)
220 size.width = GetPreferredSize().width;
224 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT)
226 size.height = mAnimatedSize.height;
230 if(GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::FIXED)
232 size.height = GetPreferredSize().height;
236 if(mUseAnimatedSize & AnimatedSizeFlag::DEPTH)
238 size.depth = mAnimatedSize.depth;
244 void ActorSizer::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
246 EnsureRelayouter().SetResizePolicy(policy, dimension, mTargetSize, mTargetSizeDirtyFlag);
247 mOwner.OnSetResizePolicy(policy, dimension);
251 ResizePolicy::Type ActorSizer::GetResizePolicy(Dimension::Type dimension) const
253 return mRelayoutData ? mRelayoutData->GetResizePolicy(dimension) : ResizePolicy::DEFAULT;
256 void ActorSizer::SetSizeScalePolicy(SizeScalePolicy::Type policy)
259 mRelayoutData->sizeSetPolicy = policy;
263 SizeScalePolicy::Type ActorSizer::GetSizeScalePolicy() const
265 return mRelayoutData ? mRelayoutData->sizeSetPolicy : Relayouter::DEFAULT_SIZE_SCALE_POLICY;
268 Dimension::Type ActorSizer::GetDimensionDependency(Dimension::Type dimension) const
270 return mRelayoutData ? mRelayoutData->GetDimensionDependency(dimension) : Dimension::ALL_DIMENSIONS;
273 void ActorSizer::SetRelayoutEnabled(bool relayoutEnabled)
275 // If relayout data has not been allocated yet and the client is requesting
276 // to disable it, do nothing
277 if(mRelayoutData || relayoutEnabled)
281 DALI_ASSERT_DEBUG(mRelayoutData && "mRelayoutData not created");
283 mRelayoutData->relayoutEnabled = relayoutEnabled;
287 bool ActorSizer::IsRelayoutEnabled() const
289 // Assume that if relayout data has not been allocated yet then relayout is disabled
290 return mRelayoutData && mRelayoutData->relayoutEnabled;
293 void ActorSizer::SetLayoutDirty(bool dirty, Dimension::Type dimension)
295 EnsureRelayouter().SetLayoutDirty(dirty, dimension);
298 bool ActorSizer::IsLayoutDirty(Dimension::Type dimension) const
300 return mRelayoutData && mRelayoutData->IsLayoutDirty(dimension);
303 bool ActorSizer::RelayoutPossible(Dimension::Type dimension) const
305 return mRelayoutData && mRelayoutData->relayoutEnabled && !IsLayoutDirty(dimension);
308 bool ActorSizer::RelayoutRequired(Dimension::Type dimension) const
310 return mRelayoutData && mRelayoutData->relayoutEnabled && IsLayoutDirty(dimension);
313 ActorSizer::Relayouter& ActorSizer::EnsureRelayouter()
318 mRelayoutData = new Relayouter();
321 return *mRelayoutData;
324 bool ActorSizer::RelayoutDependentOnParent(Dimension::Type dimension)
326 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE. This
327 // will keep the existing behaviour, and return false.
329 // Check if actor is dependent on parent
330 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
332 if((dimension & (1 << i)))
334 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
335 if(resizePolicy == ResizePolicy::FILL_TO_PARENT || resizePolicy == ResizePolicy::SIZE_RELATIVE_TO_PARENT || resizePolicy == ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT)
345 bool ActorSizer::RelayoutDependentOnChildrenBase(Dimension::Type dimension)
347 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE.
348 // This means this will return true when there is no relayouting, but that seems
349 // counter-intuitive. Will keep current behaviour for now, as it is consistent.
351 // Check if actor is dependent on children
352 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
354 if((dimension & (1 << i)))
356 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
359 case ResizePolicy::FIT_TO_CHILDREN:
360 case ResizePolicy::USE_NATURAL_SIZE: // i.e. For things that calculate their size based on children
376 bool ActorSizer::RelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependentDimension)
378 return mRelayoutData && mRelayoutData->GetRelayoutDependentOnDimension(dimension, dependentDimension);
381 void ActorSizer::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
385 mRelayoutData->SetNegotiatedDimension(negotiatedDimension, dimension);
389 float ActorSizer::GetNegotiatedDimension(Dimension::Type dimension) const
391 return mRelayoutData ? mRelayoutData->GetNegotiatedDimension(dimension) : 0.0f;
394 void ActorSizer::SetPadding(const Vector2& padding, Dimension::Type dimension)
396 EnsureRelayouter().SetPadding(padding, dimension);
399 Vector2 ActorSizer::GetPadding(Dimension::Type dimension) const
401 return mRelayoutData ? mRelayoutData->GetPadding(dimension) : Relayouter::DEFAULT_DIMENSION_PADDING;
404 void ActorSizer::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
406 EnsureRelayouter().SetLayoutNegotiated(negotiated, dimension);
409 bool ActorSizer::IsLayoutNegotiated(Dimension::Type dimension) const
411 return mRelayoutData && mRelayoutData->IsLayoutNegotiated(dimension);
414 float ActorSizer::GetHeightForWidthBase(float width)
416 const Vector3 naturalSize = mOwner.GetNaturalSize();
417 return naturalSize.width > 0.0f ? naturalSize.height * width / naturalSize.width : width;
420 float ActorSizer::GetWidthForHeightBase(float height)
422 const Vector3 naturalSize = mOwner.GetNaturalSize();
423 return naturalSize.height > 0.0f ? naturalSize.width * height / naturalSize.height : height;
426 float ActorSizer::CalculateChildSizeBase(const Dali::Actor& child, Dimension::Type dimension)
428 // Fill to parent, taking size mode factor into account
429 switch(child.GetResizePolicy(dimension))
431 case ResizePolicy::FILL_TO_PARENT:
433 return GetLatestSize(dimension);
436 case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
438 Property::Value value = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
439 Vector3 childSizeModeFactor = value.Get<Vector3>();
440 return GetLatestSize(dimension) * GetDimensionValue(childSizeModeFactor, dimension);
443 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
445 Property::Value value = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
446 Vector3 childSizeModeFactor = value.Get<Vector3>();
447 return GetLatestSize(dimension) + GetDimensionValue(childSizeModeFactor, dimension);
452 return GetLatestSize(dimension);
457 float ActorSizer::GetLatestSize(Dimension::Type dimension) const
459 return IsLayoutNegotiated(dimension) ? GetNegotiatedDimension(dimension) : GetSize(dimension);
462 float ActorSizer::GetRelayoutSize(Dimension::Type dimension) const
464 Vector2 padding = GetPadding(dimension);
466 return GetLatestSize(dimension) + padding.x + padding.y;
469 float ActorSizer::NegotiateFromParent(Dimension::Type dimension)
471 Actor* parent = mOwner.GetParent();
474 Vector2 padding(GetPadding(dimension));
475 Vector2 parentPadding(parent->mSizer.GetPadding(dimension));
477 // Need to use actor API here to allow deriving actors to layout their children
478 return parent->CalculateChildSize(Dali::Actor(&mOwner), dimension) - parentPadding.x - parentPadding.y - padding.x - padding.y;
484 float ActorSizer::NegotiateFromChildren(Dimension::Type dimension)
486 float maxDimensionPoint = 0.0f;
488 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
490 ActorPtr child = mOwner.GetChildAt(i);
492 if(!child->RelayoutDependentOnParent(dimension))
494 // Calculate the min and max points that the children range across
495 float childPosition = GetDimensionValue(child->GetTargetPosition(), dimension);
496 float dimensionSize = child->mSizer.GetRelayoutSize(dimension);
497 maxDimensionPoint = std::max(maxDimensionPoint, childPosition + dimensionSize);
501 return maxDimensionPoint;
504 float ActorSizer::GetSize(Dimension::Type dimension) const
506 return GetDimensionValue(mTargetSize, dimension);
509 float ActorSizer::GetNaturalSize(Dimension::Type dimension) const
511 return GetDimensionValue(mOwner.GetNaturalSize(), dimension);
514 Vector2 ActorSizer::ApplySizeSetPolicy(const Vector2& size)
516 return mRelayoutData->ApplySizeSetPolicy(mOwner, size);
519 void ActorSizer::SetNegotiatedSize(RelayoutContainer& container)
521 // Do the set actor size
522 Vector2 negotiatedSize(GetLatestSize(Dimension::WIDTH), GetLatestSize(Dimension::HEIGHT));
524 // Adjust for size set policy
525 negotiatedSize = ApplySizeSetPolicy(negotiatedSize);
527 // Lock the flag to stop recursive relayouts on set size
528 mRelayoutData->insideRelayout = true;
529 SetSize(Vector3(negotiatedSize.width, negotiatedSize.height, 0.0f));
530 mRelayoutData->insideRelayout = false;
532 // Clear flags for all dimensions
533 SetLayoutDirty(false);
535 // Give deriving classes a chance to respond
536 mOwner.OnRelayout(negotiatedSize, container);
538 if(!mOwner.mOnRelayoutSignal.Empty())
540 Dali::Actor handle(&mOwner);
541 mOwner.mOnRelayoutSignal.Emit(handle);
544 mRelayoutData->relayoutRequested = false;
547 void ActorSizer::NegotiateSize(const Vector2& allocatedSize, RelayoutContainer& container)
549 // Force a size negotiation for actors that has assigned size during relayout
550 // This is required as otherwise the flags that force a relayout will not
551 // necessarilly be set. This will occur if the actor has already been laid out.
552 // The dirty flags are then cleared. Then if the actor is added back into the
553 // relayout container afterwards, the dirty flags would still be clear...
554 // causing a relayout to be skipped. Here we force any actors added to the
555 // container to be relayed out.
556 DALI_LOG_TIMER_START(NegSizeTimer1);
558 if(GetUseAssignedSize(Dimension::WIDTH))
560 SetLayoutNegotiated(false, Dimension::WIDTH);
562 if(GetUseAssignedSize(Dimension::HEIGHT))
564 SetLayoutNegotiated(false, Dimension::HEIGHT);
567 // Do the negotiation
568 NegotiateDimensions(allocatedSize);
570 // Set the actor size
571 SetNegotiatedSize(container);
573 // Negotiate down to children
574 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
576 ActorPtr child = mOwner.GetChildAt(i);
577 ActorSizer& childSizer = child->mSizer;
579 // Forces children that have already been laid out to be relayed out
580 // if they have assigned size during relayout.
581 if(childSizer.GetUseAssignedSize(Dimension::WIDTH))
583 childSizer.SetLayoutNegotiated(false, Dimension::WIDTH);
584 childSizer.SetLayoutDirty(true, Dimension::WIDTH);
587 if(childSizer.GetUseAssignedSize(Dimension::HEIGHT))
589 childSizer.SetLayoutNegotiated(false, Dimension::HEIGHT);
590 childSizer.SetLayoutDirty(true, Dimension::HEIGHT);
593 // Only relayout if required
594 if(childSizer.RelayoutRequired())
596 container.Add(Dali::Actor(child.Get()), mTargetSize.GetVectorXY());
600 // Reset the flag so that size negotiation will respect the actor's original resize policy
601 SetUseAssignedSize(false);
602 DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
605 void ActorSizer::SetUseAssignedSize(bool use, Dimension::Type dimension)
609 mRelayoutData->SetUseAssignedSize(use, dimension);
613 bool ActorSizer::GetUseAssignedSize(Dimension::Type dimension) const
615 return mRelayoutData && mRelayoutData->GetUseAssignedSize(dimension);
618 void ActorSizer::RelayoutRequest(Dimension::Type dimension)
620 Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
621 if(relayoutController)
623 Dali::Actor owner(&mOwner);
624 relayoutController->RequestRelayout(owner, dimension);
628 mRelayoutData->relayoutRequested = true;
633 void ActorSizer::SetPreferredSize(const Vector2& size)
637 // If valid width or height, then set the resize policy to FIXED
638 // 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,
639 // then change to FIXED as well
641 if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
643 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
646 if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
648 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
651 mRelayoutData->preferredSize = size;
652 mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
656 Vector2 ActorSizer::GetPreferredSize() const
658 return mRelayoutData ? Vector2(mRelayoutData->preferredSize) : Relayouter::DEFAULT_PREFERRED_SIZE;
661 void ActorSizer::SetMinimumSize(float size, Dimension::Type dimension)
663 EnsureRelayouter().SetMinimumSize(size, dimension);
667 float ActorSizer::GetMinimumSize(Dimension::Type dimension) const
669 return mRelayoutData ? mRelayoutData->GetMinimumSize(dimension) : 0.0f;
672 void ActorSizer::SetMaximumSize(float size, Dimension::Type dimension)
674 EnsureRelayouter().SetMaximumSize(size, dimension);
678 float ActorSizer::GetMaximumSize(Dimension::Type dimension) const
680 return mRelayoutData ? mRelayoutData->GetMaximumSize(dimension) : FLT_MAX;
683 void ActorSizer::OnAnimateSize(Animation& animation, Vector3 targetSize, bool relative)
685 mTargetSize = targetSize + mTargetSize * float(relative);
686 mAnimatedSize = mTargetSize;
687 mUseAnimatedSize = AnimatedSizeFlag::WIDTH | AnimatedSizeFlag::HEIGHT | AnimatedSizeFlag::DEPTH;
689 if(mRelayoutData && !mRelayoutData->relayoutRequested)
691 mRelayoutData->preferredSize.width = mAnimatedSize.width;
692 mRelayoutData->preferredSize.height = mAnimatedSize.height;
695 // Notify deriving classes
696 mOwner.OnSizeAnimation(animation, mTargetSize);
699 void ActorSizer::OnAnimateWidth(Animation& animation, float width, bool relative)
701 mTargetSize.width = width + float(relative) * mTargetSize.width;
702 mAnimatedSize.width = mTargetSize.width;
703 mUseAnimatedSize |= AnimatedSizeFlag::WIDTH;
705 if(mRelayoutData && !mRelayoutData->relayoutRequested)
707 mRelayoutData->preferredSize.width = mAnimatedSize.width;
710 // Notify deriving classes
711 mOwner.OnSizeAnimation(animation, mTargetSize);
714 void ActorSizer::OnAnimateHeight(Animation& animation, float height, bool relative)
716 mTargetSize.height = height + float(relative) * mTargetSize.height;
717 mAnimatedSize.height = mTargetSize.height;
718 mUseAnimatedSize |= AnimatedSizeFlag::HEIGHT;
720 if(mRelayoutData && !mRelayoutData->relayoutRequested)
722 mRelayoutData->preferredSize.height = mAnimatedSize.height;
725 // Notify deriving classes
726 mOwner.OnSizeAnimation(animation, mTargetSize);
729 void ActorSizer::OnAnimateDepth(Animation& animation, float depth, bool relative)
731 mTargetSize.depth = depth + float(relative) * mTargetSize.depth;
732 mAnimatedSize.depth = mTargetSize.depth;
733 mUseAnimatedSize |= AnimatedSizeFlag::DEPTH;
735 // Notify deriving classes
736 mOwner.OnSizeAnimation(animation, mTargetSize);
740 * @brief Extract a given dimension from a Vector3
742 * @param[in] values The values to extract from
743 * @param[in] dimension The dimension to extract
744 * @return Return the value for the dimension
746 float ActorSizer::GetDimensionValue(const Vector3& values, const Dimension::Type dimension) const
748 return ::GetDimensionValue(values.GetVectorXY(), dimension);
751 float ActorSizer::ClampDimension(float size, Dimension::Type dimension) const
753 const float minSize = GetMinimumSize(dimension);
754 const float maxSize = GetMaximumSize(dimension);
756 return std::max(minSize, std::min(size, maxSize));
759 void ActorSizer::NegotiateDimension(Dimension::Type dimension, const Vector2& allocatedSize, ActorDimensionStack& recursionStack)
761 // Check if it needs to be negotiated
762 if(IsLayoutDirty(dimension) && !IsLayoutNegotiated(dimension))
764 // Check that we havn't gotten into an infinite loop
765 ActorDimensionPair searchActor = ActorDimensionPair(&mOwner, dimension);
766 bool recursionFound = false;
767 for(auto& element : recursionStack)
769 if(element == searchActor)
771 recursionFound = true;
778 // Record the path that we have taken
779 recursionStack.emplace_back(&mOwner, dimension);
781 // Dimension dependency check
782 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
784 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
786 if(RelayoutDependentOnDimension(dimension, dimensionToCheck))
788 NegotiateDimension(dimensionToCheck, allocatedSize, recursionStack);
792 // Parent dependency check
793 Actor* parent = mOwner.GetParent();
794 if(parent && RelayoutDependentOnParent(dimension))
796 parent->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
799 // Children dependency check
800 if(mOwner.RelayoutDependentOnChildren(dimension))
802 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
804 ActorPtr child = mOwner.GetChildAt(i);
806 // Only relayout child first if it is not dependent on this actor
807 if(!child->RelayoutDependentOnParent(dimension))
809 child->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
814 // For deriving classes
815 mOwner.OnCalculateRelayoutSize(dimension);
817 // All dependencies checked, calculate the size and set negotiated flag
818 const float newSize = ClampDimension(CalculateSize(dimension, allocatedSize), dimension);
820 SetNegotiatedDimension(newSize, dimension);
821 SetLayoutNegotiated(true, dimension);
823 // For deriving classes
824 mOwner.OnLayoutNegotiated(newSize, dimension);
826 // This actor has been successfully processed, pop it off the recursion stack
827 recursionStack.pop_back();
831 // TODO: Break infinite loop
832 SetLayoutNegotiated(true, dimension);
837 void ActorSizer::NegotiateDimensions(const Vector2& allocatedSize)
839 // Negotiate all dimensions that require it
840 gRecursionStack.clear();
842 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
844 const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
847 NegotiateDimension(dimension, allocatedSize, gRecursionStack);
851 float ActorSizer::CalculateSize(Dimension::Type dimension, const Vector2& maximumSize)
853 switch(GetResizePolicy(dimension))
855 case ResizePolicy::USE_NATURAL_SIZE:
857 return GetNaturalSize(dimension);
860 case ResizePolicy::FIXED:
862 return ::GetDimensionValue(GetPreferredSize(), dimension);
865 case ResizePolicy::USE_ASSIGNED_SIZE:
867 return ::GetDimensionValue(maximumSize, dimension);
870 case ResizePolicy::FILL_TO_PARENT:
871 case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
872 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
874 return NegotiateFromParent(dimension);
877 case ResizePolicy::FIT_TO_CHILDREN:
879 return NegotiateFromChildren(dimension);
882 case ResizePolicy::DIMENSION_DEPENDENCY:
884 const Dimension::Type dimensionDependency = GetDimensionDependency(dimension);
887 if(dimension == Dimension::WIDTH && dimensionDependency == Dimension::HEIGHT)
889 // Use actor API to allow deriving actors to layout their content
890 return mOwner.GetWidthForHeight(GetNegotiatedDimension(Dimension::HEIGHT));
893 if(dimension == Dimension::HEIGHT && dimensionDependency == Dimension::WIDTH)
895 // Use actor API to allow deriving actors to layout their content
896 return mOwner.GetHeightForWidth(GetNegotiatedDimension(Dimension::WIDTH));
908 return 0.0f; // Default
911 } // namespace Dali::Internal