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),
45 // Set size negotiation defaults
46 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
48 resizePolicies[i] = ResizePolicy::DEFAULT;
49 useAssignedSize[i] = false;
50 negotiatedDimensions[i] = 0.0f;
51 dimensionNegotiated[i] = false;
52 dimensionDirty[i] = false;
53 dimensionDependencies[i] = Dimension::ALL_DIMENSIONS;
54 dimensionPadding[i] = DEFAULT_DIMENSION_PADDING;
55 minimumSize[i] = 0.0f;
56 maximumSize[i] = FLT_MAX;
60 ResizePolicy::Type Actor::Relayouter::GetResizePolicy(Dimension::Type dimension) const
62 // If more than one dimension is requested, just return the first one found
63 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
65 if((dimension & (1 << i)))
67 if(useAssignedSize[i])
69 return ResizePolicy::USE_ASSIGNED_SIZE;
73 return resizePolicies[i];
78 return ResizePolicy::DEFAULT;
81 void Actor::Relayouter::SetPadding(const Vector2& padding, Dimension::Type dimension)
83 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
85 if(dimension & (1 << i))
87 dimensionPadding[i] = padding;
92 void Actor::Relayouter::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
94 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
96 if(dimension & (1 << i))
98 dimensionNegotiated[i] = negotiated;
103 bool Actor::Relayouter::IsLayoutNegotiated(Dimension::Type dimension) const
105 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
107 if((dimension & (1 << i)) && dimensionNegotiated[i])
115 Vector2 Actor::Relayouter::ApplySizeSetPolicy(Internal::Actor& actor, const Vector2& size)
117 switch(sizeSetPolicy)
119 case SizeScalePolicy::USE_SIZE_SET:
124 case SizeScalePolicy::FIT_WITH_ASPECT_RATIO:
126 // Scale size to fit within the original size bounds, keeping the natural size aspect ratio
127 const Vector3 naturalSize = actor.GetNaturalSize();
128 if(naturalSize.width > 0.0f && naturalSize.height > 0.0f && size.width > 0.0f && size.height > 0.0f)
130 const float sizeRatio = size.width / size.height;
131 const float naturalSizeRatio = naturalSize.width / naturalSize.height;
133 if(naturalSizeRatio < sizeRatio)
135 return Vector2(naturalSizeRatio * size.height, size.height);
137 else if(naturalSizeRatio > sizeRatio)
139 return Vector2(size.width, size.width / naturalSizeRatio);
150 case SizeScalePolicy::FILL_WITH_ASPECT_RATIO:
152 // Scale size to fill the original size bounds, keeping the natural size aspect ratio. Potentially exceeding the original bounds.
153 const Vector3 naturalSize = actor.GetNaturalSize();
154 if(naturalSize.width > 0.0f && naturalSize.height > 0.0f && size.width > 0.0f && size.height > 0.0f)
156 const float sizeRatio = size.width / size.height;
157 const float naturalSizeRatio = naturalSize.width / naturalSize.height;
159 if(naturalSizeRatio < sizeRatio)
161 return Vector2(size.width, size.width / naturalSizeRatio);
163 else if(naturalSizeRatio > sizeRatio)
165 return Vector2(naturalSizeRatio * size.height, size.height);
184 void Actor::Relayouter::SetUseAssignedSize(bool use, Dimension::Type dimension)
186 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
188 if(dimension & (1 << i))
190 useAssignedSize[i] = use;
195 bool Actor::Relayouter::GetUseAssignedSize(Dimension::Type dimension) const
197 // If more than one dimension is requested, just return the first one found
198 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
200 if(dimension & (1 << i))
202 return useAssignedSize[i];
209 void Actor::Relayouter::SetMinimumSize(float size, Dimension::Type dimension)
211 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
213 if(dimension & (1 << i))
215 minimumSize[i] = size;
220 float Actor::Relayouter::GetMinimumSize(Dimension::Type dimension) const
222 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
224 if(dimension & (1 << i))
226 return minimumSize[i];
230 return 0.0f; // Default
233 void Actor::Relayouter::SetMaximumSize(float size, Dimension::Type dimension)
235 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
237 if(dimension & (1 << i))
239 maximumSize[i] = size;
244 float Actor::Relayouter::GetMaximumSize(Dimension::Type dimension) const
246 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
248 if(dimension & (1 << i))
250 return maximumSize[i];
254 return FLT_MAX; // Default
257 void Actor::Relayouter::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension, Vector3& targetSize)
259 ResizePolicy::Type originalWidthPolicy = GetResizePolicy(Dimension::WIDTH);
260 ResizePolicy::Type originalHeightPolicy = GetResizePolicy(Dimension::HEIGHT);
262 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
264 if(dimension & (1 << i))
266 if(policy == ResizePolicy::USE_ASSIGNED_SIZE)
268 useAssignedSize[i] = true;
272 resizePolicies[i] = policy;
273 useAssignedSize[i] = false;
278 if(policy == ResizePolicy::DIMENSION_DEPENDENCY)
280 if(dimension & Dimension::WIDTH)
282 SetDimensionDependency(Dimension::WIDTH, Dimension::HEIGHT);
285 if(dimension & Dimension::HEIGHT)
287 SetDimensionDependency(Dimension::HEIGHT, Dimension::WIDTH);
291 // If calling SetResizePolicy, assume we want relayout enabled
292 relayoutEnabled = true;
294 // If the resize policy is set to be FIXED, the preferred size
295 // should be overrided by the target size. Otherwise the target
296 // size should be overrided by the preferred size.
298 if(dimension & Dimension::WIDTH)
300 if(originalWidthPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED)
302 preferredSize.width = targetSize.width;
304 else if(originalWidthPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED)
306 targetSize.width = preferredSize.width;
310 if(dimension & Dimension::HEIGHT)
312 if(originalHeightPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED)
314 preferredSize.height = targetSize.height;
316 else if(originalHeightPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED)
318 targetSize.height = preferredSize.height;
323 void Actor::Relayouter::SetDimensionDependency(Dimension::Type dimension, Dimension::Type dependency)
325 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
327 if(dimension & (1 << i))
329 dimensionDependencies[i] = dependency;
334 Dimension::Type Actor::Relayouter::GetDimensionDependency(Dimension::Type dimension) const
336 // If more than one dimension is requested, just return the first one found
337 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
339 if((dimension & (1 << i)))
341 return dimensionDependencies[i];
345 return Dimension::ALL_DIMENSIONS; // Default
348 void Actor::Relayouter::SetLayoutDirty(bool dirty, Dimension::Type dimension)
350 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
352 if(dimension & (1 << i))
354 dimensionDirty[i] = dirty;
359 bool Actor::Relayouter::IsLayoutDirty(Dimension::Type dimension) const
361 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
363 if((dimension & (1 << i)) && dimensionDirty[i])
372 void Actor::Relayouter::SetPreferredSize(Actor& actor, const Vector2& size)
374 // If valid width or height, then set the resize policy to FIXED
375 // 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,
376 // then change to FIXED as well
378 if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
380 actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
383 if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
385 actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
388 actor.mRelayoutData->preferredSize = size;
390 actor.mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
392 actor.RelayoutRequest();
395 float Actor::Relayouter::ClampDimension(const Internal::Actor& actor, float size, Dimension::Type dimension)
397 const float minSize = actor.GetMinimumSize(dimension);
398 const float maxSize = actor.GetMaximumSize(dimension);
400 return std::max(minSize, std::min(size, maxSize));
403 void Actor::Relayouter::NegotiateDimension(Actor& actor, Dimension::Type dimension, const Vector2& allocatedSize, Actor::ActorDimensionStack& recursionStack)
405 // Check if it needs to be negotiated
406 if(actor.IsLayoutDirty(dimension) && !actor.IsLayoutNegotiated(dimension))
408 // Check that we havn't gotten into an infinite loop
409 Actor::ActorDimensionPair searchActor = Actor::ActorDimensionPair(&actor, dimension);
410 bool recursionFound = false;
411 for(auto& element : recursionStack)
413 if(element == searchActor)
415 recursionFound = true;
422 // Record the path that we have taken
423 recursionStack.push_back(Actor::ActorDimensionPair(&actor, dimension));
425 // Dimension dependency check
426 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
428 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
430 if(actor.RelayoutDependentOnDimension(dimension, dimensionToCheck))
432 NegotiateDimension(actor, dimensionToCheck, allocatedSize, recursionStack);
436 // Parent dependency check
437 Actor* parent = actor.GetParent();
438 if(parent && actor.RelayoutDependentOnParent(dimension))
440 NegotiateDimension(*parent, dimension, allocatedSize, recursionStack);
443 // Children dependency check
444 if(actor.RelayoutDependentOnChildren(dimension))
446 for(uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i)
448 ActorPtr child = actor.GetChildAt(i);
450 // Only relayout child first if it is not dependent on this actor
451 if(!child->RelayoutDependentOnParent(dimension))
453 NegotiateDimension(*child, dimension, allocatedSize, recursionStack);
458 // For deriving classes
459 actor.OnCalculateRelayoutSize(dimension);
461 // All dependencies checked, calculate the size and set negotiated flag
462 const float newSize = ClampDimension(actor, actor.CalculateSize(dimension, allocatedSize), dimension);
464 actor.SetNegotiatedDimension(newSize, dimension);
465 actor.SetLayoutNegotiated(true, dimension);
467 // For deriving classes
468 actor.OnLayoutNegotiated(newSize, dimension);
470 // This actor has been successfully processed, pop it off the recursion stack
471 recursionStack.pop_back();
475 // TODO: Break infinite loop
476 actor.SetLayoutNegotiated(true, dimension);
481 void Actor::Relayouter::NegotiateDimensions(Actor& actor, const Vector2& allocatedSize)
483 // Negotiate all dimensions that require it
484 ActorDimensionStack recursionStack;
486 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
488 const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
491 NegotiateDimension(actor, dimension, allocatedSize, recursionStack);
495 void Actor::Relayouter::NegotiateSize(Actor& actor, const Vector2& allocatedSize, RelayoutContainer& container)
497 // Force a size negotiation for actors that has assigned size during relayout
498 // This is required as otherwise the flags that force a relayout will not
499 // necessarilly be set. This will occur if the actor has already been laid out.
500 // The dirty flags are then cleared. Then if the actor is added back into the
501 // relayout container afterwards, the dirty flags would still be clear...
502 // causing a relayout to be skipped. Here we force any actors added to the
503 // container to be relayed out.
504 DALI_LOG_TIMER_START(NegSizeTimer1);
506 if(actor.GetUseAssignedSize(Dimension::WIDTH))
508 actor.SetLayoutNegotiated(false, Dimension::WIDTH);
510 if(actor.GetUseAssignedSize(Dimension::HEIGHT))
512 actor.SetLayoutNegotiated(false, Dimension::HEIGHT);
515 // Do the negotiation
516 NegotiateDimensions(actor, allocatedSize);
518 // Set the actor size
519 actor.SetNegotiatedSize(container);
521 // Negotiate down to children
522 for(uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i)
524 ActorPtr child = actor.GetChildAt(i);
526 // Forces children that have already been laid out to be relayed out
527 // if they have assigned size during relayout.
528 if(child->GetUseAssignedSize(Dimension::WIDTH))
530 child->SetLayoutNegotiated(false, Dimension::WIDTH);
531 child->SetLayoutDirty(true, Dimension::WIDTH);
534 if(child->GetUseAssignedSize(Dimension::HEIGHT))
536 child->SetLayoutNegotiated(false, Dimension::HEIGHT);
537 child->SetLayoutDirty(true, Dimension::HEIGHT);
540 // Only relayout if required
541 if(child->RelayoutRequired())
543 container.Add(Dali::Actor(child.Get()), actor.mTargetSize.GetVectorXY());
546 DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
549 } // namespace Internal