622db16ee0307bdee3bfc55bebd13742d45af01f
[platform/core/uifw/dali-core.git] / dali / internal / event / size-negotiation / relayout-controller-impl.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "relayout-controller-impl.h"
20
21 // EXTERNAL INCLUDES
22 #if defined(DEBUG_ENABLED)
23 #include <sstream>
24 #endif // defined(DEBUG_ENABLED)
25
26 // INTERNAL INCLUDES
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>
34
35 namespace Dali
36 {
37 namespace Internal
38 {
39 namespace
40 {
41 #if defined(DEBUG_ENABLED)
42
43 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_CONTROLLER"));
44
45 /**
46  * Prints out all the children of the given actor when debug is enabled.
47  *
48  * @param[in]  actor  The actor whose children to print.
49  * @param[in]  level  The number of " | " to put in front of the children.
50  */
51 void PrintChildren(Dali::Actor actor, int level)
52 {
53   std::ostringstream output;
54
55   for(int t = 0; t < level; ++t)
56   {
57     output << " | ";
58   }
59
60   output << actor.GetTypeName();
61
62   output << ", " << actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
63
64   output << " - Pos: " << actor.GetCurrentProperty<Vector3>(Dali::Actor::Property::POSITION) << " Size: " << actor.GetTargetSize();
65
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");
69
70   output << ", (" << actor.GetObjectPtr() << ")" << std::endl;
71
72   DALI_LOG_INFO(gLogFilter, Debug::Verbose, output.str().c_str());
73
74   ++level;
75   uint32_t numChildren = actor.GetChildCount();
76   for(uint32_t i = 0; i < numChildren; ++i)
77   {
78     PrintChildren(actor.GetChildAt(i), level);
79   }
80   --level;
81 }
82
83 /**
84  * Prints the entire hierarchy of the scene.
85  */
86 void PrintHierarchy()
87 {
88   if(gLogFilter->IsEnabledFor(Debug::Verbose))
89   {
90     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "---------- ROOT LAYER ----------\n");
91
92     PrintChildren(Stage::GetCurrent()->GetRootLayer(), 0);
93   }
94 }
95
96 #define PRINT_HIERARCHY PrintHierarchy()
97
98 #else // defined(DEBUG_ENABLED)
99
100 #define PRINT_HIERARCHY
101
102 #endif // defined(DEBUG_ENABLED)
103
104 } // unnamed namespace
105
106 RelayoutController::RelayoutController(Integration::RenderController& controller)
107 : mRenderController(controller),
108   mRelayoutInfoAllocator(),
109   mSlotDelegate(this),
110   mRelayoutStack(new MemoryPoolRelayoutContainer(mRelayoutInfoAllocator)),
111   mRelayoutConnection(false),
112   mRelayoutFlag(false),
113   mEnabled(false),
114   mPerformingRelayout(false),
115   mProcessingCoreEvents(false)
116 {
117   // Make space for 32 controls to avoid having to copy construct a lot in the beginning
118   mRelayoutStack->Reserve(32);
119 }
120
121 RelayoutController::~RelayoutController()
122 {
123   delete mRelayoutStack;
124 }
125
126 RelayoutController* RelayoutController::Get()
127 {
128   return &ThreadLocalStorage::Get().GetRelayoutController();
129 }
130
131 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
132 {
133   if(actor && actor->RelayoutRequired())
134   {
135     Dali::Actor actorHandle = Dali::Actor(actor);
136     actors.Add(actorHandle, size);
137   }
138 }
139
140 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
141 {
142   if(!mEnabled)
143   {
144     return;
145   }
146
147   std::vector<Dali::Actor> potentialRedundantSubRoots;
148   std::vector<Dali::Actor> topOfSubTreeStack;
149
150   topOfSubTreeStack.push_back(actor);
151
152   // Propagate on all dimensions
153   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
154   {
155     if(dimension & (1 << i))
156     {
157       // Do the propagation
158       PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
159     }
160   }
161
162   while(!topOfSubTreeStack.empty())
163   {
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();
167
168     Dali::Actor parent = subTreeActor.GetParent();
169     if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
170     {
171       // Add sub tree root to relayout list
172       AddRequest(subTreeActor);
173
174       // Flag request for end of frame
175       Request();
176     }
177     else
178     {
179       potentialRedundantSubRoots.push_back(subTreeActor);
180     }
181   }
182
183   // Remove any redundant sub-tree heads
184   for(auto& subRoot : potentialRedundantSubRoots)
185   {
186     RemoveRequest(subRoot);
187   }
188
189   if(!mProcessingCoreEvents)
190   {
191     mRenderController.RequestProcessEventsOnIdle(false);
192   }
193 }
194
195 void RelayoutController::OnApplicationSceneCreated()
196 {
197   DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
198
199   // Open relayout controller to receive relayout requests
200   mEnabled = true;
201
202   // Flag request for end of frame
203   Request();
204 }
205
206 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
207 {
208   if(!mEnabled)
209   {
210     return;
211   }
212
213   // Only set dirty flag if doing relayout and not already marked as dirty
214   Actor& actorImpl = GetImplementation(actor);
215   if(actorImpl.RelayoutPossible())
216   {
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())
220     {
221       AddRequest(actor);
222     }
223
224     // Set dirty flag on actors that are enabled
225     actorImpl.SetLayoutDirty(true);
226     actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
227   }
228
229   // Propagate down to children
230   for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
231   {
232     Dali::Actor child = actor.GetChildAt(i);
233
234     RequestRelayoutTree(child);
235   }
236 }
237
238 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
239 {
240   // Only set dirty flag if doing relayout and not already marked as dirty
241   Actor& actorImpl = GetImplementation(actor);
242   if(actorImpl.RelayoutPossible(dimension))
243   {
244     // Set dirty and negotiated flags
245     actorImpl.SetLayoutDirty(true, dimension);
246     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
247
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)
251     {
252       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
253
254       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
255          !actorImpl.IsLayoutDirty(dimensionToCheck))
256       {
257         PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
258       }
259     }
260
261     // Propagate up to parent
262     Dali::Actor parent = actor.GetParent();
263     if(parent)
264     {
265       Actor& parentImpl = GetImplementation(parent);
266       if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
267       {
268         // Store the highest parent reached
269         bool found = false;
270         for(auto&& element : topOfSubTreeStack)
271         {
272           if(element == parent)
273           {
274             found = true;
275             break;
276           }
277         }
278
279         if(!found)
280         {
281           topOfSubTreeStack.push_back(parent);
282         }
283
284         // Propagate up
285         PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
286       }
287     }
288
289     // Propagate down to children
290     for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
291     {
292       Dali::Actor child     = actor.GetChildAt(i);
293       Actor&      childImpl = GetImplementation(child);
294
295       if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
296       {
297         if(childImpl.IsLayoutDirty(dimension))
298         {
299           // We have found a child that could potentially have already been collected for relayout
300           potentialRedundantSubRoots.push_back(child);
301         }
302         else
303         {
304           PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
305         }
306       }
307     }
308   }
309 }
310
311 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
312 {
313   // Only set dirty flag if doing relayout and not already marked as dirty
314   Actor& actorImpl = GetImplementation(actor);
315   if(actorImpl.IsRelayoutEnabled())
316   {
317     // Set dirty and negotiated flags
318     actorImpl.SetLayoutDirty(true, dimension);
319     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
320
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)
324     {
325       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
326
327       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
328       {
329         PropagateFlags(actor, dimensionToCheck);
330       }
331     }
332
333     // Propagate up to parent
334     Dali::Actor parent = actor.GetParent();
335     if(parent)
336     {
337       Actor& parentImpl = GetImplementation(parent);
338       if(parentImpl.RelayoutDependentOnChildren(dimension))
339       {
340         // Propagate up
341         PropagateFlags(parent, dimension);
342       }
343     }
344
345     // Propagate down to children
346     for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
347     {
348       Dali::Actor child     = actor.GetChildAt(i);
349       Actor&      childImpl = GetImplementation(child);
350
351       if(childImpl.RelayoutDependentOnParent(dimension))
352       {
353         PropagateFlags(child, dimension);
354       }
355     }
356   }
357 }
358
359 void RelayoutController::AddRequest(Dali::Actor& actor)
360 {
361   Internal::Actor* actorPtr = &GetImplementation(actor);
362
363   // Only add the rootActor if it is not already recorded
364   auto itr = std::find(mDirtyLayoutSubTrees.begin(), mDirtyLayoutSubTrees.end(), actorPtr);
365
366   if(itr == mDirtyLayoutSubTrees.end())
367   {
368     mDirtyLayoutSubTrees.PushBack(actorPtr);
369   }
370 }
371
372 void RelayoutController::RemoveRequest(Dali::Actor& actor)
373 {
374   Internal::Actor* actorPtr = &GetImplementation(actor);
375
376   mDirtyLayoutSubTrees.Erase(std::remove(mDirtyLayoutSubTrees.begin(),
377                                          mDirtyLayoutSubTrees.end(),
378                                          actorPtr),
379                              mDirtyLayoutSubTrees.end());
380 }
381
382 void RelayoutController::Request()
383 {
384   mRelayoutFlag = true;
385
386   if(!mRelayoutConnection)
387   {
388     ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect(mSlotDelegate, &RelayoutController::OnObjectDestroyed);
389
390     mRelayoutConnection = true;
391   }
392 }
393
394 void RelayoutController::OnObjectDestroyed(const Dali::RefObject* object)
395 {
396   // Search for and null the object if found in the following lists
397   FindAndZero(mDirtyLayoutSubTrees, object);
398 }
399
400 void RelayoutController::Relayout()
401 {
402   // Only do something when requested
403   if(mRelayoutFlag)
404   {
405     mPerformingRelayout = true;
406
407     // Clear the flag as we're now doing the relayout
408     mRelayoutFlag = false;
409
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)
413     {
414       // Need to test if actor is valid (could have been deleted and had the pointer cleared)
415       if(dirtyActor)
416       {
417         // Only negotiate actors that are on the scene
418         if(dirtyActor->OnScene())
419         {
420           Internal::Actor* parent = dirtyActor->GetParent();
421           QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
422         }
423       }
424     }
425
426     mDirtyLayoutSubTrees.Clear();
427
428     // 2. Iterate through the stack until it's empty.
429     if(mRelayoutStack->Size() > 0)
430     {
431       PRINT_HIERARCHY;
432
433       while(mRelayoutStack->Size() > 0)
434       {
435         Dali::Actor actor;
436         Vector2     size;
437         mRelayoutStack->Get(mRelayoutStack->Size() - 1, actor, size);
438         Actor& actorImpl = GetImplementation(actor);
439         mRelayoutStack->PopBack();
440
441         if(actorImpl.RelayoutRequired())
442         {
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);
444
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);
448         }
449       }
450
451       // We are done with the RelayoutInfos now so delete the pool
452       mRelayoutInfoAllocator.ResetMemoryPool();
453
454       PRINT_HIERARCHY;
455     }
456
457     mPerformingRelayout = false;
458   }
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
461 }
462
463 void RelayoutController::SetEnabled(bool enabled)
464 {
465   mEnabled = enabled;
466 }
467
468 bool RelayoutController::IsPerformingRelayout() const
469 {
470   return mPerformingRelayout;
471 }
472
473 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
474 {
475   mProcessingCoreEvents = processingEvents;
476 }
477
478 void RelayoutController::FindAndZero(const RawActorList& list, const Dali::RefObject* object)
479 {
480   // Object has been destroyed so clear it from this list
481   for(auto& actor : list)
482   {
483     if(actor && (actor == object))
484     {
485       actor = nullptr; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
486     }
487   }
488 }
489
490 } // namespace Internal
491
492 } // namespace Dali