2 * Copyright (c) 2020 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 #endif // defined(DEBUG_ENABLED)
27 #include <dali/integration-api/debug.h>
28 #include <dali/integration-api/render-controller.h>
29 #include <dali/public-api/object/type-registry.h>
30 #include <dali/public-api/object/object-registry.h>
31 #include <dali/internal/event/actors/actor-impl.h>
32 #include <dali/internal/event/common/stage-impl.h>
33 #include <dali/internal/event/common/thread-local-storage.h>
43 #if defined(DEBUG_ENABLED)
45 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_CONTROLLER") );
48 * Prints out all the children of the given actor when debug is enabled.
50 * @param[in] actor The actor whose children to print.
51 * @param[in] level The number of " | " to put in front of the children.
53 void PrintChildren( Dali::Actor actor, int level )
55 std::ostringstream output;
57 for ( int t = 0; t < level; ++t )
62 output << actor.GetTypeName();
64 output << ", " << actor.GetProperty< std::string >( Dali::Actor::Property::NAME );
66 output << " - Pos: " << actor.GetCurrentProperty< Vector3 >( Dali::Actor::Property::POSITION ) << " Size: " << actor.GetTargetSize();
68 output << ", Dirty: (" << ( GetImplementation( actor ).IsLayoutDirty( Dimension::WIDTH ) ? "TRUE" : "FALSE" ) << "," << ( GetImplementation( actor ).IsLayoutDirty( Dimension::HEIGHT ) ? "TRUE" : "FALSE" ) << ")";
69 output << ", Negotiated: (" << ( GetImplementation( actor ).IsLayoutNegotiated( Dimension::WIDTH ) ? "TRUE" : "FALSE" ) << "," << ( GetImplementation( actor ).IsLayoutNegotiated( Dimension::HEIGHT ) ? "TRUE" : "FALSE" ) << ")";
70 output << ", Enabled: " << ( GetImplementation( actor ).IsRelayoutEnabled() ? "TRUE" : "FALSE" );
72 output << ", (" << actor.GetObjectPtr() << ")" << std::endl;
74 DALI_LOG_INFO( gLogFilter, Debug::Verbose, output.str().c_str() );
77 uint32_t numChildren = actor.GetChildCount();
78 for( uint32_t i=0; i<numChildren; ++i )
80 PrintChildren( actor.GetChildAt(i), level );
86 * Prints the entire hierarchy of the scene.
90 if ( gLogFilter->IsEnabledFor( Debug::Verbose ) )
92 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "---------- ROOT LAYER ----------\n" );
94 PrintChildren( Stage::GetCurrent()->GetRootLayer(), 0 );
98 #define PRINT_HIERARCHY PrintHierarchy()
100 #else // defined(DEBUG_ENABLED)
102 #define PRINT_HIERARCHY
104 #endif // defined(DEBUG_ENABLED)
106 } // unnamed namespace
108 RelayoutController::RelayoutController( Integration::RenderController& controller )
109 : mRenderController( controller ),
110 mRelayoutInfoAllocator(),
111 mSlotDelegate( this ),
112 mRelayoutStack( new MemoryPoolRelayoutContainer( mRelayoutInfoAllocator ) ),
113 mRelayoutConnection( false ),
114 mRelayoutFlag( false ),
116 mPerformingRelayout( false ),
117 mProcessingCoreEvents( false )
119 // Make space for 32 controls to avoid having to copy construct a lot in the beginning
120 mRelayoutStack->Reserve( 32 );
123 RelayoutController::~RelayoutController()
125 delete mRelayoutStack;
128 RelayoutController* RelayoutController::Get()
130 return &ThreadLocalStorage::Get().GetRelayoutController();
133 void RelayoutController::QueueActor( Internal::Actor* actor, RelayoutContainer& actors, Vector2 size )
135 if( actor && actor->RelayoutRequired() )
137 Dali::Actor actorHandle = Dali::Actor( actor );
138 actors.Add( actorHandle, size );
142 void RelayoutController::RequestRelayout( Dali::Actor& actor, Dimension::Type dimension )
149 std::vector< Dali::Actor > potentialRedundantSubRoots;
150 std::vector< Dali::Actor > topOfSubTreeStack;
152 topOfSubTreeStack.push_back( actor );
154 // Propagate on all dimensions
155 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
157 if( dimension & ( 1 << i ) )
159 // Do the propagation
160 PropagateAll( actor, static_cast< Dimension::Type >( 1 << i ), topOfSubTreeStack, potentialRedundantSubRoots );
164 while( !topOfSubTreeStack.empty() )
166 // Request this actor as head of sub-tree if it is not dependent on a parent that is dirty
167 Dali::Actor subTreeActor = topOfSubTreeStack.back();
168 topOfSubTreeStack.pop_back();
170 Dali::Actor parent = subTreeActor.GetParent();
171 if( !parent || !( GetImplementation( subTreeActor ).RelayoutDependentOnParent() && GetImplementation( parent ).RelayoutRequired() ) )
173 // Add sub tree root to relayout list
174 AddRequest( subTreeActor );
176 // Flag request for end of frame
181 potentialRedundantSubRoots.push_back( subTreeActor );
185 // Remove any redundant sub-tree heads
186 for( auto& subRoot : potentialRedundantSubRoots )
188 RemoveRequest( subRoot );
191 if ( !mProcessingCoreEvents )
193 mRenderController.RequestProcessEventsOnIdle( false );
197 void RelayoutController::OnApplicationSceneCreated()
199 DALI_LOG_INFO( gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n" );
201 // Open relayout controller to receive relayout requests
204 // Flag request for end of frame
208 void RelayoutController::RequestRelayoutTree( Dali::Actor& actor )
215 // Only set dirty flag if doing relayout and not already marked as dirty
216 Actor& actorImpl = GetImplementation( actor );
217 if( actorImpl.RelayoutPossible() )
219 // If parent is not in relayout we are at the top of a new sub-tree
220 Dali::Actor parent = actor.GetParent();
221 if( !parent || !GetImplementation( parent ).IsRelayoutEnabled() )
226 // Set dirty flag on actors that are enabled
227 actorImpl.SetLayoutDirty( true );
228 actorImpl.SetLayoutNegotiated( false ); // Reset this flag ready for next relayout
231 // Propagate down to children
232 for( uint32_t i = 0; i < actor.GetChildCount(); ++i )
234 Dali::Actor child = actor.GetChildAt( i );
236 RequestRelayoutTree( child );
240 void RelayoutController::PropagateAll( Dali::Actor& actor, Dimension::Type dimension, std::vector< Dali::Actor >& topOfSubTreeStack, std::vector< Dali::Actor >& potentialRedundantSubRoots )
242 // Only set dirty flag if doing relayout and not already marked as dirty
243 Actor& actorImpl = GetImplementation( actor );
244 if( actorImpl.RelayoutPossible( dimension ) )
246 // Set dirty and negotiated flags
247 actorImpl.SetLayoutDirty( true, dimension );
248 actorImpl.SetLayoutNegotiated( false, dimension ); // Reset this flag ready for next relayout
250 // Check for dimension dependecy: width for height/height for width etc
251 // Check each possible dimension and see if it is dependent on the input one
252 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
254 Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
256 if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) &&
257 !actorImpl.IsLayoutDirty( dimensionToCheck ) )
259 PropagateAll( actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots );
263 // Propagate up to parent
264 Dali::Actor parent = actor.GetParent();
267 Actor& parentImpl = GetImplementation( parent );
268 if( parentImpl.RelayoutDependentOnChildren( dimension ) && !parentImpl.IsLayoutDirty( dimension ) )
270 // Store the highest parent reached
272 for( auto&& element : topOfSubTreeStack )
274 if( element == parent )
283 topOfSubTreeStack.push_back( parent );
287 PropagateAll( parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
291 // Propagate down to children
292 for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
294 Dali::Actor child = actor.GetChildAt( i );
295 Actor& childImpl = GetImplementation( child );
297 if( childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent( dimension ) )
299 if( childImpl.IsLayoutDirty( dimension ) )
301 // We have found a child that could potentially have already been collected for relayout
302 potentialRedundantSubRoots.push_back( child );
306 PropagateAll( child, dimension, topOfSubTreeStack, potentialRedundantSubRoots );
314 void RelayoutController::PropagateFlags( Dali::Actor& actor, Dimension::Type dimension )
316 // Only set dirty flag if doing relayout and not already marked as dirty
317 Actor& actorImpl = GetImplementation( actor );
318 if( actorImpl.IsRelayoutEnabled() )
320 // Set dirty and negotiated flags
321 actorImpl.SetLayoutDirty( true, dimension );
322 actorImpl.SetLayoutNegotiated( false, dimension ); // Reset this flag ready for next relayout
324 // Check for dimension dependecy: width for height/height for width etc
325 // Check each possible dimension and see if it is dependent on the input one
326 for( uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i )
328 Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
330 if( actorImpl.RelayoutDependentOnDimension( dimension, dimensionToCheck ) )
332 PropagateFlags( actor, dimensionToCheck );
336 // Propagate up to parent
337 Dali::Actor parent = actor.GetParent();
340 Actor& parentImpl = GetImplementation( parent );
341 if( parentImpl.RelayoutDependentOnChildren( dimension ) )
344 PropagateFlags( parent, dimension );
348 // Propagate down to children
349 for( uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
351 Dali::Actor child = actor.GetChildAt( i );
352 Actor& childImpl = GetImplementation( child );
354 if( childImpl.RelayoutDependentOnParent( dimension ) )
356 PropagateFlags( child, dimension );
362 void RelayoutController::AddRequest( Dali::Actor& actor )
364 Internal::Actor* actorPtr = &GetImplementation( actor );
366 // Only add the rootActor if it is not already recorded
367 auto itr = std::find( mDirtyLayoutSubTrees.begin(), mDirtyLayoutSubTrees.end(), actorPtr );
369 if( itr == mDirtyLayoutSubTrees.end() )
371 mDirtyLayoutSubTrees.PushBack( actorPtr );
375 void RelayoutController::RemoveRequest( Dali::Actor& actor )
377 Internal::Actor* actorPtr = &GetImplementation( actor );
379 mDirtyLayoutSubTrees.Erase( std::remove( mDirtyLayoutSubTrees.begin(),
380 mDirtyLayoutSubTrees.end(),
382 mDirtyLayoutSubTrees.end() );
385 void RelayoutController::Request()
387 mRelayoutFlag = true;
389 if( !mRelayoutConnection )
391 ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect( mSlotDelegate, &RelayoutController::OnObjectDestroyed );
393 mRelayoutConnection = true;
397 void RelayoutController::OnObjectDestroyed( const Dali::RefObject* object )
399 // Search for and null the object if found in the following lists
400 FindAndZero( mDirtyLayoutSubTrees, object );
403 void RelayoutController::Relayout()
405 // Only do something when requested
408 mPerformingRelayout = true;
410 // Clear the flag as we're now doing the relayout
411 mRelayoutFlag = false;
413 // 1. Finds all top-level controls from the dirty list and allocate them the size of the scene
414 // These controls are paired with the parent/scene size and added to the stack.
415 for( auto& dirtyActor : mDirtyLayoutSubTrees )
417 // Need to test if actor is valid (could have been deleted and had the pointer cleared)
420 // Only negotiate actors that are on the scene
421 if( dirtyActor->OnScene() )
423 Internal::Actor* parent = dirtyActor->GetParent();
424 QueueActor( dirtyActor, *mRelayoutStack, ( parent ) ? Vector2( parent->GetTargetSize() ) : dirtyActor->GetScene().GetSize() );
429 mDirtyLayoutSubTrees.Clear();
431 // 2. Iterate through the stack until it's empty.
432 if( mRelayoutStack->Size() > 0 )
436 while( mRelayoutStack->Size() > 0 )
440 mRelayoutStack->Get( mRelayoutStack->Size() - 1, actor, size );
441 Actor& actorImpl = GetImplementation( actor );
442 mRelayoutStack->PopBack();
444 if( actorImpl.RelayoutRequired() )
446 DALI_LOG_INFO( gLogFilter, Debug::General, "[Internal::RelayoutController::Relayout] Negotiating %p %s %s (%.2f, %.2f)\n", &actorImpl, actor.GetTypeName().c_str(), actor.GetProperty< std::string >( Dali::Actor::Property::NAME ).c_str(), size.width, size.height );
448 // 3. Negotiate the size with the current actor. Pass it an empty container which the actor
449 // has to fill with all the actors it has not done any size negotiation for.
451 actorImpl.NegotiateSize( size, *mRelayoutStack );
453 // Reset the flag so that size negotiation will respect the actor's original resize policy
454 actorImpl.SetUseAssignedSize( false );
458 // We are done with the RelayoutInfos now so delete the pool
459 mRelayoutInfoAllocator.ResetMemoryPool();
464 mPerformingRelayout = false;
466 // should not disconnect the signal as that causes some control size negotiations to not work correctly
467 // this algorithm needs more optimization as well
470 void RelayoutController::SetEnabled( bool enabled )
475 bool RelayoutController::IsPerformingRelayout() const
477 return mPerformingRelayout;
480 void RelayoutController::SetProcessingCoreEvents( bool processingEvents )
482 mProcessingCoreEvents = processingEvents;
485 void RelayoutController::FindAndZero( const RawActorList& list, const Dali::RefObject* object )
487 // Object has been destroyed so clear it from this list
488 for( auto& actor : list )
490 if( actor && ( actor == object ) )
492 actor = nullptr; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
497 } // namespace Internal