4d8e6d2906c3b864bbd463f777a8aff4cf6a4ecd
[platform/core/uifw/dali-core.git] / dali / internal / event / actors / actor-sizer.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 // CLASS HEADER
18 #include <dali/internal/event/actors/actor-sizer.h>
19
20 // EXTERNAL INCLUDES
21 #include <cfloat>
22
23 // INTERNAL INCLUDES
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>
32
33 #if defined(DEBUG_ENABLED)
34 extern Debug::Filter* gLogRelayoutFilter;
35 #endif
36
37 namespace
38 {
39 /**
40  * @brief Extract a given dimension from a Vector2
41  *
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
45  */
46 constexpr float GetDimensionValue(const Dali::Vector2& values, const Dali::Dimension::Type dimension)
47 {
48   switch(dimension)
49   {
50     case Dali::Dimension::WIDTH:
51     {
52       return values.width;
53     }
54     case Dali::Dimension::HEIGHT:
55     {
56       return values.height;
57     }
58     default:
59     {
60       break;
61     }
62   }
63   return 0.0f;
64 }
65
66 /**
67  * @brief Keep a static recursionstack vector to avoid creating temporary vectors every Relayout().
68  */
69 static Dali::Internal::ActorSizer::ActorDimensionStack gRecursionStack{};
70
71 } // namespace
72
73 namespace Dali::Internal
74 {
75 ActorSizer::ActorSizer(Internal::Actor& owner)
76 : mOwner(owner),
77   mRelayoutData(nullptr),
78   mTargetSize(Vector3::ZERO),
79   mAnimatedSize(Vector3::ZERO),
80   mUseAnimatedSize(AnimatedSizeFlag::CLEAR),
81   mInsideOnSizeSet(false)
82 {
83 }
84
85 ActorSizer::~ActorSizer()
86 {
87   // Delete optional relayout data
88   delete mRelayoutData;
89 }
90
91 void ActorSizer::SetSizeModeFactor(const Vector3& factor)
92 {
93   EnsureRelayouter();
94
95   mRelayoutData->sizeModeFactor = factor;
96 }
97 const Vector3& ActorSizer::GetSizeModeFactor() const
98 {
99   return mRelayoutData ? mRelayoutData->sizeModeFactor : Relayouter::DEFAULT_SIZE_MODE_FACTOR;
100 }
101
102 void ActorSizer::SetSize(const Vector3& size)
103 {
104   if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
105   {
106     // TODO we cannot just ignore the given Z but that means rewrite the size negotiation!!
107     SetPreferredSize(size.GetVectorXY());
108   }
109   else
110   {
111     SetSizeInternal(size);
112   }
113 }
114
115 void ActorSizer::SetSizeInternal(const Vector3& size)
116 {
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();
121
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))
128   {
129     mTargetSize = size;
130
131     // Update the preferred size after relayoutting
132     // It should be used in the next relayoutting
133     if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH && mRelayoutData)
134     {
135       mRelayoutData->preferredSize.width = mAnimatedSize.width;
136     }
137
138     if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT && mRelayoutData)
139     {
140       mRelayoutData->preferredSize.height = mAnimatedSize.height;
141     }
142
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);
146
147     // Notification for derived classes
148     mInsideOnSizeSet = true;
149     mOwner.OnSizeSet(mTargetSize);
150     mInsideOnSizeSet = false;
151
152     // Raise a relayout request if the flag is not locked
153     if(mRelayoutData && !mRelayoutData->insideRelayout)
154     {
155       RelayoutRequest();
156     }
157   }
158 }
159
160 void ActorSizer::SetWidth(float width)
161 {
162   if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
163   {
164     SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
165     mRelayoutData->preferredSize.width = width;
166   }
167   else
168   {
169     mTargetSize.width = width;
170
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);
174   }
175
176   mUseAnimatedSize &= ~AnimatedSizeFlag::WIDTH;
177   RelayoutRequest();
178 }
179
180 void ActorSizer::SetHeight(float height)
181 {
182   if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
183   {
184     SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
185     mRelayoutData->preferredSize.height = height;
186   }
187   else
188   {
189     mTargetSize.height = height;
190
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);
194   }
195
196   mUseAnimatedSize &= ~AnimatedSizeFlag::HEIGHT;
197   RelayoutRequest();
198 }
199
200 void ActorSizer::SetDepth(float depth)
201 {
202   mTargetSize.depth = depth;
203
204   mUseAnimatedSize &= ~AnimatedSizeFlag::DEPTH;
205
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);
209 }
210
211 Vector3 ActorSizer::GetTargetSize() const
212 {
213   Vector3 size = mTargetSize;
214
215   if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH)
216   {
217     // Should return animated size if size is animated
218     size.width = mAnimatedSize.width;
219   }
220   else
221   {
222     // Should return preferred size if size is fixed as set by SetSize
223     if(GetResizePolicy(Dimension::WIDTH) == ResizePolicy::FIXED)
224     {
225       size.width = GetPreferredSize().width;
226     }
227   }
228
229   if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT)
230   {
231     size.height = mAnimatedSize.height;
232   }
233   else
234   {
235     if(GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::FIXED)
236     {
237       size.height = GetPreferredSize().height;
238     }
239   }
240
241   if(mUseAnimatedSize & AnimatedSizeFlag::DEPTH)
242   {
243     size.depth = mAnimatedSize.depth;
244   }
245
246   return size;
247 }
248
249 void ActorSizer::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
250 {
251   EnsureRelayouter().SetResizePolicy(policy, dimension, mTargetSize);
252   mOwner.OnSetResizePolicy(policy, dimension);
253   RelayoutRequest();
254 }
255
256 ResizePolicy::Type ActorSizer::GetResizePolicy(Dimension::Type dimension) const
257 {
258   return mRelayoutData ? mRelayoutData->GetResizePolicy(dimension) : ResizePolicy::DEFAULT;
259 }
260
261 void ActorSizer::SetSizeScalePolicy(SizeScalePolicy::Type policy)
262 {
263   EnsureRelayouter();
264   mRelayoutData->sizeSetPolicy = policy;
265   RelayoutRequest();
266 }
267
268 SizeScalePolicy::Type ActorSizer::GetSizeScalePolicy() const
269 {
270   return mRelayoutData ? mRelayoutData->sizeSetPolicy : Relayouter::DEFAULT_SIZE_SCALE_POLICY;
271 }
272
273 Dimension::Type ActorSizer::GetDimensionDependency(Dimension::Type dimension) const
274 {
275   return mRelayoutData ? mRelayoutData->GetDimensionDependency(dimension) : Dimension::ALL_DIMENSIONS;
276 }
277
278 void ActorSizer::SetRelayoutEnabled(bool relayoutEnabled)
279 {
280   // If relayout data has not been allocated yet and the client is requesting
281   // to disable it, do nothing
282   if(mRelayoutData || relayoutEnabled)
283   {
284     EnsureRelayouter();
285
286     DALI_ASSERT_DEBUG(mRelayoutData && "mRelayoutData not created");
287
288     mRelayoutData->relayoutEnabled = relayoutEnabled;
289   }
290 }
291
292 bool ActorSizer::IsRelayoutEnabled() const
293 {
294   // Assume that if relayout data has not been allocated yet then relayout is disabled
295   return mRelayoutData && mRelayoutData->relayoutEnabled;
296 }
297
298 void ActorSizer::SetLayoutDirty(bool dirty, Dimension::Type dimension)
299 {
300   EnsureRelayouter().SetLayoutDirty(dirty, dimension);
301 }
302
303 bool ActorSizer::IsLayoutDirty(Dimension::Type dimension) const
304 {
305   return mRelayoutData && mRelayoutData->IsLayoutDirty(dimension);
306 }
307
308 bool ActorSizer::RelayoutPossible(Dimension::Type dimension) const
309 {
310   return mRelayoutData && mRelayoutData->relayoutEnabled && !IsLayoutDirty(dimension);
311 }
312
313 bool ActorSizer::RelayoutRequired(Dimension::Type dimension) const
314 {
315   return mRelayoutData && mRelayoutData->relayoutEnabled && IsLayoutDirty(dimension);
316 }
317
318 ActorSizer::Relayouter& ActorSizer::EnsureRelayouter()
319 {
320   // Assign relayouter
321   if(!mRelayoutData)
322   {
323     mRelayoutData = new Relayouter();
324   }
325
326   return *mRelayoutData;
327 }
328
329 bool ActorSizer::RelayoutDependentOnParent(Dimension::Type dimension)
330 {
331   // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE. This
332   // will keep the existing behaviour, and return false.
333
334   // Check if actor is dependent on parent
335   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
336   {
337     if((dimension & (1 << i)))
338     {
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)
341       {
342         return true;
343       }
344     }
345   }
346
347   return false;
348 }
349
350 bool ActorSizer::RelayoutDependentOnChildrenBase(Dimension::Type dimension)
351 {
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.
355
356   // Check if actor is dependent on children
357   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
358   {
359     if((dimension & (1 << i)))
360     {
361       const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
362       switch(resizePolicy)
363       {
364         case ResizePolicy::FIT_TO_CHILDREN:
365         case ResizePolicy::USE_NATURAL_SIZE: // i.e. For things that calculate their size based on children
366         {
367           return true;
368         }
369
370         default:
371         {
372           break;
373         }
374       }
375     }
376   }
377
378   return false;
379 }
380
381 bool ActorSizer::RelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependentDimension)
382 {
383   return mRelayoutData && mRelayoutData->GetRelayoutDependentOnDimension(dimension, dependentDimension);
384 }
385
386 void ActorSizer::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
387 {
388   if(mRelayoutData)
389   {
390     mRelayoutData->SetNegotiatedDimension(negotiatedDimension, dimension);
391   }
392 }
393
394 float ActorSizer::GetNegotiatedDimension(Dimension::Type dimension) const
395 {
396   return mRelayoutData ? mRelayoutData->GetNegotiatedDimension(dimension) : 0.0f;
397 }
398
399 void ActorSizer::SetPadding(const Vector2& padding, Dimension::Type dimension)
400 {
401   EnsureRelayouter().SetPadding(padding, dimension);
402 }
403
404 Vector2 ActorSizer::GetPadding(Dimension::Type dimension) const
405 {
406   return mRelayoutData ? mRelayoutData->GetPadding(dimension) : Relayouter::DEFAULT_DIMENSION_PADDING;
407 }
408
409 void ActorSizer::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
410 {
411   EnsureRelayouter().SetLayoutNegotiated(negotiated, dimension);
412 }
413
414 bool ActorSizer::IsLayoutNegotiated(Dimension::Type dimension) const
415 {
416   return mRelayoutData && mRelayoutData->IsLayoutNegotiated(dimension);
417 }
418
419 float ActorSizer::GetHeightForWidthBase(float width)
420 {
421   const Vector3 naturalSize = mOwner.GetNaturalSize();
422   return naturalSize.width > 0.0f ? naturalSize.height * width / naturalSize.width : width;
423 }
424
425 float ActorSizer::GetWidthForHeightBase(float height)
426 {
427   const Vector3 naturalSize = mOwner.GetNaturalSize();
428   return naturalSize.height > 0.0f ? naturalSize.width * height / naturalSize.height : height;
429 }
430
431 float ActorSizer::CalculateChildSizeBase(const Dali::Actor& child, Dimension::Type dimension)
432 {
433   // Fill to parent, taking size mode factor into account
434   switch(child.GetResizePolicy(dimension))
435   {
436     case ResizePolicy::FILL_TO_PARENT:
437     {
438       return GetLatestSize(dimension);
439     }
440
441     case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
442     {
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);
446     }
447
448     case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
449     {
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);
453     }
454
455     default:
456     {
457       return GetLatestSize(dimension);
458     }
459   }
460 }
461
462 float ActorSizer::GetLatestSize(Dimension::Type dimension) const
463 {
464   return IsLayoutNegotiated(dimension) ? GetNegotiatedDimension(dimension) : GetSize(dimension);
465 }
466
467 float ActorSizer::GetRelayoutSize(Dimension::Type dimension) const
468 {
469   Vector2 padding = GetPadding(dimension);
470
471   return GetLatestSize(dimension) + padding.x + padding.y;
472 }
473
474 float ActorSizer::NegotiateFromParent(Dimension::Type dimension)
475 {
476   Actor* parent = mOwner.GetParent();
477   if(parent)
478   {
479     Vector2 padding(GetPadding(dimension));
480     Vector2 parentPadding(parent->mSizer.GetPadding(dimension));
481
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;
484   }
485
486   return 0.0f;
487 }
488
489 float ActorSizer::NegotiateFromChildren(Dimension::Type dimension)
490 {
491   float maxDimensionPoint = 0.0f;
492
493   for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
494   {
495     ActorPtr child = mOwner.GetChildAt(i);
496
497     if(!child->RelayoutDependentOnParent(dimension))
498     {
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);
503     }
504   }
505
506   return maxDimensionPoint;
507 }
508
509 float ActorSizer::GetSize(Dimension::Type dimension) const
510 {
511   return GetDimensionValue(mTargetSize, dimension);
512 }
513
514 float ActorSizer::GetNaturalSize(Dimension::Type dimension) const
515 {
516   return GetDimensionValue(mOwner.GetNaturalSize(), dimension);
517 }
518
519 Vector2 ActorSizer::ApplySizeSetPolicy(const Vector2& size)
520 {
521   return mRelayoutData->ApplySizeSetPolicy(mOwner, size);
522 }
523
524 void ActorSizer::SetNegotiatedSize(RelayoutContainer& container)
525 {
526   // Do the set actor size
527   Vector2 negotiatedSize(GetLatestSize(Dimension::WIDTH), GetLatestSize(Dimension::HEIGHT));
528
529   // Adjust for size set policy
530   negotiatedSize = ApplySizeSetPolicy(negotiatedSize);
531
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;
536
537   // Clear flags for all dimensions
538   SetLayoutDirty(false);
539
540   // Give deriving classes a chance to respond
541   mOwner.OnRelayout(negotiatedSize, container);
542
543   if(!mOwner.mOnRelayoutSignal.Empty())
544   {
545     Dali::Actor handle(&mOwner);
546     mOwner.mOnRelayoutSignal.Emit(handle);
547   }
548
549   mRelayoutData->relayoutRequested = false;
550 }
551
552 void ActorSizer::NegotiateSize(const Vector2& allocatedSize, RelayoutContainer& container)
553 {
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);
562
563   if(GetUseAssignedSize(Dimension::WIDTH))
564   {
565     SetLayoutNegotiated(false, Dimension::WIDTH);
566   }
567   if(GetUseAssignedSize(Dimension::HEIGHT))
568   {
569     SetLayoutNegotiated(false, Dimension::HEIGHT);
570   }
571
572   // Do the negotiation
573   NegotiateDimensions(allocatedSize);
574
575   // Set the actor size
576   SetNegotiatedSize(container);
577
578   // Negotiate down to children
579   for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
580   {
581     ActorPtr    child      = mOwner.GetChildAt(i);
582     ActorSizer& childSizer = child->mSizer;
583
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))
587     {
588       childSizer.SetLayoutNegotiated(false, Dimension::WIDTH);
589       childSizer.SetLayoutDirty(true, Dimension::WIDTH);
590     }
591
592     if(childSizer.GetUseAssignedSize(Dimension::HEIGHT))
593     {
594       childSizer.SetLayoutNegotiated(false, Dimension::HEIGHT);
595       childSizer.SetLayoutDirty(true, Dimension::HEIGHT);
596     }
597
598     // Only relayout if required
599     if(childSizer.RelayoutRequired())
600     {
601       container.Add(Dali::Actor(child.Get()), mTargetSize.GetVectorXY());
602     }
603   }
604
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: ");
608 }
609
610 void ActorSizer::SetUseAssignedSize(bool use, Dimension::Type dimension)
611 {
612   if(mRelayoutData)
613   {
614     mRelayoutData->SetUseAssignedSize(use, dimension);
615   }
616 }
617
618 bool ActorSizer::GetUseAssignedSize(Dimension::Type dimension) const
619 {
620   return mRelayoutData && mRelayoutData->GetUseAssignedSize(dimension);
621 }
622
623 void ActorSizer::RelayoutRequest(Dimension::Type dimension)
624 {
625   Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
626   if(relayoutController)
627   {
628     Dali::Actor owner(&mOwner);
629     relayoutController->RequestRelayout(owner, dimension);
630
631     if(mRelayoutData)
632     {
633       mRelayoutData->relayoutRequested = true;
634     }
635   }
636 }
637
638 void ActorSizer::SetPreferredSize(const Vector2& size)
639 {
640   EnsureRelayouter();
641
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
645
646   if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
647   {
648     SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
649   }
650
651   if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
652   {
653     SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
654   }
655
656   mRelayoutData->preferredSize = size;
657   mUseAnimatedSize             = AnimatedSizeFlag::CLEAR;
658   RelayoutRequest();
659 }
660
661 Vector2 ActorSizer::GetPreferredSize() const
662 {
663   return mRelayoutData ? Vector2(mRelayoutData->preferredSize) : Relayouter::DEFAULT_PREFERRED_SIZE;
664 }
665
666 void ActorSizer::SetMinimumSize(float size, Dimension::Type dimension)
667 {
668   EnsureRelayouter().SetMinimumSize(size, dimension);
669   RelayoutRequest();
670 }
671
672 float ActorSizer::GetMinimumSize(Dimension::Type dimension) const
673 {
674   return mRelayoutData ? mRelayoutData->GetMinimumSize(dimension) : 0.0f;
675 }
676
677 void ActorSizer::SetMaximumSize(float size, Dimension::Type dimension)
678 {
679   EnsureRelayouter().SetMaximumSize(size, dimension);
680   RelayoutRequest();
681 }
682
683 float ActorSizer::GetMaximumSize(Dimension::Type dimension) const
684 {
685   return mRelayoutData ? mRelayoutData->GetMaximumSize(dimension) : FLT_MAX;
686 }
687
688 void ActorSizer::OnAnimateSize(Animation& animation, Vector3 targetSize, bool relative)
689 {
690   mTargetSize      = targetSize + mTargetSize * float(relative);
691   mAnimatedSize    = mTargetSize;
692   mUseAnimatedSize = AnimatedSizeFlag::WIDTH | AnimatedSizeFlag::HEIGHT | AnimatedSizeFlag::DEPTH;
693
694   if(mRelayoutData && !mRelayoutData->relayoutRequested)
695   {
696     mRelayoutData->preferredSize.width  = mAnimatedSize.width;
697     mRelayoutData->preferredSize.height = mAnimatedSize.height;
698   }
699
700   // Notify deriving classes
701   mOwner.OnSizeAnimation(animation, mTargetSize);
702 }
703
704 void ActorSizer::OnAnimateWidth(Animation& animation, float width, bool relative)
705 {
706   mTargetSize.width   = width + float(relative) * mTargetSize.width;
707   mAnimatedSize.width = mTargetSize.width;
708   mUseAnimatedSize |= AnimatedSizeFlag::WIDTH;
709
710   if(mRelayoutData && !mRelayoutData->relayoutRequested)
711   {
712     mRelayoutData->preferredSize.width = mAnimatedSize.width;
713   }
714
715   // Notify deriving classes
716   mOwner.OnSizeAnimation(animation, mTargetSize);
717 }
718
719 void ActorSizer::OnAnimateHeight(Animation& animation, float height, bool relative)
720 {
721   mTargetSize.height   = height + float(relative) * mTargetSize.height;
722   mAnimatedSize.height = mTargetSize.height;
723   mUseAnimatedSize |= AnimatedSizeFlag::HEIGHT;
724
725   if(mRelayoutData && !mRelayoutData->relayoutRequested)
726   {
727     mRelayoutData->preferredSize.height = mAnimatedSize.height;
728   }
729
730   // Notify deriving classes
731   mOwner.OnSizeAnimation(animation, mTargetSize);
732 }
733
734 void ActorSizer::OnAnimateDepth(Animation& animation, float depth, bool relative)
735 {
736   mTargetSize.depth   = depth + float(relative) * mTargetSize.depth;
737   mAnimatedSize.depth = mTargetSize.depth;
738   mUseAnimatedSize |= AnimatedSizeFlag::DEPTH;
739
740   // Notify deriving classes
741   mOwner.OnSizeAnimation(animation, mTargetSize);
742 }
743
744 /**
745  * @brief Extract a given dimension from a Vector3
746  *
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
750  */
751 float ActorSizer::GetDimensionValue(const Vector3& values, const Dimension::Type dimension) const
752 {
753   return ::GetDimensionValue(values.GetVectorXY(), dimension);
754 }
755
756 float ActorSizer::ClampDimension(float size, Dimension::Type dimension) const
757 {
758   const float minSize = GetMinimumSize(dimension);
759   const float maxSize = GetMaximumSize(dimension);
760
761   return std::max(minSize, std::min(size, maxSize));
762 }
763
764 void ActorSizer::NegotiateDimension(Dimension::Type dimension, const Vector2& allocatedSize, ActorDimensionStack& recursionStack)
765 {
766   // Check if it needs to be negotiated
767   if(IsLayoutDirty(dimension) && !IsLayoutNegotiated(dimension))
768   {
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)
773     {
774       if(element == searchActor)
775       {
776         recursionFound = true;
777         break;
778       }
779     }
780
781     if(!recursionFound)
782     {
783       // Record the path that we have taken
784       recursionStack.emplace_back(&mOwner, dimension);
785
786       // Dimension dependency check
787       for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
788       {
789         Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
790
791         if(RelayoutDependentOnDimension(dimension, dimensionToCheck))
792         {
793           NegotiateDimension(dimensionToCheck, allocatedSize, recursionStack);
794         }
795       }
796
797       // Parent dependency check
798       Actor* parent = mOwner.GetParent();
799       if(parent && RelayoutDependentOnParent(dimension))
800       {
801         parent->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
802       }
803
804       // Children dependency check
805       if(mOwner.RelayoutDependentOnChildren(dimension))
806       {
807         for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
808         {
809           ActorPtr child = mOwner.GetChildAt(i);
810
811           // Only relayout child first if it is not dependent on this actor
812           if(!child->RelayoutDependentOnParent(dimension))
813           {
814             child->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
815           }
816         }
817       }
818
819       // For deriving classes
820       mOwner.OnCalculateRelayoutSize(dimension);
821
822       // All dependencies checked, calculate the size and set negotiated flag
823       const float newSize = ClampDimension(CalculateSize(dimension, allocatedSize), dimension);
824
825       SetNegotiatedDimension(newSize, dimension);
826       SetLayoutNegotiated(true, dimension);
827
828       // For deriving classes
829       mOwner.OnLayoutNegotiated(newSize, dimension);
830
831       // This actor has been successfully processed, pop it off the recursion stack
832       recursionStack.pop_back();
833     }
834     else
835     {
836       // TODO: Break infinite loop
837       SetLayoutNegotiated(true, dimension);
838     }
839   }
840 }
841
842 void ActorSizer::NegotiateDimensions(const Vector2& allocatedSize)
843 {
844   // Negotiate all dimensions that require it
845   gRecursionStack.clear();
846
847   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
848   {
849     const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
850
851     // Negotiate
852     NegotiateDimension(dimension, allocatedSize, gRecursionStack);
853   }
854 }
855
856 float ActorSizer::CalculateSize(Dimension::Type dimension, const Vector2& maximumSize)
857 {
858   switch(GetResizePolicy(dimension))
859   {
860     case ResizePolicy::USE_NATURAL_SIZE:
861     {
862       return GetNaturalSize(dimension);
863     }
864
865     case ResizePolicy::FIXED:
866     {
867       return ::GetDimensionValue(GetPreferredSize(), dimension);
868     }
869
870     case ResizePolicy::USE_ASSIGNED_SIZE:
871     {
872       return ::GetDimensionValue(maximumSize, dimension);
873     }
874
875     case ResizePolicy::FILL_TO_PARENT:
876     case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
877     case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
878     {
879       return NegotiateFromParent(dimension);
880     }
881
882     case ResizePolicy::FIT_TO_CHILDREN:
883     {
884       return NegotiateFromChildren(dimension);
885     }
886
887     case ResizePolicy::DIMENSION_DEPENDENCY:
888     {
889       const Dimension::Type dimensionDependency = GetDimensionDependency(dimension);
890
891       // Custom rules
892       if(dimension == Dimension::WIDTH && dimensionDependency == Dimension::HEIGHT)
893       {
894         // Use actor API to allow deriving actors to layout their content
895         return mOwner.GetWidthForHeight(GetNegotiatedDimension(Dimension::HEIGHT));
896       }
897
898       if(dimension == Dimension::HEIGHT && dimensionDependency == Dimension::WIDTH)
899       {
900         // Use actor API to allow deriving actors to layout their content
901         return mOwner.GetHeightForWidth(GetNegotiatedDimension(Dimension::WIDTH));
902       }
903
904       break;
905     }
906
907     default:
908     {
909       break;
910     }
911   }
912
913   return 0.0f; // Default
914 }
915
916 } // namespace Dali::Internal