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 "relayout-controller-impl.h"
22 #if defined(DEBUG_ENABLED)
24 #endif // defined(DEBUG_ENABLED)
27 #include <dali/integration-api/debug.h>
28 #include <dali/integration-api/render-controller.h>
29 #include <dali/internal/event/actors/actor-impl.h>
30 #include <dali/internal/event/common/stage-impl.h>
31 #include <dali/internal/event/common/thread-local-storage.h>
32 #include <dali/public-api/object/object-registry.h>
33 #include <dali/public-api/object/type-registry.h>
41 #if defined(DEBUG_ENABLED)
43 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_CONTROLLER"));
46 * Prints out all the children of the given actor when debug is enabled.
48 * @param[in] actor The actor whose children to print.
49 * @param[in] level The number of " | " to put in front of the children.
51 void PrintChildren(Dali::Actor actor, int level)
53 std::ostringstream output;
55 for(int t = 0; t < level; ++t)
60 output << actor.GetTypeName();
62 output << ", " << actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
64 output << " - Pos: " << actor.GetCurrentProperty<Vector3>(Dali::Actor::Property::POSITION) << " Size: " << actor.GetTargetSize();
66 output << ", Dirty: (" << (GetImplementation(actor).IsLayoutDirty(Dimension::WIDTH) ? "TRUE" : "FALSE") << "," << (GetImplementation(actor).IsLayoutDirty(Dimension::HEIGHT) ? "TRUE" : "FALSE") << ")";
67 output << ", Negotiated: (" << (GetImplementation(actor).IsLayoutNegotiated(Dimension::WIDTH) ? "TRUE" : "FALSE") << "," << (GetImplementation(actor).IsLayoutNegotiated(Dimension::HEIGHT) ? "TRUE" : "FALSE") << ")";
68 output << ", Enabled: " << (GetImplementation(actor).IsRelayoutEnabled() ? "TRUE" : "FALSE");
70 output << ", (" << actor.GetObjectPtr() << ")" << std::endl;
72 DALI_LOG_INFO(gLogFilter, Debug::Verbose, output.str().c_str());
75 uint32_t numChildren = actor.GetChildCount();
76 for(uint32_t i = 0; i < numChildren; ++i)
78 PrintChildren(actor.GetChildAt(i), level);
84 * Prints the entire hierarchy of the scene.
88 if(gLogFilter->IsEnabledFor(Debug::Verbose))
90 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "---------- ROOT LAYER ----------\n");
92 PrintChildren(Stage::GetCurrent()->GetRootLayer(), 0);
96 #define PRINT_HIERARCHY PrintHierarchy()
98 #else // defined(DEBUG_ENABLED)
100 #define PRINT_HIERARCHY
102 #endif // defined(DEBUG_ENABLED)
104 } // unnamed namespace
106 RelayoutController::RelayoutController(Integration::RenderController& controller)
107 : mRenderController(controller),
108 mRelayoutInfoAllocator(),
110 mRelayoutStack(new MemoryPoolRelayoutContainer(mRelayoutInfoAllocator)),
111 mRelayoutConnection(false),
112 mRelayoutFlag(false),
114 mPerformingRelayout(false),
115 mProcessingCoreEvents(false)
117 // Make space for 32 controls to avoid having to copy construct a lot in the beginning
118 mRelayoutStack->Reserve(32);
121 RelayoutController::~RelayoutController()
123 delete mRelayoutStack;
126 RelayoutController* RelayoutController::Get()
128 return &ThreadLocalStorage::Get().GetRelayoutController();
131 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
133 if(actor && actor->RelayoutRequired())
135 Dali::Actor actorHandle = Dali::Actor(actor);
136 actors.Add(actorHandle, size);
140 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
147 std::vector<Dali::Actor> potentialRedundantSubRoots;
148 std::vector<Dali::Actor> topOfSubTreeStack;
150 topOfSubTreeStack.push_back(actor);
152 // Propagate on all dimensions
153 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
155 if(dimension & (1 << i))
157 // Do the propagation
158 PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
162 while(!topOfSubTreeStack.empty())
164 // Request this actor as head of sub-tree if it is not dependent on a parent that is dirty
165 Dali::Actor subTreeActor = topOfSubTreeStack.back();
166 topOfSubTreeStack.pop_back();
168 Dali::Actor parent = subTreeActor.GetParent();
169 if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
171 // Add sub tree root to relayout list
172 AddRequest(subTreeActor);
174 // Flag request for end of frame
179 potentialRedundantSubRoots.push_back(subTreeActor);
183 // Remove any redundant sub-tree heads
184 for(auto& subRoot : potentialRedundantSubRoots)
186 RemoveRequest(subRoot);
189 if(!mProcessingCoreEvents)
191 mRenderController.RequestProcessEventsOnIdle(false);
195 void RelayoutController::OnApplicationSceneCreated()
197 DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
199 // Open relayout controller to receive relayout requests
202 // Flag request for end of frame
206 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
213 // Only set dirty flag if doing relayout and not already marked as dirty
214 Actor& actorImpl = GetImplementation(actor);
215 if(actorImpl.RelayoutPossible())
217 // If parent is not in relayout we are at the top of a new sub-tree
218 Dali::Actor parent = actor.GetParent();
219 if(!parent || !GetImplementation(parent).IsRelayoutEnabled())
224 // Set dirty flag on actors that are enabled
225 actorImpl.SetLayoutDirty(true);
226 actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
229 // Propagate down to children
230 for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
232 Dali::Actor child = actor.GetChildAt(i);
234 RequestRelayoutTree(child);
238 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
240 // Only set dirty flag if doing relayout and not already marked as dirty
241 Actor& actorImpl = GetImplementation(actor);
242 if(actorImpl.RelayoutPossible(dimension))
244 // Set dirty and negotiated flags
245 actorImpl.SetLayoutDirty(true, dimension);
246 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
248 // Check for dimension dependecy: width for height/height for width etc
249 // Check each possible dimension and see if it is dependent on the input one
250 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
252 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
254 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
255 !actorImpl.IsLayoutDirty(dimensionToCheck))
257 PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
261 // Propagate up to parent
262 Dali::Actor parent = actor.GetParent();
265 Actor& parentImpl = GetImplementation(parent);
266 if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
268 // Store the highest parent reached
270 for(auto&& element : topOfSubTreeStack)
272 if(element == parent)
281 topOfSubTreeStack.push_back(parent);
285 PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
289 // Propagate down to children
290 for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
292 Dali::Actor child = actor.GetChildAt(i);
293 Actor& childImpl = GetImplementation(child);
295 if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
297 if(childImpl.IsLayoutDirty(dimension))
299 // We have found a child that could potentially have already been collected for relayout
300 potentialRedundantSubRoots.push_back(child);
304 PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
311 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
313 // Only set dirty flag if doing relayout and not already marked as dirty
314 Actor& actorImpl = GetImplementation(actor);
315 if(actorImpl.IsRelayoutEnabled())
317 // Set dirty and negotiated flags
318 actorImpl.SetLayoutDirty(true, dimension);
319 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
321 // Check for dimension dependecy: width for height/height for width etc
322 // Check each possible dimension and see if it is dependent on the input one
323 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
325 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
327 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
329 PropagateFlags(actor, dimensionToCheck);
333 // Propagate up to parent
334 Dali::Actor parent = actor.GetParent();
337 Actor& parentImpl = GetImplementation(parent);
338 if(parentImpl.RelayoutDependentOnChildren(dimension))
341 PropagateFlags(parent, dimension);
345 // Propagate down to children
346 for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
348 Dali::Actor child = actor.GetChildAt(i);
349 Actor& childImpl = GetImplementation(child);
351 if(childImpl.RelayoutDependentOnParent(dimension))
353 PropagateFlags(child, dimension);
359 void RelayoutController::AddRequest(Dali::Actor& actor)
361 Internal::Actor* actorPtr = &GetImplementation(actor);
363 // Only add the rootActor if it is not already recorded
364 auto itr = std::find(mDirtyLayoutSubTrees.begin(), mDirtyLayoutSubTrees.end(), actorPtr);
366 if(itr == mDirtyLayoutSubTrees.end())
368 mDirtyLayoutSubTrees.PushBack(actorPtr);
372 void RelayoutController::RemoveRequest(Dali::Actor& actor)
374 Internal::Actor* actorPtr = &GetImplementation(actor);
376 mDirtyLayoutSubTrees.Erase(std::remove(mDirtyLayoutSubTrees.begin(),
377 mDirtyLayoutSubTrees.end(),
379 mDirtyLayoutSubTrees.end());
382 void RelayoutController::Request()
384 mRelayoutFlag = true;
386 if(!mRelayoutConnection)
388 ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect(mSlotDelegate, &RelayoutController::OnObjectDestroyed);
390 mRelayoutConnection = true;
394 void RelayoutController::OnObjectDestroyed(const Dali::RefObject* object)
396 // Search for and null the object if found in the following lists
397 FindAndZero(mDirtyLayoutSubTrees, object);
400 void RelayoutController::Relayout()
402 // Only do something when requested
405 mPerformingRelayout = true;
407 // Clear the flag as we're now doing the relayout
408 mRelayoutFlag = false;
410 // 1. Finds all top-level controls from the dirty list and allocate them the size of the scene
411 // These controls are paired with the parent/scene size and added to the stack.
412 for(auto& dirtyActor : mDirtyLayoutSubTrees)
414 // Need to test if actor is valid (could have been deleted and had the pointer cleared)
417 // Only negotiate actors that are on the scene
418 if(dirtyActor->OnScene())
420 Internal::Actor* parent = dirtyActor->GetParent();
421 QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
426 mDirtyLayoutSubTrees.Clear();
428 // 2. Iterate through the stack until it's empty.
429 if(mRelayoutStack->Size() > 0)
433 while(mRelayoutStack->Size() > 0)
437 mRelayoutStack->Get(mRelayoutStack->Size() - 1, actor, size);
438 Actor& actorImpl = GetImplementation(actor);
439 mRelayoutStack->PopBack();
441 if(actorImpl.RelayoutRequired())
443 DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::Relayout] Negotiating %p %s %s (%.2f, %.2f)\n", &actorImpl, actor.GetTypeName().c_str(), actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str(), size.width, size.height);
445 // 3. Negotiate the size with the current actor. Pass it an empty container which the actor
446 // has to fill with all the actors it has not done any size negotiation for.
447 actorImpl.NegotiateSize(size, *mRelayoutStack);
451 // We are done with the RelayoutInfos now so delete the pool
452 mRelayoutInfoAllocator.ResetMemoryPool();
457 mPerformingRelayout = false;
459 // should not disconnect the signal as that causes some control size negotiations to not work correctly
460 // this algorithm needs more optimization as well
463 void RelayoutController::SetEnabled(bool enabled)
468 bool RelayoutController::IsPerformingRelayout() const
470 return mPerformingRelayout;
473 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
475 mProcessingCoreEvents = processingEvents;
478 void RelayoutController::FindAndZero(const RawActorList& list, const Dali::RefObject* object)
480 // Object has been destroyed so clear it from this list
481 for(auto& actor : list)
483 if(actor && (actor == object))
485 actor = nullptr; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
490 } // namespace Internal