2 * Copyright (c) 2021 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/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>
29 #if defined(DEBUG_ENABLED)
30 Debug::Filter* gLogRelayoutFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_TIMER");
32 } // unnamed namespace
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)
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;
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