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 Vector3 currentSize = mOwner.GetCurrentSize();
122 if((fabsf(mTargetSize.width - size.width) > Math::MACHINE_EPSILON_1) ||
123 (fabsf(mTargetSize.height - size.height) > Math::MACHINE_EPSILON_1) ||
124 (fabsf(mTargetSize.depth - size.depth) > Math::MACHINE_EPSILON_1) ||
125 (fabsf(mTargetSize.width - currentSize.width) > Math::MACHINE_EPSILON_1) ||
126 (fabsf(mTargetSize.height - currentSize.height) > Math::MACHINE_EPSILON_1) ||
127 (fabsf(mTargetSize.depth - currentSize.depth) > Math::MACHINE_EPSILON_1))
131 // Update the preferred size after relayoutting
132 // It should be used in the next relayoutting
133 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH && mRelayoutData)
135 mRelayoutData->preferredSize.width = mAnimatedSize.width;
138 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT && mRelayoutData)
140 mRelayoutData->preferredSize.height = mAnimatedSize.height;
143 // node is being used in a separate thread; queue a message to set the value & base value
144 auto& node = mOwner.GetNode();
145 SceneGraph::NodeTransformPropertyMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::Bake, mTargetSize);
147 // Notification for derived classes
148 mInsideOnSizeSet = true;
149 mOwner.OnSizeSet(mTargetSize);
150 mInsideOnSizeSet = false;
152 // Raise a relayout request if the flag is not locked
153 if(mRelayoutData && !mRelayoutData->insideRelayout)
160 void ActorSizer::SetWidth(float width)
162 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
164 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
165 mRelayoutData->preferredSize.width = width;
169 mTargetSize.width = width;
171 // node is being used in a separate thread; queue a message to set the value & base value
172 auto& node = mOwner.GetNode();
173 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeX, width);
176 mUseAnimatedSize &= ~AnimatedSizeFlag::WIDTH;
180 void ActorSizer::SetHeight(float height)
182 if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
184 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
185 mRelayoutData->preferredSize.height = height;
189 mTargetSize.height = height;
191 // node is being used in a separate thread; queue a message to set the value & base value
192 auto& node = mOwner.GetNode();
193 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeY, height);
196 mUseAnimatedSize &= ~AnimatedSizeFlag::HEIGHT;
200 void ActorSizer::SetDepth(float depth)
202 mTargetSize.depth = depth;
204 mUseAnimatedSize &= ~AnimatedSizeFlag::DEPTH;
206 // node is being used in a separate thread; queue a message to set the value & base value
207 auto& node = mOwner.GetNode();
208 SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeZ, depth);
211 Vector3 ActorSizer::GetTargetSize() const
213 Vector3 size = mTargetSize;
215 if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH)
217 // Should return animated size if size is animated
218 size.width = mAnimatedSize.width;
222 // Should return preferred size if size is fixed as set by SetSize
223 if(GetResizePolicy(Dimension::WIDTH) == ResizePolicy::FIXED)
225 size.width = GetPreferredSize().width;
229 if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT)
231 size.height = mAnimatedSize.height;
235 if(GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::FIXED)
237 size.height = GetPreferredSize().height;
241 if(mUseAnimatedSize & AnimatedSizeFlag::DEPTH)
243 size.depth = mAnimatedSize.depth;
249 void ActorSizer::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
251 EnsureRelayouter().SetResizePolicy(policy, dimension, mTargetSize);
252 mOwner.OnSetResizePolicy(policy, dimension);
256 ResizePolicy::Type ActorSizer::GetResizePolicy(Dimension::Type dimension) const
258 return mRelayoutData ? mRelayoutData->GetResizePolicy(dimension) : ResizePolicy::DEFAULT;
261 void ActorSizer::SetSizeScalePolicy(SizeScalePolicy::Type policy)
264 mRelayoutData->sizeSetPolicy = policy;
268 SizeScalePolicy::Type ActorSizer::GetSizeScalePolicy() const
270 return mRelayoutData ? mRelayoutData->sizeSetPolicy : Relayouter::DEFAULT_SIZE_SCALE_POLICY;
273 Dimension::Type ActorSizer::GetDimensionDependency(Dimension::Type dimension) const
275 return mRelayoutData ? mRelayoutData->GetDimensionDependency(dimension) : Dimension::ALL_DIMENSIONS;
278 void ActorSizer::SetRelayoutEnabled(bool relayoutEnabled)
280 // If relayout data has not been allocated yet and the client is requesting
281 // to disable it, do nothing
282 if(mRelayoutData || relayoutEnabled)
286 DALI_ASSERT_DEBUG(mRelayoutData && "mRelayoutData not created");
288 mRelayoutData->relayoutEnabled = relayoutEnabled;
292 bool ActorSizer::IsRelayoutEnabled() const
294 // Assume that if relayout data has not been allocated yet then relayout is disabled
295 return mRelayoutData && mRelayoutData->relayoutEnabled;
298 void ActorSizer::SetLayoutDirty(bool dirty, Dimension::Type dimension)
300 EnsureRelayouter().SetLayoutDirty(dirty, dimension);
303 bool ActorSizer::IsLayoutDirty(Dimension::Type dimension) const
305 return mRelayoutData && mRelayoutData->IsLayoutDirty(dimension);
308 bool ActorSizer::RelayoutPossible(Dimension::Type dimension) const
310 return mRelayoutData && mRelayoutData->relayoutEnabled && !IsLayoutDirty(dimension);
313 bool ActorSizer::RelayoutRequired(Dimension::Type dimension) const
315 return mRelayoutData && mRelayoutData->relayoutEnabled && IsLayoutDirty(dimension);
318 ActorSizer::Relayouter& ActorSizer::EnsureRelayouter()
323 mRelayoutData = new Relayouter();
326 return *mRelayoutData;
329 bool ActorSizer::RelayoutDependentOnParent(Dimension::Type dimension)
331 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE. This
332 // will keep the existing behaviour, and return false.
334 // Check if actor is dependent on parent
335 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
337 if((dimension & (1 << i)))
339 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
340 if(resizePolicy == ResizePolicy::FILL_TO_PARENT || resizePolicy == ResizePolicy::SIZE_RELATIVE_TO_PARENT || resizePolicy == ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT)
350 bool ActorSizer::RelayoutDependentOnChildrenBase(Dimension::Type dimension)
352 // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE.
353 // This means this will return true when there is no relayouting, but that seems
354 // counter-intuitive. Will keep current behaviour for now, as it is consistent.
356 // Check if actor is dependent on children
357 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
359 if((dimension & (1 << i)))
361 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
364 case ResizePolicy::FIT_TO_CHILDREN:
365 case ResizePolicy::USE_NATURAL_SIZE: // i.e. For things that calculate their size based on children
381 bool ActorSizer::RelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependentDimension)
383 return mRelayoutData && mRelayoutData->GetRelayoutDependentOnDimension(dimension, dependentDimension);
386 void ActorSizer::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
390 mRelayoutData->SetNegotiatedDimension(negotiatedDimension, dimension);
394 float ActorSizer::GetNegotiatedDimension(Dimension::Type dimension) const
396 return mRelayoutData ? mRelayoutData->GetNegotiatedDimension(dimension) : 0.0f;
399 void ActorSizer::SetPadding(const Vector2& padding, Dimension::Type dimension)
401 EnsureRelayouter().SetPadding(padding, dimension);
404 Vector2 ActorSizer::GetPadding(Dimension::Type dimension) const
406 return mRelayoutData ? mRelayoutData->GetPadding(dimension) : Relayouter::DEFAULT_DIMENSION_PADDING;
409 void ActorSizer::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
411 EnsureRelayouter().SetLayoutNegotiated(negotiated, dimension);
414 bool ActorSizer::IsLayoutNegotiated(Dimension::Type dimension) const
416 return mRelayoutData && mRelayoutData->IsLayoutNegotiated(dimension);
419 float ActorSizer::GetHeightForWidthBase(float width)
421 const Vector3 naturalSize = mOwner.GetNaturalSize();
422 return naturalSize.width > 0.0f ? naturalSize.height * width / naturalSize.width : width;
425 float ActorSizer::GetWidthForHeightBase(float height)
427 const Vector3 naturalSize = mOwner.GetNaturalSize();
428 return naturalSize.height > 0.0f ? naturalSize.width * height / naturalSize.height : height;
431 float ActorSizer::CalculateChildSizeBase(const Dali::Actor& child, Dimension::Type dimension)
433 // Fill to parent, taking size mode factor into account
434 switch(child.GetResizePolicy(dimension))
436 case ResizePolicy::FILL_TO_PARENT:
438 return GetLatestSize(dimension);
441 case ResizePolicy::SIZE_RELATIVE_TO_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);
448 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
450 Property::Value value = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
451 Vector3 childSizeModeFactor = value.Get<Vector3>();
452 return GetLatestSize(dimension) + GetDimensionValue(childSizeModeFactor, dimension);
457 return GetLatestSize(dimension);
462 float ActorSizer::GetLatestSize(Dimension::Type dimension) const
464 return IsLayoutNegotiated(dimension) ? GetNegotiatedDimension(dimension) : GetSize(dimension);
467 float ActorSizer::GetRelayoutSize(Dimension::Type dimension) const
469 Vector2 padding = GetPadding(dimension);
471 return GetLatestSize(dimension) + padding.x + padding.y;
474 float ActorSizer::NegotiateFromParent(Dimension::Type dimension)
476 Actor* parent = mOwner.GetParent();
479 Vector2 padding(GetPadding(dimension));
480 Vector2 parentPadding(parent->mSizer.GetPadding(dimension));
482 // Need to use actor API here to allow deriving actors to layout their children
483 return parent->CalculateChildSize(Dali::Actor(&mOwner), dimension) - parentPadding.x - parentPadding.y - padding.x - padding.y;
489 float ActorSizer::NegotiateFromChildren(Dimension::Type dimension)
491 float maxDimensionPoint = 0.0f;
493 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
495 ActorPtr child = mOwner.GetChildAt(i);
497 if(!child->RelayoutDependentOnParent(dimension))
499 // Calculate the min and max points that the children range across
500 float childPosition = GetDimensionValue(child->GetTargetPosition(), dimension);
501 float dimensionSize = child->mSizer.GetRelayoutSize(dimension);
502 maxDimensionPoint = std::max(maxDimensionPoint, childPosition + dimensionSize);
506 return maxDimensionPoint;
509 float ActorSizer::GetSize(Dimension::Type dimension) const
511 return GetDimensionValue(mTargetSize, dimension);
514 float ActorSizer::GetNaturalSize(Dimension::Type dimension) const
516 return GetDimensionValue(mOwner.GetNaturalSize(), dimension);
519 Vector2 ActorSizer::ApplySizeSetPolicy(const Vector2& size)
521 return mRelayoutData->ApplySizeSetPolicy(mOwner, size);
524 void ActorSizer::SetNegotiatedSize(RelayoutContainer& container)
526 // Do the set actor size
527 Vector2 negotiatedSize(GetLatestSize(Dimension::WIDTH), GetLatestSize(Dimension::HEIGHT));
529 // Adjust for size set policy
530 negotiatedSize = ApplySizeSetPolicy(negotiatedSize);
532 // Lock the flag to stop recursive relayouts on set size
533 mRelayoutData->insideRelayout = true;
534 SetSize(Vector3(negotiatedSize.width, negotiatedSize.height, 0.0f));
535 mRelayoutData->insideRelayout = false;
537 // Clear flags for all dimensions
538 SetLayoutDirty(false);
540 // Give deriving classes a chance to respond
541 mOwner.OnRelayout(negotiatedSize, container);
543 if(!mOwner.mOnRelayoutSignal.Empty())
545 Dali::Actor handle(&mOwner);
546 mOwner.mOnRelayoutSignal.Emit(handle);
549 mRelayoutData->relayoutRequested = false;
552 void ActorSizer::NegotiateSize(const Vector2& allocatedSize, RelayoutContainer& container)
554 // Force a size negotiation for actors that has assigned size during relayout
555 // This is required as otherwise the flags that force a relayout will not
556 // necessarilly be set. This will occur if the actor has already been laid out.
557 // The dirty flags are then cleared. Then if the actor is added back into the
558 // relayout container afterwards, the dirty flags would still be clear...
559 // causing a relayout to be skipped. Here we force any actors added to the
560 // container to be relayed out.
561 DALI_LOG_TIMER_START(NegSizeTimer1);
563 if(GetUseAssignedSize(Dimension::WIDTH))
565 SetLayoutNegotiated(false, Dimension::WIDTH);
567 if(GetUseAssignedSize(Dimension::HEIGHT))
569 SetLayoutNegotiated(false, Dimension::HEIGHT);
572 // Do the negotiation
573 NegotiateDimensions(allocatedSize);
575 // Set the actor size
576 SetNegotiatedSize(container);
578 // Negotiate down to children
579 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
581 ActorPtr child = mOwner.GetChildAt(i);
582 ActorSizer& childSizer = child->mSizer;
584 // Forces children that have already been laid out to be relayed out
585 // if they have assigned size during relayout.
586 if(childSizer.GetUseAssignedSize(Dimension::WIDTH))
588 childSizer.SetLayoutNegotiated(false, Dimension::WIDTH);
589 childSizer.SetLayoutDirty(true, Dimension::WIDTH);
592 if(childSizer.GetUseAssignedSize(Dimension::HEIGHT))
594 childSizer.SetLayoutNegotiated(false, Dimension::HEIGHT);
595 childSizer.SetLayoutDirty(true, Dimension::HEIGHT);
598 // Only relayout if required
599 if(childSizer.RelayoutRequired())
601 container.Add(Dali::Actor(child.Get()), mTargetSize.GetVectorXY());
605 // Reset the flag so that size negotiation will respect the actor's original resize policy
606 SetUseAssignedSize(false);
607 DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
610 void ActorSizer::SetUseAssignedSize(bool use, Dimension::Type dimension)
614 mRelayoutData->SetUseAssignedSize(use, dimension);
618 bool ActorSizer::GetUseAssignedSize(Dimension::Type dimension) const
620 return mRelayoutData && mRelayoutData->GetUseAssignedSize(dimension);
623 void ActorSizer::RelayoutRequest(Dimension::Type dimension)
625 Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
626 if(relayoutController)
628 Dali::Actor owner(&mOwner);
629 relayoutController->RequestRelayout(owner, dimension);
633 mRelayoutData->relayoutRequested = true;
638 void ActorSizer::SetPreferredSize(const Vector2& size)
642 // If valid width or height, then set the resize policy to FIXED
643 // 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,
644 // then change to FIXED as well
646 if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
648 SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
651 if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
653 SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
656 mRelayoutData->preferredSize = size;
657 mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
661 Vector2 ActorSizer::GetPreferredSize() const
663 return mRelayoutData ? Vector2(mRelayoutData->preferredSize) : Relayouter::DEFAULT_PREFERRED_SIZE;
666 void ActorSizer::SetMinimumSize(float size, Dimension::Type dimension)
668 EnsureRelayouter().SetMinimumSize(size, dimension);
672 float ActorSizer::GetMinimumSize(Dimension::Type dimension) const
674 return mRelayoutData ? mRelayoutData->GetMinimumSize(dimension) : 0.0f;
677 void ActorSizer::SetMaximumSize(float size, Dimension::Type dimension)
679 EnsureRelayouter().SetMaximumSize(size, dimension);
683 float ActorSizer::GetMaximumSize(Dimension::Type dimension) const
685 return mRelayoutData ? mRelayoutData->GetMaximumSize(dimension) : FLT_MAX;
688 void ActorSizer::OnAnimateSize(Animation& animation, Vector3 targetSize, bool relative)
690 mTargetSize = targetSize + mTargetSize * float(relative);
691 mAnimatedSize = mTargetSize;
692 mUseAnimatedSize = AnimatedSizeFlag::WIDTH | AnimatedSizeFlag::HEIGHT | AnimatedSizeFlag::DEPTH;
694 if(mRelayoutData && !mRelayoutData->relayoutRequested)
696 mRelayoutData->preferredSize.width = mAnimatedSize.width;
697 mRelayoutData->preferredSize.height = mAnimatedSize.height;
700 // Notify deriving classes
701 mOwner.OnSizeAnimation(animation, mTargetSize);
704 void ActorSizer::OnAnimateWidth(Animation& animation, float width, bool relative)
706 mTargetSize.width = width + float(relative) * mTargetSize.width;
707 mAnimatedSize.width = mTargetSize.width;
708 mUseAnimatedSize |= AnimatedSizeFlag::WIDTH;
710 if(mRelayoutData && !mRelayoutData->relayoutRequested)
712 mRelayoutData->preferredSize.width = mAnimatedSize.width;
715 // Notify deriving classes
716 mOwner.OnSizeAnimation(animation, mTargetSize);
719 void ActorSizer::OnAnimateHeight(Animation& animation, float height, bool relative)
721 mTargetSize.height = height + float(relative) * mTargetSize.height;
722 mAnimatedSize.height = mTargetSize.height;
723 mUseAnimatedSize |= AnimatedSizeFlag::HEIGHT;
725 if(mRelayoutData && !mRelayoutData->relayoutRequested)
727 mRelayoutData->preferredSize.height = mAnimatedSize.height;
730 // Notify deriving classes
731 mOwner.OnSizeAnimation(animation, mTargetSize);
734 void ActorSizer::OnAnimateDepth(Animation& animation, float depth, bool relative)
736 mTargetSize.depth = depth + float(relative) * mTargetSize.depth;
737 mAnimatedSize.depth = mTargetSize.depth;
738 mUseAnimatedSize |= AnimatedSizeFlag::DEPTH;
740 // Notify deriving classes
741 mOwner.OnSizeAnimation(animation, mTargetSize);
745 * @brief Extract a given dimension from a Vector3
747 * @param[in] values The values to extract from
748 * @param[in] dimension The dimension to extract
749 * @return Return the value for the dimension
751 float ActorSizer::GetDimensionValue(const Vector3& values, const Dimension::Type dimension) const
753 return ::GetDimensionValue(values.GetVectorXY(), dimension);
756 float ActorSizer::ClampDimension(float size, Dimension::Type dimension) const
758 const float minSize = GetMinimumSize(dimension);
759 const float maxSize = GetMaximumSize(dimension);
761 return std::max(minSize, std::min(size, maxSize));
764 void ActorSizer::NegotiateDimension(Dimension::Type dimension, const Vector2& allocatedSize, ActorDimensionStack& recursionStack)
766 // Check if it needs to be negotiated
767 if(IsLayoutDirty(dimension) && !IsLayoutNegotiated(dimension))
769 // Check that we havn't gotten into an infinite loop
770 ActorDimensionPair searchActor = ActorDimensionPair(&mOwner, dimension);
771 bool recursionFound = false;
772 for(auto& element : recursionStack)
774 if(element == searchActor)
776 recursionFound = true;
783 // Record the path that we have taken
784 recursionStack.emplace_back(&mOwner, dimension);
786 // Dimension dependency check
787 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
789 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
791 if(RelayoutDependentOnDimension(dimension, dimensionToCheck))
793 NegotiateDimension(dimensionToCheck, allocatedSize, recursionStack);
797 // Parent dependency check
798 Actor* parent = mOwner.GetParent();
799 if(parent && RelayoutDependentOnParent(dimension))
801 parent->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
804 // Children dependency check
805 if(mOwner.RelayoutDependentOnChildren(dimension))
807 for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
809 ActorPtr child = mOwner.GetChildAt(i);
811 // Only relayout child first if it is not dependent on this actor
812 if(!child->RelayoutDependentOnParent(dimension))
814 child->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
819 // For deriving classes
820 mOwner.OnCalculateRelayoutSize(dimension);
822 // All dependencies checked, calculate the size and set negotiated flag
823 const float newSize = ClampDimension(CalculateSize(dimension, allocatedSize), dimension);
825 SetNegotiatedDimension(newSize, dimension);
826 SetLayoutNegotiated(true, dimension);
828 // For deriving classes
829 mOwner.OnLayoutNegotiated(newSize, dimension);
831 // This actor has been successfully processed, pop it off the recursion stack
832 recursionStack.pop_back();
836 // TODO: Break infinite loop
837 SetLayoutNegotiated(true, dimension);
842 void ActorSizer::NegotiateDimensions(const Vector2& allocatedSize)
844 // Negotiate all dimensions that require it
845 gRecursionStack.clear();
847 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
849 const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
852 NegotiateDimension(dimension, allocatedSize, gRecursionStack);
856 float ActorSizer::CalculateSize(Dimension::Type dimension, const Vector2& maximumSize)
858 switch(GetResizePolicy(dimension))
860 case ResizePolicy::USE_NATURAL_SIZE:
862 return GetNaturalSize(dimension);
865 case ResizePolicy::FIXED:
867 return ::GetDimensionValue(GetPreferredSize(), dimension);
870 case ResizePolicy::USE_ASSIGNED_SIZE:
872 return ::GetDimensionValue(maximumSize, dimension);
875 case ResizePolicy::FILL_TO_PARENT:
876 case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
877 case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
879 return NegotiateFromParent(dimension);
882 case ResizePolicy::FIT_TO_CHILDREN:
884 return NegotiateFromChildren(dimension);
887 case ResizePolicy::DIMENSION_DEPENDENCY:
889 const Dimension::Type dimensionDependency = GetDimensionDependency(dimension);
892 if(dimension == Dimension::WIDTH && dimensionDependency == Dimension::HEIGHT)
894 // Use actor API to allow deriving actors to layout their content
895 return mOwner.GetWidthForHeight(GetNegotiatedDimension(Dimension::HEIGHT));
898 if(dimension == Dimension::HEIGHT && dimensionDependency == Dimension::WIDTH)
900 // Use actor API to allow deriving actors to layout their content
901 return mOwner.GetHeightForWidth(GetNegotiatedDimension(Dimension::WIDTH));
913 return 0.0f; // Default
916 } // namespace Dali::Internal