Moved more actor methods into relayouter
[platform/core/uifw/dali-core.git] / dali / internal / event / actors / actor-relayouter.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
18 // CLASS HEADER
19 #include <dali/internal/event/actors/actor-relayouter.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/internal/event/size-negotiation/relayout-controller-impl.h>
24 #include <dali/public-api/math/vector2.h>
25 #include <dali/public-api/math/vector3.h>
26
27 namespace
28 {
29 #if defined(DEBUG_ENABLED)
30 Debug::Filter* gLogRelayoutFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_TIMER");
31 #endif
32 } // unnamed namespace
33
34 namespace Dali
35 {
36 namespace Internal
37 {
38 Actor::Relayouter::Relayouter()
39 : sizeModeFactor(DEFAULT_SIZE_MODE_FACTOR),
40   preferredSize(DEFAULT_PREFERRED_SIZE),
41   sizeSetPolicy(DEFAULT_SIZE_SCALE_POLICY),
42   relayoutEnabled(false),
43   insideRelayout(false),
44   relayoutRequested(false)
45 {
46   // Set size negotiation defaults
47   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
48   {
49     resizePolicies[i]        = ResizePolicy::DEFAULT;
50     useAssignedSize[i]       = false;
51     negotiatedDimensions[i]  = 0.0f;
52     dimensionNegotiated[i]   = false;
53     dimensionDirty[i]        = false;
54     dimensionDependencies[i] = Dimension::ALL_DIMENSIONS;
55     dimensionPadding[i]      = DEFAULT_DIMENSION_PADDING;
56     minimumSize[i]           = 0.0f;
57     maximumSize[i]           = FLT_MAX;
58   }
59 }
60
61 ResizePolicy::Type Actor::Relayouter::GetResizePolicy(Dimension::Type dimension) const
62 {
63   // If more than one dimension is requested, just return the first one found
64   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
65   {
66     if((dimension & (1 << i)))
67     {
68       if(useAssignedSize[i])
69       {
70         return ResizePolicy::USE_ASSIGNED_SIZE;
71       }
72       else
73       {
74         return resizePolicies[i];
75       }
76     }
77   }
78
79   return ResizePolicy::DEFAULT;
80 }
81
82 void Actor::Relayouter::SetPadding(const Vector2& padding, Dimension::Type dimension)
83 {
84   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
85   {
86     if(dimension & (1 << i))
87     {
88       dimensionPadding[i] = padding;
89     }
90   }
91 }
92
93 Vector2 Actor::Relayouter::GetPadding(Dimension::Type dimension)
94 {
95   // If more than one dimension is requested, just return the first one found
96   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
97   {
98     if((dimension & (1 << i)))
99     {
100       return dimensionPadding[i];
101     }
102   }
103
104   return DEFAULT_DIMENSION_PADDING;
105 }
106
107 void Actor::Relayouter::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
108 {
109   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
110   {
111     if(dimension & (1 << i))
112     {
113       dimensionNegotiated[i] = negotiated;
114     }
115   }
116 }
117
118 bool Actor::Relayouter::IsLayoutNegotiated(Dimension::Type dimension) const
119 {
120   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
121   {
122     if((dimension & (1 << i)) && dimensionNegotiated[i])
123     {
124       return true;
125     }
126   }
127   return false;
128 }
129
130 Vector2 Actor::Relayouter::ApplySizeSetPolicy(Internal::Actor& actor, const Vector2& size)
131 {
132   switch(sizeSetPolicy)
133   {
134     case SizeScalePolicy::USE_SIZE_SET:
135     {
136       return size;
137     }
138
139     case SizeScalePolicy::FIT_WITH_ASPECT_RATIO:
140     {
141       // Scale size to fit within the original size bounds, keeping the natural size aspect ratio
142       const Vector3 naturalSize = actor.GetNaturalSize();
143       if(naturalSize.width > 0.0f && naturalSize.height > 0.0f && size.width > 0.0f && size.height > 0.0f)
144       {
145         const float sizeRatio        = size.width / size.height;
146         const float naturalSizeRatio = naturalSize.width / naturalSize.height;
147
148         if(naturalSizeRatio < sizeRatio)
149         {
150           return Vector2(naturalSizeRatio * size.height, size.height);
151         }
152         else if(naturalSizeRatio > sizeRatio)
153         {
154           return Vector2(size.width, size.width / naturalSizeRatio);
155         }
156         else
157         {
158           return size;
159         }
160       }
161
162       break;
163     }
164
165     case SizeScalePolicy::FILL_WITH_ASPECT_RATIO:
166     {
167       // Scale size to fill the original size bounds, keeping the natural size aspect ratio. Potentially exceeding the original bounds.
168       const Vector3 naturalSize = actor.GetNaturalSize();
169       if(naturalSize.width > 0.0f && naturalSize.height > 0.0f && size.width > 0.0f && size.height > 0.0f)
170       {
171         const float sizeRatio        = size.width / size.height;
172         const float naturalSizeRatio = naturalSize.width / naturalSize.height;
173
174         if(naturalSizeRatio < sizeRatio)
175         {
176           return Vector2(size.width, size.width / naturalSizeRatio);
177         }
178         else if(naturalSizeRatio > sizeRatio)
179         {
180           return Vector2(naturalSizeRatio * size.height, size.height);
181         }
182         else
183         {
184           return size;
185         }
186       }
187       break;
188     }
189
190     default:
191     {
192       break;
193     }
194   }
195
196   return size;
197 }
198
199 void Actor::Relayouter::SetUseAssignedSize(bool use, Dimension::Type dimension)
200 {
201   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
202   {
203     if(dimension & (1 << i))
204     {
205       useAssignedSize[i] = use;
206     }
207   }
208 }
209
210 bool Actor::Relayouter::GetUseAssignedSize(Dimension::Type dimension) const
211 {
212   // If more than one dimension is requested, just return the first one found
213   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
214   {
215     if(dimension & (1 << i))
216     {
217       return useAssignedSize[i];
218     }
219   }
220
221   return false;
222 }
223
224 void Actor::Relayouter::SetMinimumSize(float size, Dimension::Type dimension)
225 {
226   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
227   {
228     if(dimension & (1 << i))
229     {
230       minimumSize[i] = size;
231     }
232   }
233 }
234
235 float Actor::Relayouter::GetMinimumSize(Dimension::Type dimension) const
236 {
237   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
238   {
239     if(dimension & (1 << i))
240     {
241       return minimumSize[i];
242     }
243   }
244
245   return 0.0f; // Default
246 }
247
248 void Actor::Relayouter::SetMaximumSize(float size, Dimension::Type dimension)
249 {
250   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
251   {
252     if(dimension & (1 << i))
253     {
254       maximumSize[i] = size;
255     }
256   }
257 }
258
259 float Actor::Relayouter::GetMaximumSize(Dimension::Type dimension) const
260 {
261   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
262   {
263     if(dimension & (1 << i))
264     {
265       return maximumSize[i];
266     }
267   }
268
269   return FLT_MAX; // Default
270 }
271
272 void Actor::Relayouter::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension, Vector3& targetSize)
273 {
274   ResizePolicy::Type originalWidthPolicy  = GetResizePolicy(Dimension::WIDTH);
275   ResizePolicy::Type originalHeightPolicy = GetResizePolicy(Dimension::HEIGHT);
276
277   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
278   {
279     if(dimension & (1 << i))
280     {
281       if(policy == ResizePolicy::USE_ASSIGNED_SIZE)
282       {
283         useAssignedSize[i] = true;
284       }
285       else
286       {
287         resizePolicies[i]  = policy;
288         useAssignedSize[i] = false;
289       }
290     }
291   }
292
293   if(policy == ResizePolicy::DIMENSION_DEPENDENCY)
294   {
295     if(dimension & Dimension::WIDTH)
296     {
297       SetDimensionDependency(Dimension::WIDTH, Dimension::HEIGHT);
298     }
299
300     if(dimension & Dimension::HEIGHT)
301     {
302       SetDimensionDependency(Dimension::HEIGHT, Dimension::WIDTH);
303     }
304   }
305
306   // If calling SetResizePolicy, assume we want relayout enabled
307   relayoutEnabled = true;
308
309   // If the resize policy is set to be FIXED, the preferred size
310   // should be overrided by the target size. Otherwise the target
311   // size should be overrided by the preferred size.
312
313   if(dimension & Dimension::WIDTH)
314   {
315     if(originalWidthPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED)
316     {
317       preferredSize.width = targetSize.width;
318     }
319     else if(originalWidthPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED)
320     {
321       targetSize.width = preferredSize.width;
322     }
323   }
324
325   if(dimension & Dimension::HEIGHT)
326   {
327     if(originalHeightPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED)
328     {
329       preferredSize.height = targetSize.height;
330     }
331     else if(originalHeightPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED)
332     {
333       targetSize.height = preferredSize.height;
334     }
335   }
336 }
337
338 bool Actor::Relayouter::GetRelayoutDependentOnParent(Dimension::Type dimension)
339 {
340   // Check if actor is dependent on parent
341   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
342   {
343     if((dimension & (1 << i)))
344     {
345       const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
346       if(resizePolicy == ResizePolicy::FILL_TO_PARENT || resizePolicy == ResizePolicy::SIZE_RELATIVE_TO_PARENT || resizePolicy == ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT)
347       {
348         return true;
349       }
350     }
351   }
352   return false;
353 }
354
355 bool Actor::Relayouter::GetRelayoutDependentOnChildren(Dimension::Type dimension)
356 {
357   // Check if actor is dependent on children
358   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
359   {
360     if((dimension & (1 << i)))
361     {
362       const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
363       if(resizePolicy == ResizePolicy::FIT_TO_CHILDREN || resizePolicy == ResizePolicy::USE_NATURAL_SIZE)
364       {
365         return true;
366       }
367       break;
368     }
369   }
370
371   return false;
372 }
373
374 bool Actor::Relayouter::GetRelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependency)
375 {
376   // Check each possible dimension and see if it is dependent on the input one
377   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
378   {
379     if(dimension & (1 << i))
380     {
381       return resizePolicies[i] == ResizePolicy::DIMENSION_DEPENDENCY && dimensionDependencies[i] == dependency;
382     }
383   }
384
385   return false;
386 }
387
388 void Actor::Relayouter::SetDimensionDependency(Dimension::Type dimension, Dimension::Type dependency)
389 {
390   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
391   {
392     if(dimension & (1 << i))
393     {
394       dimensionDependencies[i] = dependency;
395     }
396   }
397 }
398
399 Dimension::Type Actor::Relayouter::GetDimensionDependency(Dimension::Type dimension) const
400 {
401   // If more than one dimension is requested, just return the first one found
402   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
403   {
404     if((dimension & (1 << i)))
405     {
406       return dimensionDependencies[i];
407     }
408   }
409
410   return Dimension::ALL_DIMENSIONS; // Default
411 }
412
413 void Actor::Relayouter::SetLayoutDirty(bool dirty, Dimension::Type dimension)
414 {
415   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
416   {
417     if(dimension & (1 << i))
418     {
419       dimensionDirty[i] = dirty;
420     }
421   }
422 }
423
424 bool Actor::Relayouter::IsLayoutDirty(Dimension::Type dimension) const
425 {
426   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
427   {
428     if((dimension & (1 << i)) && dimensionDirty[i])
429     {
430       return true;
431     }
432   }
433
434   return false;
435 }
436
437 void Actor::Relayouter::SetPreferredSize(Actor& actor, const Vector2& size)
438 {
439   // If valid width or height, then set the resize policy to FIXED
440   // 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,
441   // then change to FIXED as well
442
443   if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
444   {
445     actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
446   }
447
448   if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
449   {
450     actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
451   }
452
453   actor.mRelayoutData->preferredSize = size;
454
455   actor.mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
456
457   actor.RelayoutRequest();
458 }
459
460 float Actor::Relayouter::ClampDimension(const Internal::Actor& actor, float size, Dimension::Type dimension)
461 {
462   const float minSize = actor.GetMinimumSize(dimension);
463   const float maxSize = actor.GetMaximumSize(dimension);
464
465   return std::max(minSize, std::min(size, maxSize));
466 }
467
468 void Actor::Relayouter::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
469 {
470   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
471   {
472     if(dimension & (1 << i))
473     {
474       negotiatedDimensions[i] = negotiatedDimension;
475     }
476   }
477 }
478
479 float Actor::Relayouter::GetNegotiatedDimension(Dimension::Type dimension)
480 {
481   // If more than one dimension is requested, just return the first one found
482   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
483   {
484     if((dimension & (1 << i)))
485     {
486       return negotiatedDimensions[i];
487     }
488   }
489
490   return 0.0f; // Default
491 }
492
493 void Actor::Relayouter::NegotiateDimension(Actor& actor, Dimension::Type dimension, const Vector2& allocatedSize, Actor::ActorDimensionStack& recursionStack)
494 {
495   // Check if it needs to be negotiated
496   if(actor.IsLayoutDirty(dimension) && !actor.IsLayoutNegotiated(dimension))
497   {
498     // Check that we havn't gotten into an infinite loop
499     Actor::ActorDimensionPair searchActor    = Actor::ActorDimensionPair(&actor, dimension);
500     bool                      recursionFound = false;
501     for(auto& element : recursionStack)
502     {
503       if(element == searchActor)
504       {
505         recursionFound = true;
506         break;
507       }
508     }
509
510     if(!recursionFound)
511     {
512       // Record the path that we have taken
513       recursionStack.push_back(Actor::ActorDimensionPair(&actor, dimension));
514
515       // Dimension dependency check
516       for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
517       {
518         Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
519
520         if(actor.RelayoutDependentOnDimension(dimension, dimensionToCheck))
521         {
522           NegotiateDimension(actor, dimensionToCheck, allocatedSize, recursionStack);
523         }
524       }
525
526       // Parent dependency check
527       Actor* parent = actor.GetParent();
528       if(parent && actor.RelayoutDependentOnParent(dimension))
529       {
530         NegotiateDimension(*parent, dimension, allocatedSize, recursionStack);
531       }
532
533       // Children dependency check
534       if(actor.RelayoutDependentOnChildren(dimension))
535       {
536         for(uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i)
537         {
538           ActorPtr child = actor.GetChildAt(i);
539
540           // Only relayout child first if it is not dependent on this actor
541           if(!child->RelayoutDependentOnParent(dimension))
542           {
543             NegotiateDimension(*child, dimension, allocatedSize, recursionStack);
544           }
545         }
546       }
547
548       // For deriving classes
549       actor.OnCalculateRelayoutSize(dimension);
550
551       // All dependencies checked, calculate the size and set negotiated flag
552       const float newSize = ClampDimension(actor, actor.CalculateSize(dimension, allocatedSize), dimension);
553
554       actor.SetNegotiatedDimension(newSize, dimension);
555       actor.SetLayoutNegotiated(true, dimension);
556
557       // For deriving classes
558       actor.OnLayoutNegotiated(newSize, dimension);
559
560       // This actor has been successfully processed, pop it off the recursion stack
561       recursionStack.pop_back();
562     }
563     else
564     {
565       // TODO: Break infinite loop
566       actor.SetLayoutNegotiated(true, dimension);
567     }
568   }
569 }
570
571 void Actor::Relayouter::NegotiateDimensions(Actor& actor, const Vector2& allocatedSize)
572 {
573   // Negotiate all dimensions that require it
574   ActorDimensionStack recursionStack;
575
576   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
577   {
578     const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
579
580     // Negotiate
581     NegotiateDimension(actor, dimension, allocatedSize, recursionStack);
582   }
583 }
584
585 void Actor::Relayouter::NegotiateSize(Actor& actor, const Vector2& allocatedSize, RelayoutContainer& container)
586 {
587   // Force a size negotiation for actors that has assigned size during relayout
588   // This is required as otherwise the flags that force a relayout will not
589   // necessarilly be set. This will occur if the actor has already been laid out.
590   // The dirty flags are then cleared. Then if the actor is added back into the
591   // relayout container afterwards, the dirty flags would still be clear...
592   // causing a relayout to be skipped. Here we force any actors added to the
593   // container to be relayed out.
594   DALI_LOG_TIMER_START(NegSizeTimer1);
595
596   if(actor.GetUseAssignedSize(Dimension::WIDTH))
597   {
598     actor.SetLayoutNegotiated(false, Dimension::WIDTH);
599   }
600   if(actor.GetUseAssignedSize(Dimension::HEIGHT))
601   {
602     actor.SetLayoutNegotiated(false, Dimension::HEIGHT);
603   }
604
605   // Do the negotiation
606   NegotiateDimensions(actor, allocatedSize);
607
608   // Set the actor size
609   actor.SetNegotiatedSize(container);
610
611   // Negotiate down to children
612   for(uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i)
613   {
614     ActorPtr child = actor.GetChildAt(i);
615
616     // Forces children that have already been laid out to be relayed out
617     // if they have assigned size during relayout.
618     if(child->GetUseAssignedSize(Dimension::WIDTH))
619     {
620       child->SetLayoutNegotiated(false, Dimension::WIDTH);
621       child->SetLayoutDirty(true, Dimension::WIDTH);
622     }
623
624     if(child->GetUseAssignedSize(Dimension::HEIGHT))
625     {
626       child->SetLayoutNegotiated(false, Dimension::HEIGHT);
627       child->SetLayoutDirty(true, Dimension::HEIGHT);
628     }
629
630     // Only relayout if required
631     if(child->RelayoutRequired())
632     {
633       container.Add(Dali::Actor(child.Get()), actor.mTargetSize.GetVectorXY());
634     }
635   }
636   DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
637 }
638
639 } // namespace Internal
640
641 } // namespace Dali