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