2 * Copyright (c) 2015 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.
19 #include "relayout-controller-impl.h"
22 #if defined(DEBUG_ENABLED)
24 #include <dali/internal/event/common/system-overlay-impl.h>
25 #endif // defined(DEBUG_ENABLED)
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>
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( 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" );
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( 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( 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 ),
118 mPerformingRelayout( false ),
119 mProcessingCoreEvents( false )
121 // Make space for 32 controls to avoid having to copy construct a lot in the beginning
122 mRelayoutStack->Reserve( 32 );
125 RelayoutController::~RelayoutController()
127 delete mRelayoutStack;
130 RelayoutController* RelayoutController::Get()
132 return &ThreadLocalStorage::Get().GetRelayoutController();
135 void RelayoutController::SetStageSize( unsigned int width, unsigned int height )
137 mStageSize.width = width;
138 mStageSize.height = height;
141 void RelayoutController::QueueActor( Dali::Actor& actor, RelayoutContainer& actors, Vector2 size )
143 if( GetImplementation( actor ).RelayoutRequired() )
145 actors.Add( actor, size );
149 void RelayoutController::RequestRelayout( Dali::Actor& actor, Dimension::Type dimension )
156 std::vector< Dali::Actor > potentialRedundantSubRoots;
157 std::vector< Dali::Actor > topOfSubTreeStack;
159 topOfSubTreeStack.push_back( actor );
161 // Propagate on all dimensions
162 for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
164 if( dimension & ( 1 << i ) )
166 // Do the propagation
167 PropagateAll( actor, static_cast< Dimension::Type >( 1 << i ), topOfSubTreeStack, potentialRedundantSubRoots );
171 while( !topOfSubTreeStack.empty() )
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();
177 Dali::Actor parent = subTreeActor.GetParent();
178 if( !parent || !( GetImplementation( subTreeActor ).RelayoutDependentOnParent() && GetImplementation( parent ).RelayoutRequired() ) )
180 // Add sub tree root to relayout list
181 AddRequest( subTreeActor );
183 // Flag request for end of frame
188 potentialRedundantSubRoots.push_back( subTreeActor );
192 // Remove any redundant sub-tree heads
193 for( std::vector< Dali::Actor >::iterator it = potentialRedundantSubRoots.begin(), itEnd = potentialRedundantSubRoots.end(); it != itEnd; ++it )
195 Dali::Actor subRoot = *it;
197 RemoveRequest( subRoot );
200 if ( !mProcessingCoreEvents )
202 mRenderController.RequestProcessEventsOnIdle();
206 void RelayoutController::OnApplicationSceneCreated()
208 DALI_LOG_INFO( gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n" );
210 // Open relayout controller to receive relayout requests
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 );
219 // Also add request on the root of system overlay
220 Dali::Internal::SystemOverlay* systemOverlay = GetImplementation(stage).GetSystemOverlayInternal();
223 Dali::Internal::Actor& systemOverlayInternalRoot = systemOverlay->GetDefaultRootActor();
224 Dali::Actor systemOverlayRoot = Dali::Actor(&systemOverlayInternalRoot);
225 RequestRelayoutTree( systemOverlayRoot );
228 // Flag request for end of frame
232 void RelayoutController::RequestRelayoutTree( Dali::Actor& actor )
239 // Only set dirty flag if doing relayout and not already marked as dirty
240 Actor& actorImpl = GetImplementation( actor );
241 if( actorImpl.RelayoutPossible() )
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() )
250 // Set dirty flag on actors that are enabled
251 actorImpl.SetLayoutDirty( true );
252 actorImpl.SetLayoutNegotiated( false ); // Reset this flag ready for next relayout
255 // Propagate down to children
256 for( unsigned int i = 0; i < actor.GetChildCount(); ++i )
258 Dali::Actor child = actor.GetChildAt( i );
260 RequestRelayoutTree( child );
264 void RelayoutController::PropagateAll( Dali::Actor& actor, Dimension::Type dimension, std::vector< Dali::Actor >& topOfSubTreeStack, std::vector< Dali::Actor >& potentialRedundantSubRoots )
266 // Only set dirty flag if doing relayout and not already marked as dirty
267 Actor& actorImpl = GetImplementation( actor );
268 if( actorImpl.RelayoutPossible( dimension ) )
270 // Set dirty and negotiated flags
271 actorImpl.SetLayoutDirty( true, dimension );
272 actorImpl.SetLayoutNegotiated( false, dimension ); // Reset this flag ready for next relayout
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 )
278 Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
280 if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) &&
281 !actorImpl.IsLayoutDirty( dimensionToCheck ) )
283 PropagateAll( actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots );
287 // Propagate up to parent
288 Dali::Actor parent = actor.GetParent();
291 Actor& parentImpl = GetImplementation( parent );
292 if( parentImpl.RelayoutDependentOnChildren( dimension ) && !parentImpl.IsLayoutDirty( dimension ) )
294 // Store the highest parent reached
296 for( unsigned int i = 0, count = topOfSubTreeStack.size(); i < count; ++i )
298 if( topOfSubTreeStack[ i ] == parent )
307 topOfSubTreeStack.push_back( parent );
311 PropagateAll( parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
315 // Propagate down to children
316 for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
318 Dali::Actor child = actor.GetChildAt( i );
319 Actor& childImpl = GetImplementation( child );
321 if( childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent( dimension ) )
323 if( childImpl.IsLayoutDirty( dimension ) )
325 // We have found a child that could potentially have already been collected for relayout
326 potentialRedundantSubRoots.push_back( child );
330 PropagateAll( child, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
338 void RelayoutController::PropagateFlags( Dali::Actor& actor, Dimension::Type dimension )
340 // Only set dirty flag if doing relayout and not already marked as dirty
341 Actor& actorImpl = GetImplementation( actor );
342 if( actorImpl.IsRelayoutEnabled() )
344 // Set dirty and negotiated flags
345 actorImpl.SetLayoutDirty( true, dimension );
346 actorImpl.SetLayoutNegotiated( false, dimension ); // Reset this flag ready for next relayout
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 )
352 Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
354 if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) )
356 PropagateFlags( actor, dimensionToCheck );
360 // Propagate up to parent
361 Dali::Actor parent = actor.GetParent();
364 Actor& parentImpl = GetImplementation( parent );
365 if( parentImpl.RelayoutDependentOnChildren( dimension ) )
368 PropagateFlags( parent, dimension );
372 // Propagate down to children
373 for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
375 Dali::Actor child = actor.GetChildAt( i );
376 Actor& childImpl = GetImplementation( child );
378 if( childImpl.RelayoutDependentOnParent( dimension ) )
380 PropagateFlags( child, dimension );
386 void RelayoutController::AddRequest( Dali::Actor& actor )
388 BaseObject* actorPtr = &GetImplementation( actor );
390 // Only add the rootActor if it is not already recorded
392 for( unsigned int i = 0, count = mDirtyLayoutSubTrees.Size(); i < count; ++i )
394 if( mDirtyLayoutSubTrees[ i ] == actorPtr )
403 mDirtyLayoutSubTrees.PushBack( actorPtr );
407 void RelayoutController::RemoveRequest( Dali::Actor& actor )
409 BaseObject* actorPtr = &GetImplementation( actor );
411 // Remove actor from dirty sub trees
412 for( RawActorList::Iterator it = mDirtyLayoutSubTrees.Begin(), itEnd = mDirtyLayoutSubTrees.End(); it != itEnd; ++it )
414 if( *it == actorPtr )
416 mDirtyLayoutSubTrees.Erase( it );
422 void RelayoutController::Request()
424 mRelayoutFlag = true;
426 if( !mRelayoutConnection )
428 Dali::Stage stage = Dali::Stage::GetCurrent();
429 stage.GetObjectRegistry().ObjectDestroyedSignal().Connect( mSlotDelegate, &RelayoutController::OnObjectDestroyed );
431 mRelayoutConnection = true;
435 void RelayoutController::OnObjectDestroyed( const Dali::RefObject* object )
437 // Search for and null the object if found in the following lists
438 FindAndZero( mDirtyLayoutSubTrees, object );
441 void RelayoutController::Relayout()
443 // Only do something when requested
446 mPerformingRelayout = true;
448 // Clear the flag as we're now doing the relayout
449 mRelayoutFlag = false;
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 )
455 BaseObject* dirtyActor = *it;
457 // Need to test if actor is valid (could have been deleted and had the pointer cleared)
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 ) );
463 // Only negotiate actors that are on stage
464 if( actor.OnStage() )
466 Dali::Actor parent = actor.GetParent();
467 QueueActor( actor, *mRelayoutStack, ( parent ) ? Vector2( parent.GetTargetSize() ) : mStageSize );
472 mDirtyLayoutSubTrees.Clear();
474 // 2. Iterate through the stack until it's empty.
475 if( mRelayoutStack->Size() > 0 )
479 while( mRelayoutStack->Size() > 0 )
483 mRelayoutStack->Get( mRelayoutStack->Size() - 1, actor, size );
484 Actor& actorImpl = GetImplementation( actor );
485 mRelayoutStack->PopBack();
487 if( actorImpl.RelayoutRequired() )
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 );
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.
494 actorImpl.NegotiateSize( size, *mRelayoutStack );
496 // Reset the flag so that size negotiation will respect the actor's original resize policy
497 actorImpl.SetUseAssignedSize( false );
501 // We are done with the RelayoutInfos now so delete the pool
502 mRelayoutInfoAllocator.ResetMemoryPool();
507 mPerformingRelayout = false;
509 // should not disconnect the signal as that causes some control size negotiations to not work correctly
510 // this algorithm needs more optimization as well
513 void RelayoutController::SetEnabled( bool enabled )
518 bool RelayoutController::IsPerformingRelayout() const
520 return mPerformingRelayout;
523 void RelayoutController::SetProcessingCoreEvents( bool processingEvents )
525 mProcessingCoreEvents = processingEvents;
528 void RelayoutController::FindAndZero( const RawActorList& list, const Dali::RefObject* object )
530 // Object has been destroyed so clear it from this list
531 for( RawActorList::Iterator it = list.Begin(), itEnd = list.End(); it != itEnd; ++it )
533 BaseObject* actor = *it;
535 if( actor && ( actor == object ) )
537 *it = NULL; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
542 } // namespace Internal