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