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 Vector2 Actor::Relayouter::GetPadding(Dimension::Type dimension)
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)
98 if((dimension & (1 << i)))
100 return dimensionPadding[i];
104 return DEFAULT_DIMENSION_PADDING;
107 void Actor::Relayouter::SetLayoutNegotiated(bool negotiated, Dimension::Type dimension)
109 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
111 if(dimension & (1 << i))
113 dimensionNegotiated[i] = negotiated;
118 bool Actor::Relayouter::IsLayoutNegotiated(Dimension::Type dimension) const
120 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
122 if((dimension & (1 << i)) && dimensionNegotiated[i])
130 Vector2 Actor::Relayouter::ApplySizeSetPolicy(Internal::Actor& actor, const Vector2& size)
132 switch(sizeSetPolicy)
134 case SizeScalePolicy::USE_SIZE_SET:
139 case SizeScalePolicy::FIT_WITH_ASPECT_RATIO:
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)
145 const float sizeRatio = size.width / size.height;
146 const float naturalSizeRatio = naturalSize.width / naturalSize.height;
148 if(naturalSizeRatio < sizeRatio)
150 return Vector2(naturalSizeRatio * size.height, size.height);
152 else if(naturalSizeRatio > sizeRatio)
154 return Vector2(size.width, size.width / naturalSizeRatio);
165 case SizeScalePolicy::FILL_WITH_ASPECT_RATIO:
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)
171 const float sizeRatio = size.width / size.height;
172 const float naturalSizeRatio = naturalSize.width / naturalSize.height;
174 if(naturalSizeRatio < sizeRatio)
176 return Vector2(size.width, size.width / naturalSizeRatio);
178 else if(naturalSizeRatio > sizeRatio)
180 return Vector2(naturalSizeRatio * size.height, size.height);
199 void Actor::Relayouter::SetUseAssignedSize(bool use, Dimension::Type dimension)
201 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
203 if(dimension & (1 << i))
205 useAssignedSize[i] = use;
210 bool Actor::Relayouter::GetUseAssignedSize(Dimension::Type dimension) const
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)
215 if(dimension & (1 << i))
217 return useAssignedSize[i];
224 void Actor::Relayouter::SetMinimumSize(float size, Dimension::Type dimension)
226 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
228 if(dimension & (1 << i))
230 minimumSize[i] = size;
235 float Actor::Relayouter::GetMinimumSize(Dimension::Type dimension) const
237 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
239 if(dimension & (1 << i))
241 return minimumSize[i];
245 return 0.0f; // Default
248 void Actor::Relayouter::SetMaximumSize(float size, Dimension::Type dimension)
250 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
252 if(dimension & (1 << i))
254 maximumSize[i] = size;
259 float Actor::Relayouter::GetMaximumSize(Dimension::Type dimension) const
261 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
263 if(dimension & (1 << i))
265 return maximumSize[i];
269 return FLT_MAX; // Default
272 void Actor::Relayouter::SetResizePolicy(ResizePolicy::Type policy, Dimension::Type dimension, Vector3& targetSize)
274 ResizePolicy::Type originalWidthPolicy = GetResizePolicy(Dimension::WIDTH);
275 ResizePolicy::Type originalHeightPolicy = GetResizePolicy(Dimension::HEIGHT);
277 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
279 if(dimension & (1 << i))
281 if(policy == ResizePolicy::USE_ASSIGNED_SIZE)
283 useAssignedSize[i] = true;
287 resizePolicies[i] = policy;
288 useAssignedSize[i] = false;
293 if(policy == ResizePolicy::DIMENSION_DEPENDENCY)
295 if(dimension & Dimension::WIDTH)
297 SetDimensionDependency(Dimension::WIDTH, Dimension::HEIGHT);
300 if(dimension & Dimension::HEIGHT)
302 SetDimensionDependency(Dimension::HEIGHT, Dimension::WIDTH);
306 // If calling SetResizePolicy, assume we want relayout enabled
307 relayoutEnabled = true;
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.
313 if(dimension & Dimension::WIDTH)
315 if(originalWidthPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED)
317 preferredSize.width = targetSize.width;
319 else if(originalWidthPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED)
321 targetSize.width = preferredSize.width;
325 if(dimension & Dimension::HEIGHT)
327 if(originalHeightPolicy != ResizePolicy::FIXED && policy == ResizePolicy::FIXED)
329 preferredSize.height = targetSize.height;
331 else if(originalHeightPolicy == ResizePolicy::FIXED && policy != ResizePolicy::FIXED)
333 targetSize.height = preferredSize.height;
338 bool Actor::Relayouter::GetRelayoutDependentOnParent(Dimension::Type dimension)
340 // Check if actor is dependent on parent
341 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
343 if((dimension & (1 << i)))
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)
355 bool Actor::Relayouter::GetRelayoutDependentOnChildren(Dimension::Type dimension)
357 // Check if actor is dependent on children
358 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
360 if((dimension & (1 << i)))
362 const ResizePolicy::Type resizePolicy = GetResizePolicy(static_cast<Dimension::Type>(1 << i));
363 if(resizePolicy == ResizePolicy::FIT_TO_CHILDREN || resizePolicy == ResizePolicy::USE_NATURAL_SIZE)
374 bool Actor::Relayouter::GetRelayoutDependentOnDimension(Dimension::Type dimension, Dimension::Type dependency)
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)
379 if(dimension & (1 << i))
381 return resizePolicies[i] == ResizePolicy::DIMENSION_DEPENDENCY && dimensionDependencies[i] == dependency;
388 void Actor::Relayouter::SetDimensionDependency(Dimension::Type dimension, Dimension::Type dependency)
390 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
392 if(dimension & (1 << i))
394 dimensionDependencies[i] = dependency;
399 Dimension::Type Actor::Relayouter::GetDimensionDependency(Dimension::Type dimension) const
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)
404 if((dimension & (1 << i)))
406 return dimensionDependencies[i];
410 return Dimension::ALL_DIMENSIONS; // Default
413 void Actor::Relayouter::SetLayoutDirty(bool dirty, Dimension::Type dimension)
415 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
417 if(dimension & (1 << i))
419 dimensionDirty[i] = dirty;
424 bool Actor::Relayouter::IsLayoutDirty(Dimension::Type dimension) const
426 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
428 if((dimension & (1 << i)) && dimensionDirty[i])
437 void Actor::Relayouter::SetPreferredSize(Actor& actor, const Vector2& size)
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
443 if(size.width > 0.0f || GetResizePolicy(Dimension::WIDTH) == ResizePolicy::DEFAULT)
445 actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::WIDTH);
448 if(size.height > 0.0f || GetResizePolicy(Dimension::HEIGHT) == ResizePolicy::DEFAULT)
450 actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::HEIGHT);
453 actor.mRelayoutData->preferredSize = size;
455 actor.mUseAnimatedSize = AnimatedSizeFlag::CLEAR;
457 actor.RelayoutRequest();
460 float Actor::Relayouter::ClampDimension(const Internal::Actor& actor, float size, Dimension::Type dimension)
462 const float minSize = actor.GetMinimumSize(dimension);
463 const float maxSize = actor.GetMaximumSize(dimension);
465 return std::max(minSize, std::min(size, maxSize));
468 void Actor::Relayouter::SetNegotiatedDimension(float negotiatedDimension, Dimension::Type dimension)
470 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
472 if(dimension & (1 << i))
474 negotiatedDimensions[i] = negotiatedDimension;
479 float Actor::Relayouter::GetNegotiatedDimension(Dimension::Type dimension)
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)
484 if((dimension & (1 << i)))
486 return negotiatedDimensions[i];
490 return 0.0f; // Default
493 void Actor::Relayouter::NegotiateDimension(Actor& actor, Dimension::Type dimension, const Vector2& allocatedSize, Actor::ActorDimensionStack& recursionStack)
495 // Check if it needs to be negotiated
496 if(actor.IsLayoutDirty(dimension) && !actor.IsLayoutNegotiated(dimension))
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)
503 if(element == searchActor)
505 recursionFound = true;
512 // Record the path that we have taken
513 recursionStack.push_back(Actor::ActorDimensionPair(&actor, dimension));
515 // Dimension dependency check
516 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
518 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
520 if(actor.RelayoutDependentOnDimension(dimension, dimensionToCheck))
522 NegotiateDimension(actor, dimensionToCheck, allocatedSize, recursionStack);
526 // Parent dependency check
527 Actor* parent = actor.GetParent();
528 if(parent && actor.RelayoutDependentOnParent(dimension))
530 NegotiateDimension(*parent, dimension, allocatedSize, recursionStack);
533 // Children dependency check
534 if(actor.RelayoutDependentOnChildren(dimension))
536 for(uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i)
538 ActorPtr child = actor.GetChildAt(i);
540 // Only relayout child first if it is not dependent on this actor
541 if(!child->RelayoutDependentOnParent(dimension))
543 NegotiateDimension(*child, dimension, allocatedSize, recursionStack);
548 // For deriving classes
549 actor.OnCalculateRelayoutSize(dimension);
551 // All dependencies checked, calculate the size and set negotiated flag
552 const float newSize = ClampDimension(actor, actor.CalculateSize(dimension, allocatedSize), dimension);
554 actor.SetNegotiatedDimension(newSize, dimension);
555 actor.SetLayoutNegotiated(true, dimension);
557 // For deriving classes
558 actor.OnLayoutNegotiated(newSize, dimension);
560 // This actor has been successfully processed, pop it off the recursion stack
561 recursionStack.pop_back();
565 // TODO: Break infinite loop
566 actor.SetLayoutNegotiated(true, dimension);
571 void Actor::Relayouter::NegotiateDimensions(Actor& actor, const Vector2& allocatedSize)
573 // Negotiate all dimensions that require it
574 ActorDimensionStack recursionStack;
576 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
578 const Dimension::Type dimension = static_cast<Dimension::Type>(1 << i);
581 NegotiateDimension(actor, dimension, allocatedSize, recursionStack);
585 void Actor::Relayouter::NegotiateSize(Actor& actor, const Vector2& allocatedSize, RelayoutContainer& container)
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);
596 if(actor.GetUseAssignedSize(Dimension::WIDTH))
598 actor.SetLayoutNegotiated(false, Dimension::WIDTH);
600 if(actor.GetUseAssignedSize(Dimension::HEIGHT))
602 actor.SetLayoutNegotiated(false, Dimension::HEIGHT);
605 // Do the negotiation
606 NegotiateDimensions(actor, allocatedSize);
608 // Set the actor size
609 actor.SetNegotiatedSize(container);
611 // Negotiate down to children
612 for(uint32_t i = 0, count = actor.GetChildCount(); i < count; ++i)
614 ActorPtr child = actor.GetChildAt(i);
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))
620 child->SetLayoutNegotiated(false, Dimension::WIDTH);
621 child->SetLayoutDirty(true, Dimension::WIDTH);
624 if(child->GetUseAssignedSize(Dimension::HEIGHT))
626 child->SetLayoutNegotiated(false, Dimension::HEIGHT);
627 child->SetLayoutDirty(true, Dimension::HEIGHT);
630 // Only relayout if required
631 if(child->RelayoutRequired())
633 container.Add(Dali::Actor(child.Get()), actor.mTargetSize.GetVectorXY());
636 DALI_LOG_TIMER_END(NegSizeTimer1, gLogRelayoutFilter, Debug::Concise, "NegotiateSize() took: ");
639 } // namespace Internal