2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/event/actors/actor-relayouter.h>
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>
28 #if defined(DEBUG_ENABLED)
29 Debug::Filter* gLogRelayoutFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_TIMER" );
31 } // unnamed namespace
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 )
46 // Set size negotiation defaults
47 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
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;
61 ResizePolicy::Type Actor::Relayouter::GetResizePolicy( Dimension::Type dimension ) const
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 )
66 if( ( dimension & ( 1 << i ) ) )
68 if( useAssignedSize[ i ] )
70 return ResizePolicy::USE_ASSIGNED_SIZE;
74 return resizePolicies[ i ];
79 return ResizePolicy::DEFAULT;
82 void Actor::Relayouter::SetPadding( const Vector2& padding, Dimension::Type dimension )
84 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
86 if( dimension & ( 1 << i ) )
88 dimensionPadding[ i ] = padding;
93 void Actor::Relayouter::SetLayoutNegotiated( bool negotiated, Dimension::Type dimension )
95 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
97 if( dimension & ( 1 << i ) )
99 dimensionNegotiated[ i ] = negotiated;
104 bool Actor::Relayouter::IsLayoutNegotiated( Dimension::Type dimension ) const
106 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
108 if( ( dimension & ( 1 << i ) ) && dimensionNegotiated[ i ] )
116 Vector2 Actor::Relayouter::ApplySizeSetPolicy( Internal::Actor& actor, const Vector2& size )
118 switch( sizeSetPolicy )
120 case SizeScalePolicy::USE_SIZE_SET:
125 case SizeScalePolicy::FIT_WITH_ASPECT_RATIO:
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 )
131 const float sizeRatio = size.width / size.height;
132 const float naturalSizeRatio = naturalSize.width / naturalSize.height;
134 if( naturalSizeRatio < sizeRatio )
136 return Vector2( naturalSizeRatio * size.height, size.height );
138 else if( naturalSizeRatio > sizeRatio )
140 return Vector2( size.width, size.width / naturalSizeRatio );
151 case SizeScalePolicy::FILL_WITH_ASPECT_RATIO:
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 )
157 const float sizeRatio = size.width / size.height;
158 const float naturalSizeRatio = naturalSize.width / naturalSize.height;
160 if( naturalSizeRatio < sizeRatio )
162 return Vector2( size.width, size.width / naturalSizeRatio );
164 else if( naturalSizeRatio > sizeRatio )
166 return Vector2( naturalSizeRatio * size.height, size.height );
185 void Actor::Relayouter::SetUseAssignedSize( bool use, Dimension::Type dimension )
187 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
189 if( dimension & ( 1 << i ) )
191 useAssignedSize[ i ] = use;
196 bool Actor::Relayouter::GetUseAssignedSize( Dimension::Type dimension ) const
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 )
201 if( dimension & ( 1 << i ) )
203 return useAssignedSize[ i ];
210 void Actor::Relayouter::SetMinimumSize( float size, Dimension::Type dimension )
212 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
214 if( dimension & ( 1 << i ) )
216 minimumSize[ i ] = size;
221 float Actor::Relayouter::GetMinimumSize( Dimension::Type dimension ) const
223 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
225 if( dimension & ( 1 << i ) )
227 return minimumSize[ i ];
231 return 0.0f; // Default
234 void Actor::Relayouter::SetMaximumSize( float size, Dimension::Type dimension )
236 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
238 if( dimension & ( 1 << i ) )
240 maximumSize[ i ] = size;
245 float Actor::Relayouter::GetMaximumSize( Dimension::Type dimension ) const
247 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
249 if( dimension & ( 1 << i ) )
251 return maximumSize[ i ];
255 return FLT_MAX; // Default
258 void Actor::Relayouter::SetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension, Vector3& targetSize )
260 ResizePolicy::Type originalWidthPolicy = GetResizePolicy(Dimension::WIDTH);
261 ResizePolicy::Type originalHeightPolicy = GetResizePolicy(Dimension::HEIGHT);
263 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
265 if( dimension & ( 1 << i ) )
267 if ( policy == ResizePolicy::USE_ASSIGNED_SIZE )
269 useAssignedSize[ i ] = true;
273 resizePolicies[ i ] = policy;
274 useAssignedSize[ i ] = false;
279 if( policy == ResizePolicy::DIMENSION_DEPENDENCY )
281 if( dimension & Dimension::WIDTH )
283 SetDimensionDependency( Dimension::WIDTH, Dimension::HEIGHT );
286 if( dimension & Dimension::HEIGHT )
288 SetDimensionDependency( Dimension::HEIGHT, Dimension::WIDTH );
292 // If calling SetResizePolicy, assume we want relayout enabled
293 relayoutEnabled = true;
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.
299 if( dimension & Dimension::WIDTH )
301 if( originalWidthPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED )
303 preferredSize.width = targetSize.width;
305 else if( originalWidthPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED )
307 targetSize.width = preferredSize.width;
311 if( dimension & Dimension::HEIGHT )
313 if( originalHeightPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED )
315 preferredSize.height = targetSize.height;
317 else if( originalHeightPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED )
319 targetSize.height = preferredSize.height;
324 void Actor::Relayouter::SetDimensionDependency( Dimension::Type dimension, Dimension::Type dependency )
326 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
328 if( dimension & ( 1 << i ) )
330 dimensionDependencies[ i ] = dependency;
335 Dimension::Type Actor::Relayouter::GetDimensionDependency( Dimension::Type dimension ) const
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 )
340 if( ( dimension & ( 1 << i ) ) )
342 return dimensionDependencies[ i ];
346 return Dimension::ALL_DIMENSIONS; // Default
349 void Actor::Relayouter::SetLayoutDirty( bool dirty, Dimension::Type dimension )
351 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
353 if( dimension & ( 1 << i ) )
355 dimensionDirty[ i ] = dirty;
360 bool Actor::Relayouter::IsLayoutDirty( Dimension::Type dimension ) const
362 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
364 if( ( dimension & ( 1 << i ) ) && dimensionDirty[ i ] )
373 void Actor::Relayouter::SetPreferredSize( Actor& actor, const Vector2& size )
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
379 if( size.width > 0.0f || GetResizePolicy( Dimension::WIDTH ) == ResizePolicy::DEFAULT )
381 actor.SetResizePolicy( ResizePolicy::FIXED, Dimension::WIDTH );
384 if( size.height > 0.0f || GetResizePolicy( Dimension::HEIGHT ) == ResizePolicy::DEFAULT )
386 actor.SetResizePolicy( ResizePolicy::FIXED, Dimension::HEIGHT );
389 actor.mRelayoutData->preferredSize = size;
391 actor.mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
393 actor.RelayoutRequest();
396 float Actor::Relayouter::ClampDimension( const Internal::Actor& actor, float size, Dimension::Type dimension )
398 const float minSize = actor.GetMinimumSize( dimension );
399 const float maxSize = actor.GetMaximumSize( dimension );
401 return std::max( minSize, std::min( size, maxSize ) );
404 void Actor::Relayouter::NegotiateDimension( Actor& actor, Dimension::Type dimension, const Vector2& allocatedSize, Actor::ActorDimensionStack& recursionStack )
406 // Check if it needs to be negotiated
407 if( actor.IsLayoutDirty( dimension ) && !actor.IsLayoutNegotiated( dimension ) )
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 )
414 if( element == searchActor )
416 recursionFound = true;
421 if( !recursionFound )
423 // Record the path that we have taken
424 recursionStack.push_back( Actor::ActorDimensionPair( &actor, dimension ) );
426 // Dimension dependency check
427 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
429 Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
431 if( actor.RelayoutDependentOnDimension( dimension, dimensionToCheck ) )
433 NegotiateDimension( actor, dimensionToCheck, allocatedSize, recursionStack );
437 // Parent dependency check
438 Actor* parent = actor.GetParent();
439 if( parent && actor.RelayoutDependentOnParent( dimension ) )
441 NegotiateDimension( *parent, dimension, allocatedSize, recursionStack );
444 // Children dependency check
445 if( actor.RelayoutDependentOnChildren( dimension ) )
447 for( uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i )
449 ActorPtr child = actor.GetChildAt( i );
451 // Only relayout child first if it is not dependent on this actor
452 if( !child->RelayoutDependentOnParent( dimension ) )
454 NegotiateDimension( *child, dimension, allocatedSize, recursionStack );
459 // For deriving classes
460 actor.OnCalculateRelayoutSize( dimension );
462 // All dependencies checked, calculate the size and set negotiated flag
463 const float newSize = ClampDimension( actor, actor.CalculateSize( dimension, allocatedSize ), dimension );
465 actor.SetNegotiatedDimension( newSize, dimension );
466 actor.SetLayoutNegotiated( true, dimension );
468 // For deriving classes
469 actor.OnLayoutNegotiated( newSize, dimension );
471 // This actor has been successfully processed, pop it off the recursion stack
472 recursionStack.pop_back();
476 // TODO: Break infinite loop
477 actor.SetLayoutNegotiated( true, dimension );
482 void Actor::Relayouter::NegotiateDimensions(Actor& actor, const Vector2& allocatedSize)
484 // Negotiate all dimensions that require it
485 ActorDimensionStack recursionStack;
487 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
489 const Dimension::Type dimension = static_cast< Dimension::Type >( 1 << i );
492 NegotiateDimension(actor, dimension, allocatedSize, recursionStack);
496 void Actor::Relayouter::NegotiateSize(Actor& actor, const Vector2& allocatedSize, RelayoutContainer& container)
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 );
507 if( actor.GetUseAssignedSize(Dimension::WIDTH ) )
509 actor.SetLayoutNegotiated( false, Dimension::WIDTH );
511 if( actor.GetUseAssignedSize( Dimension::HEIGHT ) )
513 actor.SetLayoutNegotiated( false, Dimension::HEIGHT );
516 // Do the negotiation
517 NegotiateDimensions(actor, allocatedSize);
519 // Set the actor size
520 actor.SetNegotiatedSize( container );
522 // Negotiate down to children
523 for( uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i )
525 ActorPtr child = actor.GetChildAt( i );
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) )
531 child->SetLayoutNegotiated(false, Dimension::WIDTH);
532 child->SetLayoutDirty(true, Dimension::WIDTH);
535 if( child->GetUseAssignedSize(Dimension::HEIGHT) )
537 child->SetLayoutNegotiated(false, Dimension::HEIGHT);
538 child->SetLayoutDirty(true, Dimension::HEIGHT);
541 // Only relayout if required
542 if( child->RelayoutRequired() )
544 container.Add( Dali::Actor( child.Get() ), actor.mTargetSize.GetVectorXY() );
547 DALI_LOG_TIMER_END( NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
550 } // namespace Internal