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