2 * Copyright (c) 2023 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 Dali::Internal::ActorSizer::ActorDimensionStack& GetRecursionStack()
71 static Dali::Internal::ActorSizer::ActorDimensionStack gRecursionStack{};
72 return gRecursionStack;
76 namespace Dali::Internal
78 ActorSizer::ActorSizer(Internal::Actor& owner)
80 mRelayoutData(nullptr),
81 mTargetSize(Vector3::ZERO),
82 mAnimatedSize(Vector3::ZERO),
83 mUseAnimatedSize(AnimatedSizeFlag::CLEAR),
84 mTargetSizeDirtyFlag(false),
85 mInsideOnSizeSet(false)
89 ActorSizer::~ActorSizer()
91 // Delete optional relayout data
95 void ActorSizer::SetSizeModeFactor(const Vector3& factor)
99 mRelayoutData->sizeModeFactor = factor;
101 const Vector3& ActorSizer::GetSizeModeFactor() const
103 return mRelayoutData ? mRelayoutData->sizeModeFactor : Relayouter::DEFAULT_SIZE_MODE_FACTOR;
106 void ActorSizer::SetSize(const Vector3& size)
108 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
110 // TODO we cannot just ignore the given Z but that means rewrite the size negotiation!!
111 SetPreferredSize(size.GetVectorXY());
115 SetSizeInternal(size);
119 void ActorSizer::SetSizeInternal(const Vector3& size)
121 // dont allow recursive loop
122 DALI_ASSERT_ALWAYS(!mInsideOnSizeSet && "Cannot call SetSize from OnSizeSet");
123 // 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
124 if(mTargetSize != size || mTargetSizeDirtyFlag)
126 mTargetSizeDirtyFlag = false;
129 // Update the preferred size after relayoutting
130 // It should be used in the next relayoutting
131 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH && mRelayoutData)
133 mRelayoutData->preferredSize.width = mAnimatedSize.width;
136 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT && mRelayoutData)
138 mRelayoutData->preferredSize.height = mAnimatedSize.height;
141 // node is being used in a separate thread; queue a message to set the value & base value
142 auto& node = mOwner.GetNode();
143 SceneGraph::NodeTransformPropertyMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::Bake, mTargetSize);
145 // Notification for derived classes
146 mInsideOnSizeSet = true;
147 mOwner.OnSizeSet(mTargetSize);
148 mInsideOnSizeSet = false;
150 // Raise a relayout request if the flag is not locked
151 if(mRelayoutData && !mRelayoutData->insideRelayout)
158 void ActorSizer::SetWidth(float width)
160 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
162 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
163 mRelayoutData->preferredSize.width = width;
167 mTargetSize.width = width;
169 // node is being used in a separate thread; queue a message to set the value & base value
170 auto& node = mOwner.GetNode();
171 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeX, width);
174 mUseAnimatedSize &= ~AnimatedSizeFlag::WIDTH;
178 void ActorSizer::SetHeight(float height)
180 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
182 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
183 mRelayoutData->preferredSize.height = height;
187 mTargetSize.height = height;
189 // node is being used in a separate thread; queue a message to set the value & base value
190 auto& node = mOwner.GetNode();
191 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeY, height);
194 mUseAnimatedSize &= ~AnimatedSizeFlag::HEIGHT;
198 void ActorSizer::SetDepth(float depth)
200 mTargetSize.depth = depth;
202 mUseAnimatedSize &= ~AnimatedSizeFlag::DEPTH;
204 // node is being used in a separate thread; queue a message to set the value & base value
205 auto& node = mOwner.GetNode();
206 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeZ, depth);
209 Vector3 ActorSizer::GetTargetSize() const
211 Vector3 size = mTargetSize;
213 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH)
215 // Should return animated size if size is animated
216 size.width = mAnimatedSize.width;
220 // Should return preferred size if size is fixed as set by SetSize
221 if(GetResizePolicy(Dimension::WIDTH) == ResizePolicy::FIXED)
223 size.width = GetPreferredSize().width;
227 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT)
229 size.height = mAnimatedSize.height;
233 if(GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::FIXED)
235 size.height = GetPreferredSize().height;
239 if(mUseAnimatedSize & AnimatedSizeFlag::DEPTH)
241 size.depth = mAnimatedSize.depth;
247 void ActorSizer::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
249 EnsureRelayouter().SetResizePolicy(policy, dimension, mTargetSize, mTargetSizeDirtyFlag);
250 mOwner.OnSetResizePolicy(policy, dimension);
254 ResizePolicy::Type ActorSizer::GetResizePolicy(Dimension::Type dimension) const
256 return mRelayoutData ? mRelayoutData->GetResizePolicy(dimension) : ResizePolicy::DEFAULT;
259 void ActorSizer::SetSizeScalePolicy(SizeScalePolicy::Type policy)
262 mRelayoutData->sizeSetPolicy = policy;
266 SizeScalePolicy::Type ActorSizer::GetSizeScalePolicy() const
268 return mRelayoutData ? mRelayoutData->sizeSetPolicy : Relayouter::DEFAULT_SIZE_SCALE_POLICY;
271 Dimension::Type ActorSizer::GetDimensionDependency(Dimension::Type dimension) const
273 return mRelayoutData ? mRelayoutData->GetDimensionDependency(dimension) : Dimension::ALL_DIMENSIONS;
276 void ActorSizer::SetRelayoutEnabled(bool relayoutEnabled)
278 // If relayout data has not been allocated yet and the client is requesting
279 // to disable it, do nothing
280 if(mRelayoutData || relayoutEnabled)
284 DALI_ASSERT_DEBUG(mRelayoutData && "mRelayoutData not created");
286 mRelayoutData->relayoutEnabled = relayoutEnabled;
290 bool ActorSizer::IsRelayoutEnabled() const
292 // Assume that if relayout data has not been allocated yet then relayout is disabled
293 return mRelayoutData && mRelayoutData->relayoutEnabled;
296 void ActorSizer::SetLayoutDirty(bool dirty, Dimension::Type dimension)
298 EnsureRelayouter().SetLayoutDirty(dirty, dimension);
301 bool ActorSizer::IsLayoutDirty(Dimension::Type dimension) const
303 return mRelayoutData && mRelayoutData->IsLayoutDirty(dimension);
306 bool ActorSizer::RelayoutPossible(Dimension::Type dimension) const
308 return mRelayoutData && mRelayoutData->relayoutEnabled && !IsLayoutDirty(dimension);
311 bool ActorSizer::RelayoutRequired(Dimension::Type dimension) const
313 return mRelayoutData && mRelayoutData->relayoutEnabled && IsLayoutDirty(dimension);
316 ActorSizer::Relayouter& ActorSizer::EnsureRelayouter()
321 mRelayoutData = new Relayouter();
324 return *mRelayoutData;
327 bool ActorSizer::RelayoutDependentOnParent(Dimension::Type dimension)
329 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE. This
330 // will keep the existing behaviour, and return false.
332 // Check if actor is dependent on parent
333 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
335 if((dimension & (1 << i)))
337 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
338 if(resizePolicy == ResizePolicy::FILL_TO_PARENT || resizePolicy == ResizePolicy::SIZE_RELATIVE_TO_PARENT || resizePolicy == ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT)
348 bool ActorSizer::RelayoutDependentOnChildrenBase(Dimension::Type dimension)
350 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE.
351 // This means this will return true when there is no relayouting, but that seems
352 // counter-intuitive. Will keep current behaviour for now, as it is consistent.
354 // Check if actor is dependent on children
355 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
357 if((dimension & (1 << i)))
359 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
362 case ResizePolicy::FIT_TO_CHILDREN:
363 case ResizePolicy::USE_NATURAL_SIZE: // i.e. For things that calculate their size based on children
379 bool ActorSizer::RelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependentDimension)
381 return mRelayoutData && mRelayoutData->GetRelayoutDependentOnDimension(dimension, dependentDimension);
384 void ActorSizer::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
388 mRelayoutData->SetNegotiatedDimension(negotiatedDimension, dimension);
392 float ActorSizer::GetNegotiatedDimension(Dimension::Type dimension) const
394 return mRelayoutData ? mRelayoutData->GetNegotiatedDimension(dimension) : 0.0f;
397 void ActorSizer::SetPadding(const Vector2& padding, Dimension::Type dimension)
399 EnsureRelayouter().SetPadding(padding, dimension);
402 Vector2 ActorSizer::GetPadding(Dimension::Type dimension) const
404 return mRelayoutData ? mRelayoutData->GetPadding(dimension) : Relayouter::DEFAULT_DIMENSION_PADDING;
407 void ActorSizer::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
409 EnsureRelayouter().SetLayoutNegotiated(negotiated, dimension);
412 bool ActorSizer::IsLayoutNegotiated(Dimension::Type dimension) const
414 return mRelayoutData && mRelayoutData->IsLayoutNegotiated(dimension);
417 float ActorSizer::GetHeightForWidthBase(float width)
419 const Vector3 naturalSize = mOwner.GetNaturalSize();
420 return naturalSize.width > 0.0f ? naturalSize.height * width / naturalSize.width : width;
423 float ActorSizer::GetWidthForHeightBase(float height)
425 const Vector3 naturalSize = mOwner.GetNaturalSize();
426 return naturalSize.height > 0.0f ? naturalSize.width * height / naturalSize.height : height;
429 float ActorSizer::CalculateChildSizeBase(const Dali::Actor& child, Dimension::Type dimension)
431 // Fill to parent, taking size mode factor into account
432 switch(child.GetResizePolicy(dimension))
434 case ResizePolicy::FILL_TO_PARENT:
436 return GetLatestSize(dimension);
439 case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
441 Property::Value value = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
442 Vector3 childSizeModeFactor = value.Get<Vector3>();
443 return GetLatestSize(dimension) * GetDimensionValue(childSizeModeFactor, dimension);
446 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
448 Property::Value value = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
449 Vector3 childSizeModeFactor = value.Get<Vector3>();
450 return GetLatestSize(dimension) + GetDimensionValue(childSizeModeFactor, dimension);
455 return GetLatestSize(dimension);
460 float ActorSizer::GetLatestSize(Dimension::Type dimension) const
462 return IsLayoutNegotiated(dimension) ? GetNegotiatedDimension(dimension) : GetSize(dimension);
465 float ActorSizer::GetRelayoutSize(Dimension::Type dimension) const
467 Vector2 padding = GetPadding(dimension);
469 return GetLatestSize(dimension) + padding.x + padding.y;
472 float ActorSizer::NegotiateFromParent(Dimension::Type dimension)
474 Actor* parent = mOwner.GetParent();
477 Vector2 padding(GetPadding(dimension));
478 Vector2 parentPadding(parent->mSizer.GetPadding(dimension));
480 // Need to use actor API here to allow deriving actors to layout their children
481 return parent->CalculateChildSize(Dali::Actor(&mOwner), dimension) - parentPadding.x - parentPadding.y - padding.x - padding.y;
487 float ActorSizer::NegotiateFromChildren(Dimension::Type dimension)
489 float maxDimensionPoint = 0.0f;
491 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
493 ActorPtr child = mOwner.GetChildAt(i);
495 if(!child->RelayoutDependentOnParent(dimension))
497 // Calculate the min and max points that the children range across
498 float childPosition = GetDimensionValue(child->GetTargetPosition(), dimension);
499 float dimensionSize = child->mSizer.GetRelayoutSize(dimension);
500 maxDimensionPoint = std::max(maxDimensionPoint, childPosition + dimensionSize);
504 return maxDimensionPoint;
507 float ActorSizer::GetSize(Dimension::Type dimension) const
509 return GetDimensionValue(mTargetSize, dimension);
512 float ActorSizer::GetNaturalSize(Dimension::Type dimension) const
514 return GetDimensionValue(mOwner.GetNaturalSize(), dimension);
517 Vector2 ActorSizer::ApplySizeSetPolicy(const Vector2& size)
519 return mRelayoutData->ApplySizeSetPolicy(mOwner, size);
522 void ActorSizer::SetNegotiatedSize(RelayoutContainer& container)
524 // Do the set actor size
525 Vector2 negotiatedSize(GetLatestSize(Dimension::WIDTH), GetLatestSize(Dimension::HEIGHT));
527 // Adjust for size set policy
528 negotiatedSize = ApplySizeSetPolicy(negotiatedSize);
530 // Lock the flag to stop recursive relayouts on set size
531 mRelayoutData->insideRelayout = true;
532 SetSize(Vector3(negotiatedSize.width, negotiatedSize.height, 0.0f));
533 mRelayoutData->insideRelayout = false;
535 // Clear flags for all dimensions
536 SetLayoutDirty(false);
538 // Give deriving classes a chance to respond
539 mOwner.OnRelayout(negotiatedSize, container);
541 if(!mOwner.mOnRelayoutSignal.Empty())
543 Dali::Actor handle(&mOwner);
544 mOwner.mOnRelayoutSignal.Emit(handle);
547 mRelayoutData->relayoutRequested = false;
550 void ActorSizer::NegotiateSize(const Vector2& allocatedSize, RelayoutContainer& container)
552 // Force a size negotiation for actors that has assigned size during relayout
553 // This is required as otherwise the flags that force a relayout will not
554 // necessarilly be set. This will occur if the actor has already been laid out.
555 // The dirty flags are then cleared. Then if the actor is added back into the
556 // relayout container afterwards, the dirty flags would still be clear...
557 // causing a relayout to be skipped. Here we force any actors added to the
558 // container to be relayed out.
559 DALI_LOG_TIMER_START(NegSizeTimer1);
561 if(GetUseAssignedSize(Dimension::WIDTH))
563 SetLayoutNegotiated(false, Dimension::WIDTH);
565 if(GetUseAssignedSize(Dimension::HEIGHT))
567 SetLayoutNegotiated(false, Dimension::HEIGHT);
570 // Do the negotiation
571 NegotiateDimensions(allocatedSize);
573 // Set the actor size
574 SetNegotiatedSize(container);
576 // Negotiate down to children
577 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
579 ActorPtr child = mOwner.GetChildAt(i);
580 ActorSizer& childSizer = child->mSizer;
582 // Forces children that have already been laid out to be relayed out
583 // if they have assigned size during relayout.
584 if(childSizer.GetUseAssignedSize(Dimension::WIDTH))
586 childSizer.SetLayoutNegotiated(false, Dimension::WIDTH);
587 childSizer.SetLayoutDirty(true, Dimension::WIDTH);
590 if(childSizer.GetUseAssignedSize(Dimension::HEIGHT))
592 childSizer.SetLayoutNegotiated(false, Dimension::HEIGHT);
593 childSizer.SetLayoutDirty(true, Dimension::HEIGHT);
596 // Only relayout if required
597 if(childSizer.RelayoutRequired())
599 container.Add(Dali::Actor(child.Get()), mTargetSize.GetVectorXY());
603 // Reset the flag so that size negotiation will respect the actor's original resize policy
604 SetUseAssignedSize(false);
605 DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
608 void ActorSizer::SetUseAssignedSize(bool use, Dimension::Type dimension)
612 mRelayoutData->SetUseAssignedSize(use, dimension);
616 bool ActorSizer::GetUseAssignedSize(Dimension::Type dimension) const
618 return mRelayoutData && mRelayoutData->GetUseAssignedSize(dimension);
621 void ActorSizer::RelayoutRequest(Dimension::Type dimension)
623 Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
624 if(relayoutController)
626 Dali::Actor owner(&mOwner);
627 relayoutController->RequestRelayout(owner, dimension);
631 mRelayoutData->relayoutRequested = true;
636 void ActorSizer::SetPreferredSize(const Vector2& size)
640 // If valid width or height, then set the resize policy to FIXED
641 // 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,
642 // then change to FIXED as well
644 if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
646 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
649 if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
651 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
654 mRelayoutData->preferredSize = size;
655 mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
659 Vector2 ActorSizer::GetPreferredSize() const
661 return mRelayoutData ? Vector2(mRelayoutData->preferredSize) : Relayouter::DEFAULT_PREFERRED_SIZE;
664 void ActorSizer::SetMinimumSize(float size, Dimension::Type dimension)
666 EnsureRelayouter().SetMinimumSize(size, dimension);
670 float ActorSizer::GetMinimumSize(Dimension::Type dimension) const
672 return mRelayoutData ? mRelayoutData->GetMinimumSize(dimension) : 0.0f;
675 void ActorSizer::SetMaximumSize(float size, Dimension::Type dimension)
677 EnsureRelayouter().SetMaximumSize(size, dimension);
681 float ActorSizer::GetMaximumSize(Dimension::Type dimension) const
683 return mRelayoutData ? mRelayoutData->GetMaximumSize(dimension) : FLT_MAX;
686 void ActorSizer::OnAnimateSize(Animation& animation, Vector3 targetSize, bool relative)
688 mTargetSize = targetSize + mTargetSize * float(relative);
689 mAnimatedSize = mTargetSize;
690 mUseAnimatedSize = AnimatedSizeFlag::WIDTH | AnimatedSizeFlag::HEIGHT | AnimatedSizeFlag::DEPTH;
692 if(mRelayoutData && !mRelayoutData->relayoutRequested)
694 mRelayoutData->preferredSize.width = mAnimatedSize.width;
695 mRelayoutData->preferredSize.height = mAnimatedSize.height;
698 // Notify deriving classes
699 mOwner.OnSizeAnimation(animation, mTargetSize);
702 void ActorSizer::OnAnimateWidth(Animation& animation, float width, bool relative)
704 mTargetSize.width = width + float(relative) * mTargetSize.width;
705 mAnimatedSize.width = mTargetSize.width;
706 mUseAnimatedSize |= AnimatedSizeFlag::WIDTH;
708 if(mRelayoutData && !mRelayoutData->relayoutRequested)
710 mRelayoutData->preferredSize.width = mAnimatedSize.width;
713 // Notify deriving classes
714 mOwner.OnSizeAnimation(animation, mTargetSize);
717 void ActorSizer::OnAnimateHeight(Animation& animation, float height, bool relative)
719 mTargetSize.height = height + float(relative) * mTargetSize.height;
720 mAnimatedSize.height = mTargetSize.height;
721 mUseAnimatedSize |= AnimatedSizeFlag::HEIGHT;
723 if(mRelayoutData && !mRelayoutData->relayoutRequested)
725 mRelayoutData->preferredSize.height = mAnimatedSize.height;
728 // Notify deriving classes
729 mOwner.OnSizeAnimation(animation, mTargetSize);
732 void ActorSizer::OnAnimateDepth(Animation& animation, float depth, bool relative)
734 mTargetSize.depth = depth + float(relative) * mTargetSize.depth;
735 mAnimatedSize.depth = mTargetSize.depth;
736 mUseAnimatedSize |= AnimatedSizeFlag::DEPTH;
738 // Notify deriving classes
739 mOwner.OnSizeAnimation(animation, mTargetSize);
743 * @brief Extract a given dimension from a Vector3
745 * @param[in] values The values to extract from
746 * @param[in] dimension The dimension to extract
747 * @return Return the value for the dimension
749 float ActorSizer::GetDimensionValue(const Vector3& values, const Dimension::Type dimension) const
751 return ::GetDimensionValue(values.GetVectorXY(), dimension);
754 float ActorSizer::ClampDimension(float size, Dimension::Type dimension) const
756 const float minSize = GetMinimumSize(dimension);
757 const float maxSize = GetMaximumSize(dimension);
759 return std::max(minSize, std::min(size, maxSize));
762 void ActorSizer::NegotiateDimension(Dimension::Type dimension, const Vector2& allocatedSize, ActorDimensionStack& recursionStack)
764 // Check if it needs to be negotiated
765 if(IsLayoutDirty(dimension) && !IsLayoutNegotiated(dimension))
767 // Check that we havn't gotten into an infinite loop
768 ActorDimensionPair searchActor = ActorDimensionPair(&mOwner, dimension);
769 bool recursionFound = false;
770 for(auto& element : recursionStack)
772 if(element == searchActor)
774 recursionFound = true;
781 // Record the path that we have taken
782 recursionStack.emplace_back(&mOwner, dimension);
784 // Dimension dependency check
785 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
787 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
789 if(RelayoutDependentOnDimension(dimension, dimensionToCheck))
791 NegotiateDimension(dimensionToCheck, allocatedSize, recursionStack);
795 // Parent dependency check
796 Actor* parent = mOwner.GetParent();
797 if(parent && RelayoutDependentOnParent(dimension))
799 parent->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
802 // Children dependency check
803 if(mOwner.RelayoutDependentOnChildren(dimension))
805 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
807 ActorPtr child = mOwner.GetChildAt(i);
809 // Only relayout child first if it is not dependent on this actor
810 if(!child->RelayoutDependentOnParent(dimension))
812 child->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
817 // For deriving classes
818 mOwner.OnCalculateRelayoutSize(dimension);
820 // All dependencies checked, calculate the size and set negotiated flag
821 const float newSize = ClampDimension(CalculateSize(dimension, allocatedSize), dimension);
823 SetNegotiatedDimension(newSize, dimension);
824 SetLayoutNegotiated(true, dimension);
826 // For deriving classes
827 mOwner.OnLayoutNegotiated(newSize, dimension);
829 // This actor has been successfully processed, pop it off the recursion stack
830 recursionStack.pop_back();
834 // TODO: Break infinite loop
835 SetLayoutNegotiated(true, dimension);
840 void ActorSizer::NegotiateDimensions(const Vector2& allocatedSize)
842 // Negotiate all dimensions that require it
843 GetRecursionStack().clear();
845 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
847 const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
850 NegotiateDimension(dimension, allocatedSize, GetRecursionStack());
854 float ActorSizer::CalculateSize(Dimension::Type dimension, const Vector2& maximumSize)
856 switch(GetResizePolicy(dimension))
858 case ResizePolicy::USE_NATURAL_SIZE:
860 return GetNaturalSize(dimension);
863 case ResizePolicy::FIXED:
865 return ::GetDimensionValue(GetPreferredSize(), dimension);
868 case ResizePolicy::USE_ASSIGNED_SIZE:
870 return ::GetDimensionValue(maximumSize, dimension);
873 case ResizePolicy::FILL_TO_PARENT:
874 case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
875 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
877 return NegotiateFromParent(dimension);
880 case ResizePolicy::FIT_TO_CHILDREN:
882 return NegotiateFromChildren(dimension);
885 case ResizePolicy::DIMENSION_DEPENDENCY:
887 const Dimension::Type dimensionDependency = GetDimensionDependency(dimension);
890 if(dimension == Dimension::WIDTH && dimensionDependency == Dimension::HEIGHT)
892 // Use actor API to allow deriving actors to layout their content
893 return mOwner.GetWidthForHeight(GetNegotiatedDimension(Dimension::HEIGHT));
896 if(dimension == Dimension::HEIGHT && dimensionDependency == Dimension::WIDTH)
898 // Use actor API to allow deriving actors to layout their content
899 return mOwner.GetHeightForWidth(GetNegotiatedDimension(Dimension::WIDTH));
911 return 0.0f; // Default
914 } // namespace Dali::Internal