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