2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include "relayout-controller-impl.h"
23 #if defined(DEBUG_ENABLED)
25 #include <dali/internal/event/common/system-overlay-impl.h>
26 #endif // defined(DEBUG_ENABLED)
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>
45 #if defined(DEBUG_ENABLED)
47 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_CONTROLLER") );
50 * Prints out all the children of the given actor when debug is enabled.
52 * @param[in] actor The actor whose children to print.
53 * @param[in] level The number of " | " to put in front of the children.
55 void PrintChildren( Dali::Actor actor, int level )
57 std::ostringstream output;
59 for ( int t = 0; t < level; ++t )
64 output << actor.GetTypeName();
66 output << ", " << actor.GetName();
68 output << " - Pos: " << actor.GetCurrentPosition() << " Size: " << actor.GetTargetSize();
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" );
74 output << ", (" << actor.GetObjectPtr() << ")" << std::endl;
76 DALI_LOG_INFO( gLogFilter, Debug::Verbose, output.str().c_str() );
79 unsigned int numChildren = actor.GetChildCount();
80 for( unsigned int i=0; i<numChildren; ++i )
82 PrintChildren( actor.GetChildAt(i), level );
88 * Prints the entire hierarchy of the scene.
92 if ( gLogFilter->IsEnabledFor( Debug::Verbose ) )
94 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "---------- ROOT LAYER ----------\n" );
95 PrintChildren( Dali::Stage().GetCurrent().GetRootLayer(), 0 );
99 #define PRINT_HIERARCHY PrintHierarchy()
101 #else // defined(DEBUG_ENABLED)
103 #define PRINT_HIERARCHY
105 #endif // defined(DEBUG_ENABLED)
107 } // unnamed namespace
109 RelayoutController* RelayoutController::Get()
111 return &ThreadLocalStorage::Get().GetRelayoutController();
114 RelayoutController::RelayoutController()
115 : mRelayoutInfoAllocator(),
116 mSlotDelegate( this ),
117 mRelayoutStack( new MemoryPoolRelayoutContainer( mRelayoutInfoAllocator ) ),
118 mRelayoutConnection( false ),
119 mRelayoutFlag( false ),
122 // Make space for 32 controls to avoid having to copy construct a lot in the beginning
123 mRelayoutStack->Reserve( 32 );
126 RelayoutController::~RelayoutController()
128 delete mRelayoutStack;
131 void RelayoutController::QueueActor( Dali::Actor& actor, RelayoutContainer& actors, Vector2 size )
133 if( GetImplementation( actor ).RelayoutRequired() )
135 actors.Add( actor, size );
139 void RelayoutController::RequestRelayout( Dali::Actor& actor, Dimension dimension )
146 Dali::ActorContainer potentialRedundantSubRoots;
147 Dali::ActorContainer topOfSubTreeStack;
149 topOfSubTreeStack.push_back( actor );
151 // Propagate on all dimensions
152 for( unsigned int i = 0; i < DIMENSION_COUNT; ++i )
154 if( dimension & ( 1 << i ) )
156 // Do the propagation
157 PropagateAll( actor, static_cast< Dimension >( 1 << i ), topOfSubTreeStack, potentialRedundantSubRoots );
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() ) )
166 // Add sub tree root to relayout list
167 AddRequest( subTreeActor );
169 // Flag request for end of frame
174 potentialRedundantSubRoots.push_back( subTreeActor );
177 // Remove any redundant sub-tree heads
178 for( ActorContainer::iterator it = potentialRedundantSubRoots.begin(), itEnd = potentialRedundantSubRoots.end(); it != itEnd; ++it )
180 Dali::Actor subRoot = *it;
182 RemoveRequest( subRoot );
186 void RelayoutController::OnApplicationSceneCreated()
188 DALI_LOG_INFO( gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n" );
190 // Open relayout controller to receive relayout requests
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 );
198 // Flag request for end of frame
202 void RelayoutController::RequestRelayoutTree( Dali::Actor& actor )
209 // Only set dirty flag if doing relayout and not already marked as dirty
210 Actor& actorImpl = GetImplementation( actor );
211 if( actorImpl.RelayoutPossible() )
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() )
220 // Set dirty flag on actors that are enabled
221 actorImpl.SetLayoutDirty( true );
222 actorImpl.SetLayoutNegotiated( false ); // Reset this flag ready for next relayout
225 // Propagate down to children
226 for( unsigned int i = 0; i < actor.GetChildCount(); ++i )
228 Dali::Actor child = actor.GetChildAt( i );
230 RequestRelayoutTree( child );
234 void RelayoutController::PropagateAll( Dali::Actor& actor, Dimension dimension, Dali::ActorContainer& topOfSubTreeStack, Dali::ActorContainer& potentialRedundantSubRoots )
236 // Only set dirty flag if doing relayout and not already marked as dirty
237 Actor& actorImpl = GetImplementation( actor );
238 if( actorImpl.RelayoutPossible( dimension ) )
240 // Set dirty and negotiated flags
241 actorImpl.SetLayoutDirty( true, dimension );
242 actorImpl.SetLayoutNegotiated( false, dimension ); // Reset this flag ready for next relayout
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 )
248 Dimension dimensionToCheck = static_cast< Dimension >( 1 << i );
250 if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) &&
251 !actorImpl.IsLayoutDirty( dimensionToCheck ) )
253 PropagateAll( actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots );
257 // Propagate up to parent
258 Dali::Actor parent = actor.GetParent();
261 Actor& parentImpl = GetImplementation( parent );
262 if( parentImpl.RelayoutDependentOnChildren( dimension ) && !parentImpl.IsLayoutDirty( dimension ) )
264 // Store the highest parent reached
266 for( unsigned int i = 0, count = topOfSubTreeStack.size(); i < count; ++i )
268 if( topOfSubTreeStack[ i ] == parent )
277 topOfSubTreeStack.push_back( parent );
281 PropagateAll( parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
285 // Propagate down to children
286 for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
288 Dali::Actor child = actor.GetChildAt( i );
289 Actor& childImpl = GetImplementation( child );
291 if( childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent( dimension ) )
293 if( childImpl.IsLayoutDirty( dimension ) )
295 // We have found a child that could potentially have already been collected for relayout
296 potentialRedundantSubRoots.push_back( child );
300 PropagateAll( child, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
308 void RelayoutController::PropagateFlags( Dali::Actor& actor, Dimension dimension )
310 // Only set dirty flag if doing relayout and not already marked as dirty
311 Actor& actorImpl = GetImplementation( actor );
312 if( actorImpl.IsRelayoutEnabled() )
314 // Set dirty and negotiated flags
315 actorImpl.SetLayoutDirty( true, dimension );
316 actorImpl.SetLayoutNegotiated( false, dimension ); // Reset this flag ready for next relayout
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 )
322 Dimension dimensionToCheck = static_cast< Dimension >( 1 << i );
324 if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) )
326 PropagateFlags( actor, dimensionToCheck );
330 // Propagate up to parent
331 Dali::Actor parent = actor.GetParent();
334 Actor& parentImpl = GetImplementation( parent );
335 if( parentImpl.RelayoutDependentOnChildren( dimension ) )
338 PropagateFlags( parent, dimension );
342 // Propagate down to children
343 for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
345 Dali::Actor child = actor.GetChildAt( i );
346 Actor& childImpl = GetImplementation( child );
348 if( childImpl.RelayoutDependentOnParent( dimension ) )
350 PropagateFlags( child, dimension );
356 void RelayoutController::AddRequest( Dali::Actor& actor )
358 BaseObject* actorPtr = &GetImplementation( actor );
360 // Only add the rootActor if it is not already recorded
362 for( unsigned int i = 0, count = mDirtyLayoutSubTrees.Size(); i < count; ++i )
364 if( mDirtyLayoutSubTrees[ i ] == actorPtr )
373 mDirtyLayoutSubTrees.PushBack( actorPtr );
377 void RelayoutController::RemoveRequest( Dali::Actor& actor )
379 BaseObject* actorPtr = &GetImplementation( actor );
381 // Remove actor from dirty sub trees
382 for( RawActorList::Iterator it = mDirtyLayoutSubTrees.Begin(), itEnd = mDirtyLayoutSubTrees.End(); it != itEnd; ++it )
384 if( *it == actorPtr )
386 mDirtyLayoutSubTrees.Erase( it );
392 void RelayoutController::Request()
394 mRelayoutFlag = true;
396 if( !mRelayoutConnection )
398 Dali::Stage stage = Dali::Stage::GetCurrent();
399 stage.GetObjectRegistry().ObjectDestroyedSignal().Connect( mSlotDelegate, &RelayoutController::OnObjectDestroyed );
401 mRelayoutConnection = true;
405 void RelayoutController::OnObjectDestroyed( const Dali::RefObject* object )
407 // Search for and null the object if found in the following lists
408 FindAndZero( mDirtyLayoutSubTrees, object );
411 void RelayoutController::Relayout()
413 // Only do something when requested
416 // Clear the flag as we're now doing the relayout
417 mRelayoutFlag = false;
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();
423 for( RawActorList::Iterator it = mDirtyLayoutSubTrees.Begin(), itEnd = mDirtyLayoutSubTrees.End(); it != itEnd; ++it )
425 BaseObject* dirtyActor = *it;
427 // Need to test if actor is valid (could have been deleted and had the pointer cleared)
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 ) );
433 // Only negotiate actors that are on stage
434 if( actor.OnStage() )
436 Dali::Actor parent = actor.GetParent();
437 QueueActor( actor, *mRelayoutStack, ( parent ) ? Vector2( parent.GetTargetSize() ) : stageSize );
442 mDirtyLayoutSubTrees.Clear();
444 // 2. Iterate through the stack until it's empty.
445 if( mRelayoutStack->Size() > 0 )
449 while( mRelayoutStack->Size() > 0 )
453 mRelayoutStack->Get( mRelayoutStack->Size() - 1, actor, size );
454 Actor& actorImpl = GetImplementation( actor );
455 mRelayoutStack->PopBack();
457 if( actorImpl.RelayoutRequired() )
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 );
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 );
467 // We are done with the RelayoutInfos now so delete the pool
468 mRelayoutInfoAllocator.ResetMemoryPool();
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
477 void RelayoutController::SetEnabled( bool enabled )
482 void RelayoutController::FindAndZero( const RawActorList& list, const Dali::RefObject* object )
484 // Object has been destroyed so clear it from this list
485 for( RawActorList::Iterator it = list.Begin(), itEnd = list.End(); it != itEnd; ++it )
487 BaseObject* actor = *it;
489 if( actor && ( actor == object ) )
491 *it = NULL; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
496 } // namespace Internal