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