Support multiple window rendering
[platform/core/uifw/dali-core.git] / dali / internal / event / size-negotiation / relayout-controller-impl.cpp
1 /*
2  * Copyright (c) 2019 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.GetName();
65
66   output << " - Pos: " << actor.GetCurrentPosition() << " 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( std::vector< Dali::Actor >::iterator it = potentialRedundantSubRoots.begin(), itEnd = potentialRedundantSubRoots.end(); it != itEnd; ++it )
187   {
188     Dali::Actor subRoot = *it;
189
190     RemoveRequest( subRoot );
191   }
192
193   if ( !mProcessingCoreEvents )
194   {
195     mRenderController.RequestProcessEventsOnIdle( false );
196   }
197 }
198
199 void RelayoutController::OnApplicationSceneCreated()
200 {
201   DALI_LOG_INFO( gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n" );
202
203   // Open relayout controller to receive relayout requests
204   mEnabled = true;
205
206   // Flag request for end of frame
207   Request();
208 }
209
210 void RelayoutController::RequestRelayoutTree( Dali::Actor& actor )
211 {
212   if( !mEnabled )
213   {
214     return;
215   }
216
217   // Only set dirty flag if doing relayout and not already marked as dirty
218   Actor& actorImpl = GetImplementation( actor );
219   if( actorImpl.RelayoutPossible() )
220   {
221     // If parent is not in relayout we are at the top of a new sub-tree
222     Dali::Actor parent = actor.GetParent();
223     if( !parent || !GetImplementation( parent ).IsRelayoutEnabled() )
224     {
225       AddRequest( actor );
226     }
227
228     // Set dirty flag on actors that are enabled
229     actorImpl.SetLayoutDirty( true );
230     actorImpl.SetLayoutNegotiated( false );    // Reset this flag ready for next relayout
231   }
232
233   // Propagate down to children
234   for( uint32_t i = 0; i < actor.GetChildCount(); ++i )
235   {
236     Dali::Actor child = actor.GetChildAt( i );
237
238     RequestRelayoutTree( child );
239   }
240 }
241
242 void RelayoutController::PropagateAll( Dali::Actor& actor, Dimension::Type dimension, std::vector< Dali::Actor >& topOfSubTreeStack, std::vector< Dali::Actor >& potentialRedundantSubRoots )
243 {
244   // Only set dirty flag if doing relayout and not already marked as dirty
245   Actor& actorImpl = GetImplementation( actor );
246   if( actorImpl.RelayoutPossible( dimension ) )
247   {
248     // Set dirty and negotiated flags
249     actorImpl.SetLayoutDirty( true, dimension );
250     actorImpl.SetLayoutNegotiated( false, dimension );    // Reset this flag ready for next relayout
251
252     // Check for dimension dependecy: width for height/height for width etc
253     // Check each possible dimension and see if it is dependent on the input one
254     for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
255     {
256       Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
257
258       if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) &&
259           !actorImpl.IsLayoutDirty( dimensionToCheck ) )
260       {
261         PropagateAll( actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots );
262       }
263     }
264
265     // Propagate up to parent
266     Dali::Actor parent = actor.GetParent();
267     if( parent )
268     {
269       Actor& parentImpl = GetImplementation( parent );
270       if( parentImpl.RelayoutDependentOnChildren( dimension ) && !parentImpl.IsLayoutDirty( dimension ) )
271       {
272         // Store the highest parent reached
273         bool found = false;
274         for( auto&& element : topOfSubTreeStack )
275         {
276           if( element == parent )
277           {
278             found = true;
279             break;
280           }
281         }
282
283         if( !found )
284         {
285           topOfSubTreeStack.push_back( parent );
286         }
287
288         // Propagate up
289         PropagateAll( parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
290       }
291     }
292
293     // Propagate down to children
294     for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
295     {
296       Dali::Actor child = actor.GetChildAt( i );
297       Actor& childImpl = GetImplementation( child );
298
299       if( childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent( dimension ) )
300       {
301         if( childImpl.IsLayoutDirty( dimension ) )
302         {
303           // We have found a child that could potentially have already been collected for relayout
304           potentialRedundantSubRoots.push_back( child );
305         }
306         else
307         {
308           PropagateAll( child, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
309         }
310       }
311     }
312   }
313 }
314
315
316 void RelayoutController::PropagateFlags( Dali::Actor& actor, Dimension::Type dimension )
317 {
318   // Only set dirty flag if doing relayout and not already marked as dirty
319   Actor& actorImpl = GetImplementation( actor );
320   if( actorImpl.IsRelayoutEnabled() )
321   {
322     // Set dirty and negotiated flags
323     actorImpl.SetLayoutDirty( true, dimension );
324     actorImpl.SetLayoutNegotiated( false, dimension );    // Reset this flag ready for next relayout
325
326     // Check for dimension dependecy: width for height/height for width etc
327     // Check each possible dimension and see if it is dependent on the input one
328     for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
329     {
330       Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
331
332       if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) )
333       {
334         PropagateFlags( actor, dimensionToCheck );
335       }
336     }
337
338     // Propagate up to parent
339     Dali::Actor parent = actor.GetParent();
340     if( parent )
341     {
342       Actor& parentImpl = GetImplementation( parent );
343       if( parentImpl.RelayoutDependentOnChildren( dimension ) )
344       {
345         // Propagate up
346         PropagateFlags( parent, dimension );
347       }
348     }
349
350     // Propagate down to children
351     for( uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
352     {
353       Dali::Actor child = actor.GetChildAt( i );
354       Actor& childImpl = GetImplementation( child );
355
356       if( childImpl.RelayoutDependentOnParent( dimension ) )
357       {
358         PropagateFlags( child, dimension );
359       }
360     }
361   }
362 }
363
364 void RelayoutController::AddRequest( Dali::Actor& actor )
365 {
366   Internal::Actor* actorPtr = &GetImplementation( actor );
367
368   // Only add the rootActor if it is not already recorded
369   bool found = false;
370   for( auto&& item : mDirtyLayoutSubTrees )
371   {
372     if( item == actorPtr )
373     {
374       found = true;
375       break;
376     }
377   }
378
379   if( !found )
380   {
381     mDirtyLayoutSubTrees.PushBack( actorPtr );
382   }
383 }
384
385 void RelayoutController::RemoveRequest( Dali::Actor& actor )
386 {
387   Internal::Actor* actorPtr = &GetImplementation( actor );
388
389   // Remove actor from dirty sub trees
390   for( RawActorList::Iterator it = mDirtyLayoutSubTrees.Begin(), itEnd = mDirtyLayoutSubTrees.End(); it != itEnd; ++it )
391   {
392     if( *it == actorPtr )
393     {
394       mDirtyLayoutSubTrees.Erase( it );
395       break;
396     }
397   }
398 }
399
400 void RelayoutController::Request()
401 {
402   mRelayoutFlag = true;
403
404   if( !mRelayoutConnection )
405   {
406     ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect( mSlotDelegate, &RelayoutController::OnObjectDestroyed );
407
408     mRelayoutConnection = true;
409   }
410 }
411
412 void RelayoutController::OnObjectDestroyed( const Dali::RefObject* object )
413 {
414   // Search for and null the object if found in the following lists
415   FindAndZero( mDirtyLayoutSubTrees, object );
416 }
417
418 void RelayoutController::Relayout()
419 {
420   // Only do something when requested
421   if( mRelayoutFlag )
422   {
423     mPerformingRelayout = true;
424
425     // Clear the flag as we're now doing the relayout
426     mRelayoutFlag = false;
427
428     // 1. Finds all top-level controls from the dirty list and allocate them the size of the stage
429     //    These controls are paired with the parent/stage size and added to the stack.
430     for( RawActorList::Iterator it = mDirtyLayoutSubTrees.Begin(), itEnd = mDirtyLayoutSubTrees.End(); it != itEnd; ++it )
431     {
432       Internal::Actor* dirtyActor = *it;
433
434       // Need to test if actor is valid (could have been deleted and had the pointer cleared)
435       if( dirtyActor )
436       {
437         // Only negotiate actors that are on stage
438         if( dirtyActor->OnStage() )
439         {
440           Internal::Actor* parent = dirtyActor->GetParent();
441           QueueActor( dirtyActor, *mRelayoutStack, ( parent ) ? Vector2( parent->GetTargetSize() ) : dirtyActor->GetScene().GetSize() );
442         }
443       }
444     }
445
446     mDirtyLayoutSubTrees.Clear();
447
448     // 2. Iterate through the stack until it's empty.
449     if( mRelayoutStack->Size() > 0 )
450     {
451       PRINT_HIERARCHY;
452
453       while( mRelayoutStack->Size() > 0 )
454       {
455         Dali::Actor actor;
456         Vector2 size;
457         mRelayoutStack->Get( mRelayoutStack->Size() - 1, actor, size );
458         Actor& actorImpl = GetImplementation( actor );
459         mRelayoutStack->PopBack();
460
461         if( actorImpl.RelayoutRequired() )
462         {
463           DALI_LOG_INFO( gLogFilter, Debug::General, "[Internal::RelayoutController::Relayout] Negotiating %p %s %s (%.2f, %.2f)\n", &actorImpl, actor.GetTypeName().c_str(), actor.GetName().c_str(), size.width, size.height );
464
465           // 3. Negotiate the size with the current actor. Pass it an empty container which the actor
466           //    has to fill with all the actors it has not done any size negotiation for.
467
468           actorImpl.NegotiateSize( size, *mRelayoutStack );
469
470           // Reset the flag so that size negotiation will respect the actor's original resize policy
471           actorImpl.SetUseAssignedSize( false );
472         }
473       }
474
475       // We are done with the RelayoutInfos now so delete the pool
476       mRelayoutInfoAllocator.ResetMemoryPool();
477
478       PRINT_HIERARCHY;
479     }
480
481     mPerformingRelayout = false;
482   }
483   // should not disconnect the signal as that causes some control size negotiations to not work correctly
484   // this algorithm needs more optimization as well
485 }
486
487 void RelayoutController::SetEnabled( bool enabled )
488 {
489   mEnabled = enabled;
490 }
491
492 bool RelayoutController::IsPerformingRelayout() const
493 {
494   return mPerformingRelayout;
495 }
496
497 void RelayoutController::SetProcessingCoreEvents( bool processingEvents )
498 {
499   mProcessingCoreEvents = processingEvents;
500 }
501
502 void RelayoutController::FindAndZero( const RawActorList& list, const Dali::RefObject* object )
503 {
504   // Object has been destroyed so clear it from this list
505   for( RawActorList::Iterator it = list.Begin(), itEnd = list.End(); it != itEnd; ++it )
506   {
507     BaseObject* actor = *it;
508
509     if( actor && ( actor == object ) )
510     {
511       *it = NULL;    // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
512     }
513   }
514 }
515
516 } // namespace Internal
517
518 } // namespace Dali