@ -1,7 +1,12 @@
// This source file is adapted from the Windows Presentation Foundation project.
// (https://github.com/dotnet/wpf/)
//
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System ;
namespace Avalonia.Controls
namespace Avalonia.Controls
{
{
using System ;
/// <summary>
/// <summary>
/// Defines the available docking modes for a control in a <see cref="DockPanel"/>.
/// Defines the available docking modes for a control in a <see cref="DockPanel"/>.
/// </summary>
/// </summary>
@ -70,107 +75,137 @@ namespace Avalonia.Controls
set { SetValue ( LastChildFillProperty , value ) ; }
set { SetValue ( LastChildFillProperty , value ) ; }
}
}
/// <inheritdoc/>
/// <summary>
/// Updates DesiredSize of the DockPanel. Called by parent Control. This is the first pass of layout.
/// </summary>
/// <remarks>
/// Children are measured based on their sizing properties and <see cref="Dock" />.
/// Each child is allowed to consume all of the space on the side on which it is docked; Left/Right docked
/// children are granted all vertical space for their entire width, and Top/Bottom docked children are
/// granted all horizontal space for their entire height.
/// </remarks>
/// <param name="constraint">Constraint size is an "upper limit" that the return value should not exceed.</param>
/// <returns>The Panel's desired size.</returns>
protected override Size MeasureOverride ( Size constraint )
protected override Size MeasureOverride ( Size constraint )
{
{
double usedWidth = 0.0 ;
var children = Children ;
double usedHeight = 0.0 ;
double maximumWidth = 0.0 ;
double parentWidth = 0 ; // Our current required width due to children thus far.
double maximumHeight = 0.0 ;
double parentHeight = 0 ; // Our current required height due to children thus far.
double accumulatedWidth = 0 ; // Total width consumed by children.
double accumulatedHeight = 0 ; // Total height consumed by children.
// Measure each of the Children
for ( int i = 0 , count = children . Count ; i < count ; + + i )
foreach ( Control element in Children )
{
{
// Get the child's desired size
var child = children [ i ] ;
Size remainingSize = new Size (
Size childConstraint ; // Contains the suggested input constraint for this child.
Math . Max ( 0.0 , constraint . Width - usedWidth ) ,
Size childDesiredSize ; // Contains the return size from child measure.
Math . Max ( 0.0 , constraint . Height - usedHeight ) ) ;
element . Measure ( remainingSize ) ;
if ( child = = null )
Size desiredSize = element . DesiredSize ;
{ continue ; }
// Decrease the remaining space for the rest of the children
// Child constraint is the remaining size; this is total size minus size consumed by previous children.
switch ( GetDock ( element ) )
childConstraint = new Size ( Math . Max ( 0.0 , constraint . Width - accumulatedWidth ) ,
Math . Max ( 0.0 , constraint . Height - accumulatedHeight ) ) ;
// Measure child.
child . Measure ( childConstraint ) ;
childDesiredSize = child . DesiredSize ;
// Now, we adjust:
// 1. Size consumed by children (accumulatedSize). This will be used when computing subsequent
// children to determine how much space is remaining for them.
// 2. Parent size implied by this child (parentSize) when added to the current children (accumulatedSize).
// This is different from the size above in one respect: A Dock.Left child implies a height, but does
// not actually consume any height for subsequent children.
// If we accumulate size in a given dimension, the next child (or the end conditions after the child loop)
// will deal with computing our minimum size (parentSize) due to that accumulation.
// Therefore, we only need to compute our minimum size (parentSize) in dimensions that this child does
// not accumulate: Width for Top/Bottom, Height for Left/Right.
switch ( DockPanel . GetDock ( ( Control ) child ) )
{
{
case Dock . Left :
case Dock . Left :
case Dock . Right :
case Dock . Right :
maximumHeight = Math . Max ( maximumHeight , usedHeight + desiredSize . Height ) ;
parent Height = Math . Max ( parentHeight , accumulat edHeight + chil dD esiredSize. Height ) ;
usedWidth + = desiredSize . Width ;
accumulat edWidth + = chil dD esiredSize. Width ;
break ;
break ;
case Dock . Top :
case Dock . Top :
case Dock . Bottom :
case Dock . Bottom :
maximumWidth = Math . Max ( maximumWidth , usedWidth + desiredSize . Width ) ;
parent Width = Math . Max ( parentWidth , accumulat edWidth + chil dD esiredSize. Width ) ;
usedHeight + = desiredSize . Height ;
accumulat edHeight + = chil dD esiredSize. Height ;
break ;
break ;
}
}
}
}
maximumWidth = Math . Max ( maximumWidth , usedWidth ) ;
// Make sure the final accumulated size is reflected in parentSize.
maximumHeight = Math . Max ( maximumHeight , usedHeight ) ;
parentWidth = Math . Max ( parentWidth , accumulatedWidth ) ;
return new Size ( maximumWidth , maximumHeight ) ;
parentHeight = Math . Max ( parentHeight , accumulatedHeight ) ;
return ( new Size ( parentWidth , parentHeight ) ) ;
}
}
/// <inheritdoc/>
/// <summary>
/// DockPanel computes a position and final size for each of its children based upon their
/// <see cref="Dock" /> enum and sizing properties.
/// </summary>
/// <param name="arrangeSize">Size that DockPanel will assume to position children.</param>
protected override Size ArrangeOverride ( Size arrangeSize )
protected override Size ArrangeOverride ( Size arrangeSize )
{
{
double left = 0.0 ;
double top = 0.0 ;
double right = 0.0 ;
double bottom = 0.0 ;
// Arrange each of the Children
var children = Children ;
var children = Children ;
int dockedCount = children . Count - ( LastChildFill ? 1 : 0 ) ;
int totalChildrenCount = children . Count ;
int index = 0 ;
int nonFillChildrenCount = totalChildrenCount - ( LastChildFill ? 1 : 0 ) ;
double accumulatedLeft = 0 ;
double accumulatedTop = 0 ;
double accumulatedRight = 0 ;
double accumulatedBottom = 0 ;
foreach ( Control element in children )
for ( int i = 0 ; i < totalChildrenCount ; + + i )
{
{
// Determine the remaining space left to arrange the element
var child = children [ i ] ;
Rect remainingRect = new Rect (
if ( child = = null )
left ,
{ continue ; }
top ,
Math . Max ( 0.0 , arrangeSize . Width - left - right ) ,
Size childDesiredSize = child . DesiredSize ;
Math . Max ( 0.0 , arrangeSize . Height - top - bottom ) ) ;
Rect rcChild = new Rect (
accumulatedLeft ,
// Trim the remaining Rect to the docked size of the element
accumulatedTop ,
// (unless the element should fill the remaining space because
Math . Max ( 0.0 , arrangeSize . Width - ( accumulatedLeft + accumulatedRight ) ) ,
// of LastChildFill)
Math . Max ( 0.0 , arrangeSize . Height - ( accumulatedTop + accumulatedBottom ) ) ) ;
if ( index < dockedCount )
if ( i < nonFillChildrenCount )
{
{
Size desiredSize = element . DesiredSize ;
switch ( DockPanel . GetDock ( ( Control ) child ) )
switch ( GetDock ( element ) )
{
{
case Dock . Left :
case Dock . Left :
left + = desiredSize . Width ;
accumulatedLeft + = childDesiredSize . Width ;
remainingRect = remainingRect . WithWidth ( desiredSize . Width ) ;
rcChild = rcChild . WithWidth ( childDesiredSize . Width ) ;
break ;
case Dock . Top :
top + = desiredSize . Height ;
remainingRect = remainingRect . WithHeight ( desiredSize . Height ) ;
break ;
break ;
case Dock . Right :
case Dock . Right :
right + = desiredSize . Width ;
accumulatedRight + = childDesiredSize . Width ;
remainingRect = new Rect (
rcChild = rcChild . WithX ( Math . Max ( 0.0 , arrangeSize . Width - accumulatedRight ) ) ;
Math . Max ( 0.0 , arrangeSize . Width - right ) ,
rcChild = rcChild . WithWidth ( childDesiredSize . Width ) ;
remainingRect . Y ,
desiredSize . Width ,
remainingRect . Height ) ;
break ;
break ;
case Dock . Top :
accumulatedTop + = childDesiredSize . Height ;
rcChild = rcChild . WithHeight ( childDesiredSize . Height ) ;
break ;
case Dock . Bottom :
case Dock . Bottom :
bottom + = desiredSize . Height ;
accumulatedBottom + = childDesiredSize . Height ;
remainingRect = new Rect (
rcChild = rcChild . WithY ( Math . Max ( 0.0 , arrangeSize . Height - accumulatedBottom ) ) ;
remainingRect . X ,
rcChild = rcChild . WithHeight ( childDesiredSize . Height ) ;
Math . Max ( 0.0 , arrangeSize . Height - bottom ) ,
remainingRect . Width ,
desiredSize . Height ) ;
break ;
break ;
}
}
}
}
element . Arrange ( remainingRect ) ;
child . Arrange ( rcChild ) ;
index + + ;
}
}
return arrangeSize ;
return ( arrangeSize ) ;
}
}
}
}
}
}