|
|
|
@ -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 |
|
|
|
{ |
|
|
|
using System; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the available docking modes for a control in a <see cref="DockPanel"/>.
|
|
|
|
/// </summary>
|
|
|
|
@ -70,107 +75,137 @@ namespace Avalonia.Controls |
|
|
|
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) |
|
|
|
{ |
|
|
|
double usedWidth = 0.0; |
|
|
|
double usedHeight = 0.0; |
|
|
|
double maximumWidth = 0.0; |
|
|
|
double maximumHeight = 0.0; |
|
|
|
var children = Children; |
|
|
|
|
|
|
|
double parentWidth = 0; // Our current required width due to children thus far.
|
|
|
|
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
|
|
|
|
foreach (Control element in Children) |
|
|
|
for (int i = 0, count = children.Count; i < count; ++i) |
|
|
|
{ |
|
|
|
// Get the child's desired size
|
|
|
|
Size remainingSize = new Size( |
|
|
|
Math.Max(0.0, constraint.Width - usedWidth), |
|
|
|
Math.Max(0.0, constraint.Height - usedHeight)); |
|
|
|
element.Measure(remainingSize); |
|
|
|
Size desiredSize = element.DesiredSize; |
|
|
|
|
|
|
|
// Decrease the remaining space for the rest of the children
|
|
|
|
switch (GetDock(element)) |
|
|
|
var child = children[i]; |
|
|
|
Size childConstraint; // Contains the suggested input constraint for this child.
|
|
|
|
Size childDesiredSize; // Contains the return size from child measure.
|
|
|
|
|
|
|
|
if (child == null) |
|
|
|
{ continue; } |
|
|
|
|
|
|
|
// Child constraint is the remaining size; this is total size minus size consumed by previous children.
|
|
|
|
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.Right: |
|
|
|
maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height); |
|
|
|
usedWidth += desiredSize.Width; |
|
|
|
parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height); |
|
|
|
accumulatedWidth += childDesiredSize.Width; |
|
|
|
break; |
|
|
|
|
|
|
|
case Dock.Top: |
|
|
|
case Dock.Bottom: |
|
|
|
maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width); |
|
|
|
usedHeight += desiredSize.Height; |
|
|
|
parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width); |
|
|
|
accumulatedHeight += childDesiredSize.Height; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
maximumWidth = Math.Max(maximumWidth, usedWidth); |
|
|
|
maximumHeight = Math.Max(maximumHeight, usedHeight); |
|
|
|
return new Size(maximumWidth, maximumHeight); |
|
|
|
// Make sure the final accumulated size is reflected in parentSize.
|
|
|
|
parentWidth = Math.Max(parentWidth, accumulatedWidth); |
|
|
|
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) |
|
|
|
{ |
|
|
|
double left = 0.0; |
|
|
|
double top = 0.0; |
|
|
|
double right = 0.0; |
|
|
|
double bottom = 0.0; |
|
|
|
|
|
|
|
// Arrange each of the Children
|
|
|
|
var children = Children; |
|
|
|
int dockedCount = children.Count - (LastChildFill ? 1 : 0); |
|
|
|
int index = 0; |
|
|
|
int totalChildrenCount = children.Count; |
|
|
|
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
|
|
|
|
Rect remainingRect = new Rect( |
|
|
|
left, |
|
|
|
top, |
|
|
|
Math.Max(0.0, arrangeSize.Width - left - right), |
|
|
|
Math.Max(0.0, arrangeSize.Height - top - bottom)); |
|
|
|
|
|
|
|
// Trim the remaining Rect to the docked size of the element
|
|
|
|
// (unless the element should fill the remaining space because
|
|
|
|
// of LastChildFill)
|
|
|
|
if (index < dockedCount) |
|
|
|
var child = children[i]; |
|
|
|
if (child == null) |
|
|
|
{ continue; } |
|
|
|
|
|
|
|
Size childDesiredSize = child.DesiredSize; |
|
|
|
Rect rcChild = new Rect( |
|
|
|
accumulatedLeft, |
|
|
|
accumulatedTop, |
|
|
|
Math.Max(0.0, arrangeSize.Width - (accumulatedLeft + accumulatedRight)), |
|
|
|
Math.Max(0.0, arrangeSize.Height - (accumulatedTop + accumulatedBottom))); |
|
|
|
|
|
|
|
if (i < nonFillChildrenCount) |
|
|
|
{ |
|
|
|
Size desiredSize = element.DesiredSize; |
|
|
|
switch (GetDock(element)) |
|
|
|
switch (DockPanel.GetDock((Control)child)) |
|
|
|
{ |
|
|
|
case Dock.Left: |
|
|
|
left += desiredSize.Width; |
|
|
|
remainingRect = remainingRect.WithWidth(desiredSize.Width); |
|
|
|
break; |
|
|
|
case Dock.Top: |
|
|
|
top += desiredSize.Height; |
|
|
|
remainingRect = remainingRect.WithHeight(desiredSize.Height); |
|
|
|
accumulatedLeft += childDesiredSize.Width; |
|
|
|
rcChild = rcChild.WithWidth(childDesiredSize.Width); |
|
|
|
break; |
|
|
|
|
|
|
|
case Dock.Right: |
|
|
|
right += desiredSize.Width; |
|
|
|
remainingRect = new Rect( |
|
|
|
Math.Max(0.0, arrangeSize.Width - right), |
|
|
|
remainingRect.Y, |
|
|
|
desiredSize.Width, |
|
|
|
remainingRect.Height); |
|
|
|
accumulatedRight += childDesiredSize.Width; |
|
|
|
rcChild = rcChild.WithX(Math.Max(0.0, arrangeSize.Width - accumulatedRight)); |
|
|
|
rcChild = rcChild.WithWidth(childDesiredSize.Width); |
|
|
|
break; |
|
|
|
|
|
|
|
case Dock.Top: |
|
|
|
accumulatedTop += childDesiredSize.Height; |
|
|
|
rcChild = rcChild.WithHeight(childDesiredSize.Height); |
|
|
|
break; |
|
|
|
|
|
|
|
case Dock.Bottom: |
|
|
|
bottom += desiredSize.Height; |
|
|
|
remainingRect = new Rect( |
|
|
|
remainingRect.X, |
|
|
|
Math.Max(0.0, arrangeSize.Height - bottom), |
|
|
|
remainingRect.Width, |
|
|
|
desiredSize.Height); |
|
|
|
accumulatedBottom += childDesiredSize.Height; |
|
|
|
rcChild = rcChild.WithY(Math.Max(0.0, arrangeSize.Height - accumulatedBottom)); |
|
|
|
rcChild = rcChild.WithHeight(childDesiredSize.Height); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
element.Arrange(remainingRect); |
|
|
|
index++; |
|
|
|
child.Arrange(rcChild); |
|
|
|
} |
|
|
|
|
|
|
|
return arrangeSize; |
|
|
|
return (arrangeSize); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|