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