+void Actor::EnsureRelayoutData() const
+{
+ // Assign relayout data.
+ if( !mRelayoutData )
+ {
+ mRelayoutData = new RelayoutData();
+ }
+}
+
+bool Actor::RelayoutDependentOnParent( Dimension::Type dimension )
+{
+ // Check if actor is dependent on parent
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( ( dimension & ( 1 << i ) ) )
+ {
+ const ResizePolicy::Type resizePolicy = GetResizePolicy( static_cast< Dimension::Type >( 1 << i ) );
+ if( resizePolicy == ResizePolicy::FILL_TO_PARENT || resizePolicy == ResizePolicy::SIZE_RELATIVE_TO_PARENT || resizePolicy == ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Actor::RelayoutDependentOnChildren( Dimension::Type dimension )
+{
+ // Check if actor is dependent on children
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( ( dimension & ( 1 << i ) ) )
+ {
+ const ResizePolicy::Type resizePolicy = GetResizePolicy( static_cast< Dimension::Type >( 1 << i ) );
+ switch( resizePolicy )
+ {
+ case ResizePolicy::FIT_TO_CHILDREN:
+ case ResizePolicy::USE_NATURAL_SIZE: // i.e. For things that calculate their size based on children
+ {
+ return true;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Actor::RelayoutDependentOnChildrenBase( Dimension::Type dimension )
+{
+ return Actor::RelayoutDependentOnChildren( dimension );
+}
+
+bool Actor::RelayoutDependentOnDimension( Dimension::Type dimension, Dimension::Type dependentDimension )
+{
+ // Check each possible dimension and see if it is dependent on the input one
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ return mRelayoutData->resizePolicies[ i ] == ResizePolicy::DIMENSION_DEPENDENCY && mRelayoutData->dimensionDependencies[ i ] == dependentDimension;
+ }
+ }
+
+ return false;
+}
+
+void Actor::SetNegotiatedDimension( float negotiatedDimension, Dimension::Type dimension )
+{
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ mRelayoutData->negotiatedDimensions[ i ] = negotiatedDimension;
+ }
+ }
+}
+
+float Actor::GetNegotiatedDimension( Dimension::Type dimension ) const
+{
+ // If more than one dimension is requested, just return the first one found
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( ( dimension & ( 1 << i ) ) )
+ {
+ return mRelayoutData->negotiatedDimensions[ i ];
+ }
+ }
+
+ return 0.0f; // Default
+}
+
+void Actor::SetPadding( const Vector2& padding, Dimension::Type dimension )
+{
+ EnsureRelayoutData();
+
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ mRelayoutData->dimensionPadding[ i ] = padding;
+ }
+ }
+}
+
+Vector2 Actor::GetPadding( Dimension::Type dimension ) const
+{
+ EnsureRelayoutData();
+
+ // If more than one dimension is requested, just return the first one found
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( ( dimension & ( 1 << i ) ) )
+ {
+ return mRelayoutData->dimensionPadding[ i ];
+ }
+ }
+
+ return Vector2( 0.0f, 0.0f ); // Default
+}
+
+void Actor::SetLayoutNegotiated( bool negotiated, Dimension::Type dimension )
+{
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ mRelayoutData->dimensionNegotiated[ i ] = negotiated;
+ }
+ }
+}
+
+bool Actor::IsLayoutNegotiated( Dimension::Type dimension ) const
+{
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( ( dimension & ( 1 << i ) ) && mRelayoutData->dimensionNegotiated[ i ] )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+float Actor::CalculateChildSize( const Dali::Actor& child, Dimension::Type dimension )
+{
+ // Could be overridden in derived classes.
+ return CalculateChildSizeBase( child, dimension );
+}
+
+float Actor::CalculateChildSizeBase( const Dali::Actor& child, Dimension::Type dimension )
+{
+ // Fill to parent, taking size mode factor into account
+ switch( child.GetResizePolicy( dimension ) )
+ {
+ case ResizePolicy::FILL_TO_PARENT:
+ {
+ return GetLatestSize( dimension );
+ }
+
+ case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
+ {
+ return GetLatestSize( dimension ) * GetDimensionValue( child.GetSizeModeFactor(), dimension );
+ }
+
+ case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
+ {
+ return GetLatestSize( dimension ) + GetDimensionValue( child.GetSizeModeFactor(), dimension );
+ }
+
+ default:
+ {
+ return GetLatestSize( dimension );
+ }
+ }
+}
+
+float Actor::GetHeightForWidth( float width )
+{
+ // Could be overridden in derived classes.
+ float height = 0.0f;
+
+ const Vector3 naturalSize = GetNaturalSize();
+ if( naturalSize.width > 0.0f )
+ {
+ height = naturalSize.height * width / naturalSize.width;
+ }
+
+ return height;
+}
+
+float Actor::GetWidthForHeight( float height )
+{
+ // Could be overridden in derived classes.
+ float width = 0.0f;
+
+ const Vector3 naturalSize = GetNaturalSize();
+ if( naturalSize.height > 0.0f )
+ {
+ width = naturalSize.width * height / naturalSize.height;
+ }
+
+ return width;
+}
+
+float Actor::GetLatestSize( Dimension::Type dimension ) const
+{
+ return IsLayoutNegotiated( dimension ) ? GetNegotiatedDimension( dimension ) : GetSize( dimension );
+}
+
+float Actor::GetRelayoutSize( Dimension::Type dimension ) const
+{
+ Vector2 padding = GetPadding( dimension );
+
+ return GetLatestSize( dimension ) + padding.x + padding.y;
+}
+
+float Actor::NegotiateFromParent( Dimension::Type dimension )
+{
+ Actor* parent = GetParent();
+ if( parent )
+ {
+ Vector2 padding( GetPadding( dimension ) );
+ Vector2 parentPadding( parent->GetPadding( dimension ) );
+ return parent->CalculateChildSize( Dali::Actor( this ), dimension ) - parentPadding.x - parentPadding.y - padding.x - padding.y;
+ }
+
+ return 0.0f;
+}
+
+float Actor::NegotiateFromChildren( Dimension::Type dimension )
+{
+ float maxDimensionPoint = 0.0f;
+
+ for( unsigned int i = 0, count = GetChildCount(); i < count; ++i )
+ {
+ Dali::Actor child = GetChildAt( i );
+ Actor& childImpl = GetImplementation( child );
+
+ if( !childImpl.RelayoutDependentOnParent( dimension ) )
+ {
+ // Calculate the min and max points that the children range across
+ float childPosition = GetDimensionValue( childImpl.GetTargetPosition(), dimension );
+ float dimensionSize = childImpl.GetRelayoutSize( dimension );
+ maxDimensionPoint = std::max( maxDimensionPoint, childPosition + dimensionSize );
+ }
+ }
+
+ return maxDimensionPoint;
+}
+
+float Actor::GetSize( Dimension::Type dimension ) const
+{
+ return GetDimensionValue( GetTargetSize(), dimension );
+}
+
+float Actor::GetNaturalSize( Dimension::Type dimension ) const
+{
+ return GetDimensionValue( GetNaturalSize(), dimension );
+}
+
+float Actor::CalculateSize( Dimension::Type dimension, const Vector2& maximumSize )
+{
+ switch( GetResizePolicy( dimension ) )
+ {
+ case ResizePolicy::USE_NATURAL_SIZE:
+ {
+ return GetNaturalSize( dimension );
+ }
+
+ case ResizePolicy::FIXED:
+ {
+ return GetDimensionValue( GetPreferredSize(), dimension );
+ }
+
+ case ResizePolicy::USE_ASSIGNED_SIZE:
+ {
+ return GetDimensionValue( maximumSize, dimension );
+ }
+
+ case ResizePolicy::FILL_TO_PARENT:
+ case ResizePolicy::SIZE_RELATIVE_TO_PARENT:
+ case ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT:
+ {
+ return NegotiateFromParent( dimension );
+ }
+
+ case ResizePolicy::FIT_TO_CHILDREN:
+ {
+ return NegotiateFromChildren( dimension );
+ }
+
+ case ResizePolicy::DIMENSION_DEPENDENCY:
+ {
+ const Dimension::Type dimensionDependency = GetDimensionDependency( dimension );
+
+ // Custom rules
+ if( dimension == Dimension::WIDTH && dimensionDependency == Dimension::HEIGHT )
+ {
+ return GetWidthForHeight( GetNegotiatedDimension( Dimension::HEIGHT ) );
+ }
+
+ if( dimension == Dimension::HEIGHT && dimensionDependency == Dimension::WIDTH )
+ {
+ return GetHeightForWidth( GetNegotiatedDimension( Dimension::WIDTH ) );
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ return 0.0f; // Default
+}
+
+float Actor::ClampDimension( float size, Dimension::Type dimension )
+{
+ const float minSize = GetMinimumSize( dimension );
+ const float maxSize = GetMaximumSize( dimension );
+
+ return std::max( minSize, std::min( size, maxSize ) );
+}
+
+void Actor::NegotiateDimension( Dimension::Type dimension, const Vector2& allocatedSize, ActorDimensionStack& recursionStack )
+{
+ // Check if it needs to be negotiated
+ if( IsLayoutDirty( dimension ) && !IsLayoutNegotiated( dimension ) )
+ {
+ // Check that we havn't gotten into an infinite loop
+ ActorDimensionPair searchActor = ActorDimensionPair( this, dimension );
+ bool recursionFound = false;
+ for( ActorDimensionStack::iterator it = recursionStack.begin(), itEnd = recursionStack.end(); it != itEnd; ++it )
+ {
+ if( *it == searchActor )
+ {
+ recursionFound = true;
+ break;
+ }
+ }
+
+ if( !recursionFound )
+ {
+ // Record the path that we have taken
+ recursionStack.push_back( ActorDimensionPair( this, dimension ) );
+
+ // Dimension dependency check
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ Dimension::Type dimensionToCheck = static_cast< Dimension::Type >( 1 << i );
+
+ if( RelayoutDependentOnDimension( dimension, dimensionToCheck ) )
+ {
+ NegotiateDimension( dimensionToCheck, allocatedSize, recursionStack );
+ }
+ }
+
+ // Parent dependency check
+ Actor* parent = GetParent();
+ if( parent && RelayoutDependentOnParent( dimension ) )
+ {
+ parent->NegotiateDimension( dimension, allocatedSize, recursionStack );
+ }
+
+ // Children dependency check
+ if( RelayoutDependentOnChildren( dimension ) )
+ {
+ for( unsigned int i = 0, count = GetChildCount(); i < count; ++i )
+ {
+ Dali::Actor child = GetChildAt( i );
+ Actor& childImpl = GetImplementation( child );
+
+ // Only relayout child first if it is not dependent on this actor
+ if( !childImpl.RelayoutDependentOnParent( dimension ) )
+ {
+ childImpl.NegotiateDimension( dimension, allocatedSize, recursionStack );
+ }
+ }
+ }
+
+ // For deriving classes
+ OnCalculateRelayoutSize( dimension );
+
+ // All dependencies checked, calculate the size and set negotiated flag
+ const float newSize = ClampDimension( CalculateSize( dimension, allocatedSize ), dimension );
+
+ SetNegotiatedDimension( newSize, dimension );
+ SetLayoutNegotiated( true, dimension );
+
+ // For deriving classes
+ OnLayoutNegotiated( newSize, dimension );
+
+ // This actor has been successfully processed, pop it off the recursion stack
+ recursionStack.pop_back();
+ }
+ else
+ {
+ // TODO: Break infinite loop
+ SetLayoutNegotiated( true, dimension );
+ }
+ }
+}
+
+void Actor::NegotiateDimensions( const Vector2& allocatedSize )
+{
+ // Negotiate all dimensions that require it
+ ActorDimensionStack recursionStack;
+
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ const Dimension::Type dimension = static_cast< Dimension::Type >( 1 << i );
+
+ // Negotiate
+ NegotiateDimension( dimension, allocatedSize, recursionStack );
+ }
+}
+
+Vector2 Actor::ApplySizeSetPolicy( const Vector2 size )
+{
+ switch( mRelayoutData->sizeSetPolicy )
+ {
+ case SizeScalePolicy::USE_SIZE_SET:
+ {
+ return size;
+ }
+
+ case SizeScalePolicy::FIT_WITH_ASPECT_RATIO:
+ {
+ // Scale size to fit within the original size bounds, keeping the natural size aspect ratio
+ const Vector3 naturalSize = GetNaturalSize();
+ if( naturalSize.width > 0.0f && naturalSize.height > 0.0f && size.width > 0.0f && size.height > 0.0f )
+ {
+ const float sizeRatio = size.width / size.height;
+ const float naturalSizeRatio = naturalSize.width / naturalSize.height;
+
+ if( naturalSizeRatio < sizeRatio )
+ {
+ return Vector2( naturalSizeRatio * size.height, size.height );
+ }
+ else if( naturalSizeRatio > sizeRatio )
+ {
+ return Vector2( size.width, size.width / naturalSizeRatio );
+ }
+ else
+ {
+ return size;
+ }
+ }
+
+ break;
+ }
+
+ case SizeScalePolicy::FILL_WITH_ASPECT_RATIO:
+ {
+ // Scale size to fill the original size bounds, keeping the natural size aspect ratio. Potentially exceeding the original bounds.
+ const Vector3 naturalSize = GetNaturalSize();
+ if( naturalSize.width > 0.0f && naturalSize.height > 0.0f && size.width > 0.0f && size.height > 0.0f )
+ {
+ const float sizeRatio = size.width / size.height;
+ const float naturalSizeRatio = naturalSize.width / naturalSize.height;
+
+ if( naturalSizeRatio < sizeRatio )
+ {
+ return Vector2( size.width, size.width / naturalSizeRatio );
+ }
+ else if( naturalSizeRatio > sizeRatio )
+ {
+ return Vector2( naturalSizeRatio * size.height, size.height );
+ }
+ else
+ {
+ return size;
+ }
+ }
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ return size;
+}
+
+void Actor::SetNegotiatedSize( RelayoutContainer& container )
+{
+ // Do the set actor size
+ Vector2 negotiatedSize( GetLatestSize( Dimension::WIDTH ), GetLatestSize( Dimension::HEIGHT ) );
+
+ // Adjust for size set policy
+ negotiatedSize = ApplySizeSetPolicy( negotiatedSize );
+
+ // Lock the flag to stop recursive relayouts on set size
+ mRelayoutData->insideRelayout = true;
+ SetSize( negotiatedSize );
+ mRelayoutData->insideRelayout = false;
+
+ // Clear flags for all dimensions
+ SetLayoutDirty( false );
+
+ // Give deriving classes a chance to respond
+ OnRelayout( negotiatedSize, container );
+
+ if( !mOnRelayoutSignal.Empty() )
+ {
+ Dali::Actor handle( this );
+ mOnRelayoutSignal.Emit( handle );
+ }
+}
+
+void Actor::NegotiateSize( const Vector2& allocatedSize, RelayoutContainer& container )
+{
+ // Do the negotiation
+ NegotiateDimensions( allocatedSize );
+
+ // Set the actor size
+ SetNegotiatedSize( container );
+
+ // Negotiate down to children
+ const Vector2 newBounds = GetTargetSize().GetVectorXY();
+
+ for( unsigned int i = 0, count = GetChildCount(); i < count; ++i )
+ {
+ Dali::Actor child = GetChildAt( i );
+
+ // Only relayout if required
+ if( GetImplementation( child ).RelayoutRequired() )
+ {
+ container.Add( child, newBounds );
+ }
+ }
+}
+
+void Actor::RelayoutRequest( Dimension::Type dimension )
+{
+ Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
+ if( relayoutController )
+ {
+ Dali::Actor self( this );
+ relayoutController->RequestRelayout( self, dimension );
+ }
+}
+
+void Actor::PropagateRelayoutFlags()
+{
+ Internal::RelayoutController* relayoutController = Internal::RelayoutController::Get();
+ if( relayoutController )
+ {
+ Dali::Actor self( this );
+ relayoutController->PropagateFlags( self );
+ }
+}
+
+void Actor::OnCalculateRelayoutSize( Dimension::Type dimension )
+{
+}
+
+void Actor::OnLayoutNegotiated( float size, Dimension::Type dimension )
+{
+}
+
+void Actor::SetPreferredSize( const Vector2& size )
+{
+ EnsureRelayoutData();
+
+ if( size.width > 0.0f )
+ {
+ SetResizePolicy( ResizePolicy::FIXED, Dimension::WIDTH );
+ }
+
+ if( size.height > 0.0f )
+ {
+ SetResizePolicy( ResizePolicy::FIXED, Dimension::HEIGHT );
+ }
+
+ mRelayoutData->preferredSize = size;
+
+ RelayoutRequest();
+}
+
+Vector2 Actor::GetPreferredSize() const
+{
+ EnsureRelayoutData();
+
+ return mRelayoutData->preferredSize;
+}
+
+void Actor::SetMinimumSize( float size, Dimension::Type dimension )
+{
+ EnsureRelayoutData();
+
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ mRelayoutData->minimumSize[ i ] = size;
+ }
+ }
+
+ RelayoutRequest();
+}
+
+float Actor::GetMinimumSize( Dimension::Type dimension ) const
+{
+ EnsureRelayoutData();
+
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ return mRelayoutData->minimumSize[ i ];
+ }
+ }
+
+ return 0.0f; // Default
+}
+
+void Actor::SetMaximumSize( float size, Dimension::Type dimension )
+{
+ EnsureRelayoutData();
+
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ mRelayoutData->maximumSize[ i ] = size;
+ }
+ }
+
+ RelayoutRequest();
+}
+
+float Actor::GetMaximumSize( Dimension::Type dimension ) const
+{
+ EnsureRelayoutData();
+
+ for( unsigned int i = 0; i < Dimension::DIMENSION_COUNT; ++i )
+ {
+ if( dimension & ( 1 << i ) )
+ {
+ return mRelayoutData->maximumSize[ i ];
+ }
+ }
+
+ return 0.0f; // Default
+}
+