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