Merge "DALi Version 2.1.19" into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / size-negotiation / relayout-controller-impl.cpp
1 /*
2  * Copyright (c) 2022 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   // There was crash when destroying actors and the ResizePolicy is USE_NATURAL_SIZE
129   // The ThreadLocalStorage::Get() only retrieve STL without checking if it exists.
130   // The caller of RelayoutController::Get() should check if RelayoutController is not null.
131   if(ThreadLocalStorage::Created())
132   {
133     return &ThreadLocalStorage::Get().GetRelayoutController();
134   }
135
136   return nullptr;
137 }
138
139 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
140 {
141   if(actor && actor->RelayoutRequired())
142   {
143     Dali::Actor actorHandle = Dali::Actor(actor);
144     actors.Add(actorHandle, size);
145   }
146 }
147
148 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
149 {
150   if(!mEnabled)
151   {
152     return;
153   }
154
155   std::vector<Dali::Actor> potentialRedundantSubRoots;
156   std::vector<Dali::Actor> topOfSubTreeStack;
157
158   topOfSubTreeStack.push_back(actor);
159
160   // Propagate on all dimensions
161   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
162   {
163     if(dimension & (1 << i))
164     {
165       // Do the propagation
166       PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
167     }
168   }
169
170   while(!topOfSubTreeStack.empty())
171   {
172     // Request this actor as head of sub-tree if it is not dependent on a parent that is dirty
173     Dali::Actor subTreeActor = topOfSubTreeStack.back();
174     topOfSubTreeStack.pop_back();
175
176     Dali::Actor parent = subTreeActor.GetParent();
177     if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
178     {
179       // Add sub tree root to relayout list
180       AddRequest(subTreeActor);
181
182       // Flag request for end of frame
183       Request();
184     }
185     else
186     {
187       potentialRedundantSubRoots.push_back(subTreeActor);
188     }
189   }
190
191   // Remove any redundant sub-tree heads
192   for(auto& subRoot : potentialRedundantSubRoots)
193   {
194     RemoveRequest(subRoot);
195   }
196
197   if(!mProcessingCoreEvents)
198   {
199     mRenderController.RequestProcessEventsOnIdle(false);
200   }
201 }
202
203 void RelayoutController::OnApplicationSceneCreated()
204 {
205   DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
206
207   // Open relayout controller to receive relayout requests
208   mEnabled = true;
209
210   // Flag request for end of frame
211   Request();
212 }
213
214 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
215 {
216   if(!mEnabled)
217   {
218     return;
219   }
220
221   // Only set dirty flag if doing relayout and not already marked as dirty
222   Actor& actorImpl = GetImplementation(actor);
223   if(actorImpl.RelayoutPossible())
224   {
225     // If parent is not in relayout we are at the top of a new sub-tree
226     Dali::Actor parent = actor.GetParent();
227     if(!parent || !GetImplementation(parent).IsRelayoutEnabled())
228     {
229       AddRequest(actor);
230     }
231
232     // Set dirty flag on actors that are enabled
233     actorImpl.SetLayoutDirty(true);
234     actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
235   }
236
237   // Propagate down to children
238   for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
239   {
240     Dali::Actor child = actor.GetChildAt(i);
241
242     RequestRelayoutTree(child);
243   }
244 }
245
246 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
247 {
248   // Only set dirty flag if doing relayout and not already marked as dirty
249   Actor& actorImpl = GetImplementation(actor);
250   if(actorImpl.RelayoutPossible(dimension))
251   {
252     // Set dirty and negotiated flags
253     actorImpl.SetLayoutDirty(true, dimension);
254     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
255
256     // Check for dimension dependecy: width for height/height for width etc
257     // Check each possible dimension and see if it is dependent on the input one
258     for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
259     {
260       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
261
262       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
263          !actorImpl.IsLayoutDirty(dimensionToCheck))
264       {
265         PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
266       }
267     }
268
269     // Propagate up to parent
270     Dali::Actor parent = actor.GetParent();
271     if(parent)
272     {
273       Actor& parentImpl = GetImplementation(parent);
274       if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
275       {
276         // Store the highest parent reached
277         bool found = false;
278         for(auto&& element : topOfSubTreeStack)
279         {
280           if(element == parent)
281           {
282             found = true;
283             break;
284           }
285         }
286
287         if(!found)
288         {
289           topOfSubTreeStack.push_back(parent);
290         }
291
292         // Propagate up
293         PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
294       }
295     }
296
297     // Propagate down to children
298     for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
299     {
300       Dali::Actor child     = actor.GetChildAt(i);
301       Actor&      childImpl = GetImplementation(child);
302
303       if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
304       {
305         if(childImpl.IsLayoutDirty(dimension))
306         {
307           // We have found a child that could potentially have already been collected for relayout
308           potentialRedundantSubRoots.push_back(child);
309         }
310         else
311         {
312           PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
313         }
314       }
315     }
316   }
317 }
318
319 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
320 {
321   // Only set dirty flag if doing relayout and not already marked as dirty
322   Actor& actorImpl = GetImplementation(actor);
323   if(actorImpl.IsRelayoutEnabled())
324   {
325     // Set dirty and negotiated flags
326     actorImpl.SetLayoutDirty(true, dimension);
327     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
328
329     // Check for dimension dependecy: width for height/height for width etc
330     // Check each possible dimension and see if it is dependent on the input one
331     for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
332     {
333       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
334
335       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
336       {
337         PropagateFlags(actor, dimensionToCheck);
338       }
339     }
340
341     // Propagate up to parent
342     Dali::Actor parent = actor.GetParent();
343     if(parent)
344     {
345       Actor& parentImpl = GetImplementation(parent);
346       if(parentImpl.RelayoutDependentOnChildren(dimension))
347       {
348         // Propagate up
349         PropagateFlags(parent, dimension);
350       }
351     }
352
353     // Propagate down to children
354     for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
355     {
356       Dali::Actor child     = actor.GetChildAt(i);
357       Actor&      childImpl = GetImplementation(child);
358
359       if(childImpl.RelayoutDependentOnParent(dimension))
360       {
361         PropagateFlags(child, dimension);
362       }
363     }
364   }
365 }
366
367 void RelayoutController::AddRequest(Dali::Actor& actor)
368 {
369   Internal::Actor* actorPtr = &GetImplementation(actor);
370
371   // Only add the rootActor if it is not already recorded
372   auto itr = std::find(mDirtyLayoutSubTrees.begin(), mDirtyLayoutSubTrees.end(), actorPtr);
373
374   if(itr == mDirtyLayoutSubTrees.end())
375   {
376     mDirtyLayoutSubTrees.PushBack(actorPtr);
377   }
378 }
379
380 void RelayoutController::RemoveRequest(Dali::Actor& actor)
381 {
382   Internal::Actor* actorPtr = &GetImplementation(actor);
383
384   mDirtyLayoutSubTrees.Erase(std::remove(mDirtyLayoutSubTrees.begin(),
385                                          mDirtyLayoutSubTrees.end(),
386                                          actorPtr),
387                              mDirtyLayoutSubTrees.end());
388 }
389
390 void RelayoutController::Request()
391 {
392   mRelayoutFlag = true;
393
394   if(!mRelayoutConnection)
395   {
396     ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect(mSlotDelegate, &RelayoutController::OnObjectDestroyed);
397
398     mRelayoutConnection = true;
399   }
400 }
401
402 void RelayoutController::OnObjectDestroyed(const Dali::RefObject* object)
403 {
404   // Search for and null the object if found in the following lists
405   FindAndZero(mDirtyLayoutSubTrees, object);
406 }
407
408 void RelayoutController::Relayout()
409 {
410   // Only do something when requested
411   if(mRelayoutFlag)
412   {
413     mPerformingRelayout = true;
414
415     // Clear the flag as we're now doing the relayout
416     mRelayoutFlag = false;
417
418     // 1. Finds all top-level controls from the dirty list and allocate them the size of the scene
419     //    These controls are paired with the parent/scene size and added to the stack.
420     for(auto& dirtyActor : mDirtyLayoutSubTrees)
421     {
422       // Need to test if actor is valid (could have been deleted and had the pointer cleared)
423       if(dirtyActor)
424       {
425         // Only negotiate actors that are on the scene
426         if(dirtyActor->OnScene())
427         {
428           Internal::Actor* parent = dirtyActor->GetParent();
429           QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
430         }
431       }
432     }
433
434     mDirtyLayoutSubTrees.Clear();
435
436     // 2. Iterate through the stack until it's empty.
437     if(mRelayoutStack->Size() > 0)
438     {
439       PRINT_HIERARCHY;
440
441       while(mRelayoutStack->Size() > 0)
442       {
443         Dali::Actor actor;
444         Vector2     size;
445         mRelayoutStack->Get(mRelayoutStack->Size() - 1, actor, size);
446         Actor& actorImpl = GetImplementation(actor);
447         mRelayoutStack->PopBack();
448
449         if(actorImpl.RelayoutRequired() && actorImpl.OnScene())
450         {
451           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);
452
453           // 3. Negotiate the size with the current actor. Pass it an empty container which the actor
454           //    has to fill with all the actors it has not done any size negotiation for.
455           actorImpl.NegotiateSize(size, *mRelayoutStack);
456         }
457       }
458
459       // We are done with the RelayoutInfos now so delete the pool
460       mRelayoutInfoAllocator.ResetMemoryPool();
461
462       PRINT_HIERARCHY;
463     }
464
465     mPerformingRelayout = false;
466   }
467   // should not disconnect the signal as that causes some control size negotiations to not work correctly
468   // this algorithm needs more optimization as well
469 }
470
471 void RelayoutController::SetEnabled(bool enabled)
472 {
473   mEnabled = enabled;
474 }
475
476 bool RelayoutController::IsPerformingRelayout() const
477 {
478   return mPerformingRelayout;
479 }
480
481 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
482 {
483   mProcessingCoreEvents = processingEvents;
484 }
485
486 void RelayoutController::FindAndZero(const RawActorList& list, const Dali::RefObject* object)
487 {
488   // Object has been destroyed so clear it from this list
489   for(auto& actor : list)
490   {
491     if(actor && (actor == object))
492     {
493       actor = nullptr; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
494     }
495   }
496 }
497
498 } // namespace Internal
499
500 } // namespace Dali