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