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