Reduce GetCurrentSize call time
[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   if(mTargetSize != size || mTargetSize != mOwner.GetCurrentSize())
121   {
122     mTargetSize = size;
123
124     // Update the preferred size after relayoutting
125     // It should be used in the next relayoutting
126     if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH && mRelayoutData)
127     {
128       mRelayoutData->preferredSize.width = mAnimatedSize.width;
129     }
130
131     if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT && mRelayoutData)
132     {
133       mRelayoutData->preferredSize.height = mAnimatedSize.height;
134     }
135
136     // node is being used in a separate thread; queue a message to set the value & base value
137     auto& node = mOwner.GetNode();
138     SceneGraph::NodeTransformPropertyMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::Bake, mTargetSize);
139
140     // Notification for derived classes
141     mInsideOnSizeSet = true;
142     mOwner.OnSizeSet(mTargetSize);
143     mInsideOnSizeSet = false;
144
145     // Raise a relayout request if the flag is not locked
146     if(mRelayoutData && !mRelayoutData->insideRelayout)
147     {
148       RelayoutRequest();
149     }
150   }
151 }
152
153 void ActorSizer::SetWidth(float width)
154 {
155   if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
156   {
157     SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
158     mRelayoutData->preferredSize.width = width;
159   }
160   else
161   {
162     mTargetSize.width = width;
163
164     // node is being used in a separate thread; queue a message to set the value & base value
165     auto& node = mOwner.GetNode();
166     SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeX, width);
167   }
168
169   mUseAnimatedSize &= ~AnimatedSizeFlag::WIDTH;
170   RelayoutRequest();
171 }
172
173 void ActorSizer::SetHeight(float height)
174 {
175   if(IsRelayoutEnabled() && !mRelayoutData->insideRelayout)
176   {
177     SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
178     mRelayoutData->preferredSize.height = height;
179   }
180   else
181   {
182     mTargetSize.height = height;
183
184     // node is being used in a separate thread; queue a message to set the value & base value
185     auto& node = mOwner.GetNode();
186     SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeY, height);
187   }
188
189   mUseAnimatedSize &= ~AnimatedSizeFlag::HEIGHT;
190   RelayoutRequest();
191 }
192
193 void ActorSizer::SetDepth(float depth)
194 {
195   mTargetSize.depth = depth;
196
197   mUseAnimatedSize &= ~AnimatedSizeFlag::DEPTH;
198
199   // node is being used in a separate thread; queue a message to set the value & base value
200   auto& node = mOwner.GetNode();
201   SceneGraph::NodeTransformComponentMessage<Vector3>::Send(mOwner.GetEventThreadServices(), &node, &node.mSize, &SceneGraph::TransformManagerPropertyHandler<Vector3>::BakeZ, depth);
202 }
203
204 Vector3 ActorSizer::GetTargetSize() const
205 {
206   Vector3 size = mTargetSize;
207
208   if(mUseAnimatedSize & AnimatedSizeFlag::WIDTH)
209   {
210     // Should return animated size if size is animated
211     size.width = mAnimatedSize.width;
212   }
213   else
214   {
215     // Should return preferred size if size is fixed as set by SetSize
216     if(GetResizePolicy(Dimension::WIDTH) == ResizePolicy::FIXED)
217     {
218       size.width = GetPreferredSize().width;
219     }
220   }
221
222   if(mUseAnimatedSize & AnimatedSizeFlag::HEIGHT)
223   {
224     size.height = mAnimatedSize.height;
225   }
226   else
227   {
228     if(GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::FIXED)
229     {
230       size.height = GetPreferredSize().height;
231     }
232   }
233
234   if(mUseAnimatedSize & AnimatedSizeFlag::DEPTH)
235   {
236     size.depth = mAnimatedSize.depth;
237   }
238
239   return size;
240 }
241
242 void ActorSizer::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension)
243 {
244   EnsureRelayouter().SetResizePolicy(policy, dimension, mTargetSize);
245   mOwner.OnSetResizePolicy(policy, dimension);
246   RelayoutRequest();
247 }
248
249 ResizePolicy::Type ActorSizer::GetResizePolicy(Dimension::Type dimension) const
250 {
251   return mRelayoutData ? mRelayoutData->GetResizePolicy(dimension) : ResizePolicy::DEFAULT;
252 }
253
254 void ActorSizer::SetSizeScalePolicy(SizeScalePolicy::Type policy)
255 {
256   EnsureRelayouter();
257   mRelayoutData->sizeSetPolicy = policy;
258   RelayoutRequest();
259 }
260
261 SizeScalePolicy::Type ActorSizer::GetSizeScalePolicy() const
262 {
263   return mRelayoutData ? mRelayoutData->sizeSetPolicy : Relayouter::DEFAULT_SIZE_SCALE_POLICY;
264 }
265
266 Dimension::Type ActorSizer::GetDimensionDependency(Dimension::Type dimension) const
267 {
268   return mRelayoutData ? mRelayoutData->GetDimensionDependency(dimension) : Dimension::ALL_DIMENSIONS;
269 }
270
271 void ActorSizer::SetRelayoutEnabled(bool relayoutEnabled)
272 {
273   // If relayout data has not been allocated yet and the client is requesting
274   // to disable it, do nothing
275   if(mRelayoutData || relayoutEnabled)
276   {
277     EnsureRelayouter();
278
279     DALI_ASSERT_DEBUG(mRelayoutData && "mRelayoutData not created");
280
281     mRelayoutData->relayoutEnabled = relayoutEnabled;
282   }
283 }
284
285 bool ActorSizer::IsRelayoutEnabled() const
286 {
287   // Assume that if relayout data has not been allocated yet then relayout is disabled
288   return mRelayoutData && mRelayoutData->relayoutEnabled;
289 }
290
291 void ActorSizer::SetLayoutDirty(bool dirty, Dimension::Type dimension)
292 {
293   EnsureRelayouter().SetLayoutDirty(dirty, dimension);
294 }
295
296 bool ActorSizer::IsLayoutDirty(Dimension::Type dimension) const
297 {
298   return mRelayoutData && mRelayoutData->IsLayoutDirty(dimension);
299 }
300
301 bool ActorSizer::RelayoutPossible(Dimension::Type dimension) const
302 {
303   return mRelayoutData && mRelayoutData->relayoutEnabled && !IsLayoutDirty(dimension);
304 }
305
306 bool ActorSizer::RelayoutRequired(Dimension::Type dimension) const
307 {
308   return mRelayoutData && mRelayoutData->relayoutEnabled && IsLayoutDirty(dimension);
309 }
310
311 ActorSizer::Relayouter& ActorSizer::EnsureRelayouter()
312 {
313   // Assign relayouter
314   if(!mRelayoutData)
315   {
316     mRelayoutData = new Relayouter();
317   }
318
319   return *mRelayoutData;
320 }
321
322 bool ActorSizer::RelayoutDependentOnParent(Dimension::Type dimension)
323 {
324   // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE. This
325   // will keep the existing behaviour, and return false.
326
327   // Check if actor is dependent on parent
328   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
329   {
330     if((dimension & (1 << i)))
331     {
332       const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
333       if(resizePolicy == ResizePolicy::FILL_TO_PARENT || resizePolicy == ResizePolicy::SIZE_RELATIVE_TO_PARENT || resizePolicy == ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT)
334       {
335         return true;
336       }
337     }
338   }
339
340   return false;
341 }
342
343 bool ActorSizer::RelayoutDependentOnChildrenBase(Dimension::Type dimension)
344 {
345   // If there is no relayouting, GetResizePolicy returns Default, which is USE_NATURAL_SIZE.
346   // This means this will return true when there is no relayouting, but that seems
347   // counter-intuitive. Will keep current behaviour for now, as it is consistent.
348
349   // Check if actor is dependent on children
350   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
351   {
352     if((dimension & (1 << i)))
353     {
354       const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
355       switch(resizePolicy)
356       {
357         case ResizePolicy::FIT_TO_CHILDREN:
358         case ResizePolicy::USE_NATURAL_SIZE: // i.e. For things that calculate their size based on children
359         {
360           return true;
361         }
362
363         default:
364         {
365           break;
366         }
367       }
368     }
369   }
370
371   return false;
372 }
373
374 bool ActorSizer::RelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependentDimension)
375 {
376   return mRelayoutData && mRelayoutData->GetRelayoutDependentOnDimension(dimension, dependentDimension);
377 }
378
379 void ActorSizer::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
380 {
381   if(mRelayoutData)
382   {
383     mRelayoutData->SetNegotiatedDimension(negotiatedDimension, dimension);
384   }
385 }
386
387 float ActorSizer::GetNegotiatedDimension(Dimension::Type dimension) const
388 {
389   return mRelayoutData ? mRelayoutData->GetNegotiatedDimension(dimension) : 0.0f;
390 }
391
392 void ActorSizer::SetPadding(const Vector2& padding, Dimension::Type dimension)
393 {
394   EnsureRelayouter().SetPadding(padding, dimension);
395 }
396
397 Vector2 ActorSizer::GetPadding(Dimension::Type dimension) const
398 {
399   return mRelayoutData ? mRelayoutData->GetPadding(dimension) : Relayouter::DEFAULT_DIMENSION_PADDING;
400 }
401
402 void ActorSizer::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
403 {
404   EnsureRelayouter().SetLayoutNegotiated(negotiated, dimension);
405 }
406
407 bool ActorSizer::IsLayoutNegotiated(Dimension::Type dimension) const
408 {
409   return mRelayoutData && mRelayoutData->IsLayoutNegotiated(dimension);
410 }
411
412 float ActorSizer::GetHeightForWidthBase(float width)
413 {
414   const Vector3 naturalSize = mOwner.GetNaturalSize();
415   return naturalSize.width > 0.0f ? naturalSize.height * width / naturalSize.width : width;
416 }
417
418 float ActorSizer::GetWidthForHeightBase(float height)
419 {
420   const Vector3 naturalSize = mOwner.GetNaturalSize();
421   return naturalSize.height > 0.0f ? naturalSize.width * height / naturalSize.height : height;
422 }
423
424 float ActorSizer::CalculateChildSizeBase(const Dali::Actor& child, Dimension::Type dimension)
425 {
426   // Fill to parent, taking size mode factor into account
427   switch(child.GetResizePolicy(dimension))
428   {
429     case ResizePolicy::FILL_TO_PARENT:
430     {
431       return GetLatestSize(dimension);
432     }
433
434     case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
435     {
436       Property::Value value               = child.GetProperty(Dali::Actor::Property::SIZE_MODE_FACTOR);
437       Vector3         childSizeModeFactor = value.Get<Vector3>();
438       return GetLatestSize(dimension) * GetDimensionValue(childSizeModeFactor, dimension);
439     }
440
441     case ResizePolicy::SIZE_FIXED_OFFSET_FROM_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     default:
449     {
450       return GetLatestSize(dimension);
451     }
452   }
453 }
454
455 float ActorSizer::GetLatestSize(Dimension::Type dimension) const
456 {
457   return IsLayoutNegotiated(dimension) ? GetNegotiatedDimension(dimension) : GetSize(dimension);
458 }
459
460 float ActorSizer::GetRelayoutSize(Dimension::Type dimension) const
461 {
462   Vector2 padding = GetPadding(dimension);
463
464   return GetLatestSize(dimension) + padding.x + padding.y;
465 }
466
467 float ActorSizer::NegotiateFromParent(Dimension::Type dimension)
468 {
469   Actor* parent = mOwner.GetParent();
470   if(parent)
471   {
472     Vector2 padding(GetPadding(dimension));
473     Vector2 parentPadding(parent->mSizer.GetPadding(dimension));
474
475     // Need to use actor API here to allow deriving actors to layout their children
476     return parent->CalculateChildSize(Dali::Actor(&mOwner), dimension) - parentPadding.x - parentPadding.y - padding.x - padding.y;
477   }
478
479   return 0.0f;
480 }
481
482 float ActorSizer::NegotiateFromChildren(Dimension::Type dimension)
483 {
484   float maxDimensionPoint = 0.0f;
485
486   for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
487   {
488     ActorPtr child = mOwner.GetChildAt(i);
489
490     if(!child->RelayoutDependentOnParent(dimension))
491     {
492       // Calculate the min and max points that the children range across
493       float childPosition = GetDimensionValue(child->GetTargetPosition(), dimension);
494       float dimensionSize = child->mSizer.GetRelayoutSize(dimension);
495       maxDimensionPoint   = std::max(maxDimensionPoint, childPosition + dimensionSize);
496     }
497   }
498
499   return maxDimensionPoint;
500 }
501
502 float ActorSizer::GetSize(Dimension::Type dimension) const
503 {
504   return GetDimensionValue(mTargetSize, dimension);
505 }
506
507 float ActorSizer::GetNaturalSize(Dimension::Type dimension) const
508 {
509   return GetDimensionValue(mOwner.GetNaturalSize(), dimension);
510 }
511
512 Vector2 ActorSizer::ApplySizeSetPolicy(const Vector2& size)
513 {
514   return mRelayoutData->ApplySizeSetPolicy(mOwner, size);
515 }
516
517 void ActorSizer::SetNegotiatedSize(RelayoutContainer& container)
518 {
519   // Do the set actor size
520   Vector2 negotiatedSize(GetLatestSize(Dimension::WIDTH), GetLatestSize(Dimension::HEIGHT));
521
522   // Adjust for size set policy
523   negotiatedSize = ApplySizeSetPolicy(negotiatedSize);
524
525   // Lock the flag to stop recursive relayouts on set size
526   mRelayoutData->insideRelayout = true;
527   SetSize(Vector3(negotiatedSize.width, negotiatedSize.height, 0.0f));
528   mRelayoutData->insideRelayout = false;
529
530   // Clear flags for all dimensions
531   SetLayoutDirty(false);
532
533   // Give deriving classes a chance to respond
534   mOwner.OnRelayout(negotiatedSize, container);
535
536   if(!mOwner.mOnRelayoutSignal.Empty())
537   {
538     Dali::Actor handle(&mOwner);
539     mOwner.mOnRelayoutSignal.Emit(handle);
540   }
541
542   mRelayoutData->relayoutRequested = false;
543 }
544
545 void ActorSizer::NegotiateSize(const Vector2& allocatedSize, RelayoutContainer& container)
546 {
547   // Force a size negotiation for actors that has assigned size during relayout
548   // This is required as otherwise the flags that force a relayout will not
549   // necessarilly be set. This will occur if the actor has already been laid out.
550   // The dirty flags are then cleared. Then if the actor is added back into the
551   // relayout container afterwards, the dirty flags would still be clear...
552   // causing a relayout to be skipped. Here we force any actors added to the
553   // container to be relayed out.
554   DALI_LOG_TIMER_START(NegSizeTimer1);
555
556   if(GetUseAssignedSize(Dimension::WIDTH))
557   {
558     SetLayoutNegotiated(false, Dimension::WIDTH);
559   }
560   if(GetUseAssignedSize(Dimension::HEIGHT))
561   {
562     SetLayoutNegotiated(false, Dimension::HEIGHT);
563   }
564
565   // Do the negotiation
566   NegotiateDimensions(allocatedSize);
567
568   // Set the actor size
569   SetNegotiatedSize(container);
570
571   // Negotiate down to children
572   for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
573   {
574     ActorPtr    child      = mOwner.GetChildAt(i);
575     ActorSizer& childSizer = child->mSizer;
576
577     // Forces children that have already been laid out to be relayed out
578     // if they have assigned size during relayout.
579     if(childSizer.GetUseAssignedSize(Dimension::WIDTH))
580     {
581       childSizer.SetLayoutNegotiated(false, Dimension::WIDTH);
582       childSizer.SetLayoutDirty(true, Dimension::WIDTH);
583     }
584
585     if(childSizer.GetUseAssignedSize(Dimension::HEIGHT))
586     {
587       childSizer.SetLayoutNegotiated(false, Dimension::HEIGHT);
588       childSizer.SetLayoutDirty(true, Dimension::HEIGHT);
589     }
590
591     // Only relayout if required
592     if(childSizer.RelayoutRequired())
593     {
594       container.Add(Dali::Actor(child.Get()), mTargetSize.GetVectorXY());
595     }
596   }
597
598   // Reset the flag so that size negotiation will respect the actor's original resize policy
599   SetUseAssignedSize(false);
600   DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
601 }
602
603 void ActorSizer::SetUseAssignedSize(bool use, Dimension::Type dimension)
604 {
605   if(mRelayoutData)
606   {
607     mRelayoutData->SetUseAssignedSize(use, dimension);
608   }
609 }
610
611 bool ActorSizer::GetUseAssignedSize(Dimension::Type dimension) const
612 {
613   return mRelayoutData && mRelayoutData->GetUseAssignedSize(dimension);
614 }
615
616 void ActorSizer::RelayoutRequest(Dimension::Type dimension)
617 {
618   Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
619   if(relayoutController)
620   {
621     Dali::Actor owner(&mOwner);
622     relayoutController->RequestRelayout(owner, dimension);
623
624     if(mRelayoutData)
625     {
626       mRelayoutData->relayoutRequested = true;
627     }
628   }
629 }
630
631 void ActorSizer::SetPreferredSize(const Vector2& size)
632 {
633   EnsureRelayouter();
634
635   // If valid width or height, then set the resize policy to FIXED
636   // A 0 width or height may also be required so if the resize policy has not been changed, i.e. is still set to DEFAULT,
637   // then change to FIXED as well
638
639   if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
640   {
641     SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
642   }
643
644   if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
645   {
646     SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
647   }
648
649   mRelayoutData->preferredSize = size;
650   mUseAnimatedSize             = AnimatedSizeFlag::CLEAR;
651   RelayoutRequest();
652 }
653
654 Vector2 ActorSizer::GetPreferredSize() const
655 {
656   return mRelayoutData ? Vector2(mRelayoutData->preferredSize) : Relayouter::DEFAULT_PREFERRED_SIZE;
657 }
658
659 void ActorSizer::SetMinimumSize(float size, Dimension::Type dimension)
660 {
661   EnsureRelayouter().SetMinimumSize(size, dimension);
662   RelayoutRequest();
663 }
664
665 float ActorSizer::GetMinimumSize(Dimension::Type dimension) const
666 {
667   return mRelayoutData ? mRelayoutData->GetMinimumSize(dimension) : 0.0f;
668 }
669
670 void ActorSizer::SetMaximumSize(float size, Dimension::Type dimension)
671 {
672   EnsureRelayouter().SetMaximumSize(size, dimension);
673   RelayoutRequest();
674 }
675
676 float ActorSizer::GetMaximumSize(Dimension::Type dimension) const
677 {
678   return mRelayoutData ? mRelayoutData->GetMaximumSize(dimension) : FLT_MAX;
679 }
680
681 void ActorSizer::OnAnimateSize(Animation& animation, Vector3 targetSize, bool relative)
682 {
683   mTargetSize      = targetSize + mTargetSize * float(relative);
684   mAnimatedSize    = mTargetSize;
685   mUseAnimatedSize = AnimatedSizeFlag::WIDTH | AnimatedSizeFlag::HEIGHT | AnimatedSizeFlag::DEPTH;
686
687   if(mRelayoutData && !mRelayoutData->relayoutRequested)
688   {
689     mRelayoutData->preferredSize.width  = mAnimatedSize.width;
690     mRelayoutData->preferredSize.height = mAnimatedSize.height;
691   }
692
693   // Notify deriving classes
694   mOwner.OnSizeAnimation(animation, mTargetSize);
695 }
696
697 void ActorSizer::OnAnimateWidth(Animation& animation, float width, bool relative)
698 {
699   mTargetSize.width   = width + float(relative) * mTargetSize.width;
700   mAnimatedSize.width = mTargetSize.width;
701   mUseAnimatedSize |= AnimatedSizeFlag::WIDTH;
702
703   if(mRelayoutData && !mRelayoutData->relayoutRequested)
704   {
705     mRelayoutData->preferredSize.width = mAnimatedSize.width;
706   }
707
708   // Notify deriving classes
709   mOwner.OnSizeAnimation(animation, mTargetSize);
710 }
711
712 void ActorSizer::OnAnimateHeight(Animation& animation, float height, bool relative)
713 {
714   mTargetSize.height   = height + float(relative) * mTargetSize.height;
715   mAnimatedSize.height = mTargetSize.height;
716   mUseAnimatedSize |= AnimatedSizeFlag::HEIGHT;
717
718   if(mRelayoutData && !mRelayoutData->relayoutRequested)
719   {
720     mRelayoutData->preferredSize.height = mAnimatedSize.height;
721   }
722
723   // Notify deriving classes
724   mOwner.OnSizeAnimation(animation, mTargetSize);
725 }
726
727 void ActorSizer::OnAnimateDepth(Animation& animation, float depth, bool relative)
728 {
729   mTargetSize.depth   = depth + float(relative) * mTargetSize.depth;
730   mAnimatedSize.depth = mTargetSize.depth;
731   mUseAnimatedSize |= AnimatedSizeFlag::DEPTH;
732
733   // Notify deriving classes
734   mOwner.OnSizeAnimation(animation, mTargetSize);
735 }
736
737 /**
738  * @brief Extract a given dimension from a Vector3
739  *
740  * @param[in] values The values to extract from
741  * @param[in] dimension The dimension to extract
742  * @return Return the value for the dimension
743  */
744 float ActorSizer::GetDimensionValue(const Vector3& values, const Dimension::Type dimension) const
745 {
746   return ::GetDimensionValue(values.GetVectorXY(), dimension);
747 }
748
749 float ActorSizer::ClampDimension(float size, Dimension::Type dimension) const
750 {
751   const float minSize = GetMinimumSize(dimension);
752   const float maxSize = GetMaximumSize(dimension);
753
754   return std::max(minSize, std::min(size, maxSize));
755 }
756
757 void ActorSizer::NegotiateDimension(Dimension::Type dimension, const Vector2& allocatedSize, ActorDimensionStack& recursionStack)
758 {
759   // Check if it needs to be negotiated
760   if(IsLayoutDirty(dimension) && !IsLayoutNegotiated(dimension))
761   {
762     // Check that we havn't gotten into an infinite loop
763     ActorDimensionPair searchActor    = ActorDimensionPair(&mOwner, dimension);
764     bool               recursionFound = false;
765     for(auto& element : recursionStack)
766     {
767       if(element == searchActor)
768       {
769         recursionFound = true;
770         break;
771       }
772     }
773
774     if(!recursionFound)
775     {
776       // Record the path that we have taken
777       recursionStack.emplace_back(&mOwner, dimension);
778
779       // Dimension dependency check
780       for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
781       {
782         Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
783
784         if(RelayoutDependentOnDimension(dimension, dimensionToCheck))
785         {
786           NegotiateDimension(dimensionToCheck, allocatedSize, recursionStack);
787         }
788       }
789
790       // Parent dependency check
791       Actor* parent = mOwner.GetParent();
792       if(parent && RelayoutDependentOnParent(dimension))
793       {
794         parent->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
795       }
796
797       // Children dependency check
798       if(mOwner.RelayoutDependentOnChildren(dimension))
799       {
800         for(uint32_t i = 0, count = mOwner.GetChildCount(); i < count; ++i)
801         {
802           ActorPtr child = mOwner.GetChildAt(i);
803
804           // Only relayout child first if it is not dependent on this actor
805           if(!child->RelayoutDependentOnParent(dimension))
806           {
807             child->mSizer.NegotiateDimension(dimension, allocatedSize, recursionStack);
808           }
809         }
810       }
811
812       // For deriving classes
813       mOwner.OnCalculateRelayoutSize(dimension);
814
815       // All dependencies checked, calculate the size and set negotiated flag
816       const float newSize = ClampDimension(CalculateSize(dimension, allocatedSize), dimension);
817
818       SetNegotiatedDimension(newSize, dimension);
819       SetLayoutNegotiated(true, dimension);
820
821       // For deriving classes
822       mOwner.OnLayoutNegotiated(newSize, dimension);
823
824       // This actor has been successfully processed, pop it off the recursion stack
825       recursionStack.pop_back();
826     }
827     else
828     {
829       // TODO: Break infinite loop
830       SetLayoutNegotiated(true, dimension);
831     }
832   }
833 }
834
835 void ActorSizer::NegotiateDimensions(const Vector2& allocatedSize)
836 {
837   // Negotiate all dimensions that require it
838   gRecursionStack.clear();
839
840   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
841   {
842     const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
843
844     // Negotiate
845     NegotiateDimension(dimension, allocatedSize, gRecursionStack);
846   }
847 }
848
849 float ActorSizer::CalculateSize(Dimension::Type dimension, const Vector2& maximumSize)
850 {
851   switch(GetResizePolicy(dimension))
852   {
853     case ResizePolicy::USE_NATURAL_SIZE:
854     {
855       return GetNaturalSize(dimension);
856     }
857
858     case ResizePolicy::FIXED:
859     {
860       return ::GetDimensionValue(GetPreferredSize(), dimension);
861     }
862
863     case ResizePolicy::USE_ASSIGNED_SIZE:
864     {
865       return ::GetDimensionValue(maximumSize, dimension);
866     }
867
868     case ResizePolicy::FILL_TO_PARENT:
869     case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
870     case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
871     {
872       return NegotiateFromParent(dimension);
873     }
874
875     case ResizePolicy::FIT_TO_CHILDREN:
876     {
877       return NegotiateFromChildren(dimension);
878     }
879
880     case ResizePolicy::DIMENSION_DEPENDENCY:
881     {
882       const Dimension::Type dimensionDependency = GetDimensionDependency(dimension);
883
884       // Custom rules
885       if(dimension == Dimension::WIDTH && dimensionDependency == Dimension::HEIGHT)
886       {
887         // Use actor API to allow deriving actors to layout their content
888         return mOwner.GetWidthForHeight(GetNegotiatedDimension(Dimension::HEIGHT));
889       }
890
891       if(dimension == Dimension::HEIGHT && dimensionDependency == Dimension::WIDTH)
892       {
893         // Use actor API to allow deriving actors to layout their content
894         return mOwner.GetHeightForWidth(GetNegotiatedDimension(Dimension::WIDTH));
895       }
896
897       break;
898     }
899
900     default:
901     {
902       break;
903     }
904   }
905
906   return 0.0f; // Default
907 }
908
909 } // namespace Dali::Internal