|
|
|
@ -15,7 +15,7 @@ namespace Avalonia.Controls |
|
|
|
/// Positions child elements in sequential position from left to right,
|
|
|
|
/// breaking content to the next line at the edge of the containing box.
|
|
|
|
/// Subsequent ordering happens sequentially from top to bottom or from right to left,
|
|
|
|
/// depending on the value of the Orientation property.
|
|
|
|
/// depending on the value of the <see cref="Orientation"/> property.
|
|
|
|
/// </summary>
|
|
|
|
public class WrapPanel : Panel, INavigableContainer |
|
|
|
{ |
|
|
|
@ -25,6 +25,18 @@ namespace Avalonia.Controls |
|
|
|
public static readonly StyledProperty<Orientation> OrientationProperty = |
|
|
|
AvaloniaProperty.Register<WrapPanel, Orientation>(nameof(Orientation), defaultValue: Orientation.Horizontal); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="ItemWidth"/> property.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly StyledProperty<double> ItemWidthProperty = |
|
|
|
AvaloniaProperty.Register<WrapPanel, double>(nameof(ItemWidth), double.NaN); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines the <see cref="ItemHeight"/> property.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly StyledProperty<double> ItemHeightProperty = |
|
|
|
AvaloniaProperty.Register<WrapPanel, double>(nameof(ItemHeight), double.NaN); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes static members of the <see cref="WrapPanel"/> class.
|
|
|
|
/// </summary>
|
|
|
|
@ -42,6 +54,24 @@ namespace Avalonia.Controls |
|
|
|
set { SetValue(OrientationProperty, value); } |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the width of all items in the WrapPanel.
|
|
|
|
/// </summary>
|
|
|
|
public double ItemWidth |
|
|
|
{ |
|
|
|
get { return GetValue(ItemWidthProperty); } |
|
|
|
set { SetValue(ItemWidthProperty, value); } |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the height of all items in the WrapPanel.
|
|
|
|
/// </summary>
|
|
|
|
public double ItemHeight |
|
|
|
{ |
|
|
|
get { return GetValue(ItemHeightProperty); } |
|
|
|
set { SetValue(ItemHeightProperty, value); } |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the next control in the specified direction.
|
|
|
|
/// </summary>
|
|
|
|
@ -51,7 +81,9 @@ namespace Avalonia.Controls |
|
|
|
/// <returns>The control.</returns>
|
|
|
|
IInputElement INavigableContainer.GetControl(NavigationDirection direction, IInputElement from, bool wrap) |
|
|
|
{ |
|
|
|
var horiz = Orientation == Orientation.Horizontal; |
|
|
|
var orientation = Orientation; |
|
|
|
var children = Children; |
|
|
|
bool horiz = orientation == Orientation.Horizontal; |
|
|
|
int index = Children.IndexOf((IControl)from); |
|
|
|
|
|
|
|
switch (direction) |
|
|
|
@ -60,7 +92,7 @@ namespace Avalonia.Controls |
|
|
|
index = 0; |
|
|
|
break; |
|
|
|
case NavigationDirection.Last: |
|
|
|
index = Children.Count - 1; |
|
|
|
index = children.Count - 1; |
|
|
|
break; |
|
|
|
case NavigationDirection.Next: |
|
|
|
++index; |
|
|
|
@ -82,9 +114,9 @@ namespace Avalonia.Controls |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (index >= 0 && index < Children.Count) |
|
|
|
if (index >= 0 && index < children.Count) |
|
|
|
{ |
|
|
|
return Children[index]; |
|
|
|
return children[index]; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
@ -95,116 +127,140 @@ namespace Avalonia.Controls |
|
|
|
/// <inheritdoc/>
|
|
|
|
protected override Size MeasureOverride(Size constraint) |
|
|
|
{ |
|
|
|
var curLineSize = new UVSize(Orientation); |
|
|
|
var panelSize = new UVSize(Orientation); |
|
|
|
var uvConstraint = new UVSize(Orientation, constraint.Width, constraint.Height); |
|
|
|
|
|
|
|
var childConstraint = new Size(constraint.Width, constraint.Height); |
|
|
|
|
|
|
|
for (int i = 0, count = Children.Count; i < count; i++) |
|
|
|
double itemWidth = ItemWidth; |
|
|
|
double itemHeight = ItemHeight; |
|
|
|
var orientation = Orientation; |
|
|
|
var children = Children; |
|
|
|
var curLineSize = new UVSize(orientation); |
|
|
|
var panelSize = new UVSize(orientation); |
|
|
|
var uvConstraint = new UVSize(orientation, constraint.Width, constraint.Height); |
|
|
|
bool itemWidthSet = !double.IsNaN(itemWidth); |
|
|
|
bool itemHeightSet = !double.IsNaN(itemHeight); |
|
|
|
|
|
|
|
var childConstraint = new Size( |
|
|
|
itemWidthSet ? itemWidth : constraint.Width, |
|
|
|
itemHeightSet ? itemHeight : constraint.Height); |
|
|
|
|
|
|
|
for (int i = 0, count = children.Count; i < count; i++) |
|
|
|
{ |
|
|
|
var child = Children[i]; |
|
|
|
if (child == null) continue; |
|
|
|
|
|
|
|
//Flow passes its own constrint to children
|
|
|
|
child.Measure(childConstraint); |
|
|
|
|
|
|
|
//this is the size of the child in UV space
|
|
|
|
var sz = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); |
|
|
|
|
|
|
|
if (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvConstraint.U)) //need to switch to another line
|
|
|
|
var child = children[i]; |
|
|
|
if (child != null) |
|
|
|
{ |
|
|
|
panelSize.U = Max(curLineSize.U, panelSize.U); |
|
|
|
panelSize.V += curLineSize.V; |
|
|
|
curLineSize = sz; |
|
|
|
// Flow passes its own constraint to children
|
|
|
|
child.Measure(childConstraint); |
|
|
|
|
|
|
|
// This is the size of the child in UV space
|
|
|
|
var sz = new UVSize(orientation, |
|
|
|
itemWidthSet ? itemWidth : child.DesiredSize.Width, |
|
|
|
itemHeightSet ? itemHeight : child.DesiredSize.Height); |
|
|
|
|
|
|
|
if (MathUtilities.GreaterThan(sz.U, uvConstraint.U)) //the element is wider then the constrint - give it a separate line
|
|
|
|
if (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvConstraint.U)) // Need to switch to another line
|
|
|
|
{ |
|
|
|
panelSize.U = Max(sz.U, panelSize.U); |
|
|
|
panelSize.V += sz.V; |
|
|
|
curLineSize = new UVSize(Orientation); |
|
|
|
panelSize.U = Max(curLineSize.U, panelSize.U); |
|
|
|
panelSize.V += curLineSize.V; |
|
|
|
curLineSize = sz; |
|
|
|
|
|
|
|
if (MathUtilities.GreaterThan(sz.U, uvConstraint.U)) // The element is wider then the constraint - give it a separate line
|
|
|
|
{ |
|
|
|
panelSize.U = Max(sz.U, panelSize.U); |
|
|
|
panelSize.V += sz.V; |
|
|
|
curLineSize = new UVSize(orientation); |
|
|
|
} |
|
|
|
} |
|
|
|
else // Continue to accumulate a line
|
|
|
|
{ |
|
|
|
curLineSize.U += sz.U; |
|
|
|
curLineSize.V = Max(sz.V, curLineSize.V); |
|
|
|
} |
|
|
|
} |
|
|
|
else //continue to accumulate a line
|
|
|
|
{ |
|
|
|
curLineSize.U += sz.U; |
|
|
|
curLineSize.V = Max(sz.V, curLineSize.V); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//the last line size, if any should be added
|
|
|
|
// The last line size, if any should be added
|
|
|
|
panelSize.U = Max(curLineSize.U, panelSize.U); |
|
|
|
panelSize.V += curLineSize.V; |
|
|
|
|
|
|
|
//go from UV space to W/H space
|
|
|
|
// Go from UV space to W/H space
|
|
|
|
return new Size(panelSize.Width, panelSize.Height); |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
protected override Size ArrangeOverride(Size finalSize) |
|
|
|
{ |
|
|
|
double itemWidth = ItemWidth; |
|
|
|
double itemHeight = ItemHeight; |
|
|
|
var orientation = Orientation; |
|
|
|
var children = Children; |
|
|
|
int firstInLine = 0; |
|
|
|
double accumulatedV = 0; |
|
|
|
UVSize curLineSize = new UVSize(Orientation); |
|
|
|
UVSize uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height); |
|
|
|
|
|
|
|
for (int i = 0; i < Children.Count; i++) |
|
|
|
double itemU = orientation == Orientation.Horizontal ? itemWidth : itemHeight; |
|
|
|
var curLineSize = new UVSize(orientation); |
|
|
|
var uvFinalSize = new UVSize(orientation, finalSize.Width, finalSize.Height); |
|
|
|
bool itemWidthSet = !double.IsNaN(itemWidth); |
|
|
|
bool itemHeightSet = !double.IsNaN(itemHeight); |
|
|
|
bool useItemU = orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet; |
|
|
|
|
|
|
|
for (int i = 0; i < children.Count; i++) |
|
|
|
{ |
|
|
|
var child = Children[i]; |
|
|
|
if (child == null) continue; |
|
|
|
var child = children[i]; |
|
|
|
if (child != null) |
|
|
|
{ |
|
|
|
var sz = new UVSize(orientation, |
|
|
|
itemWidthSet ? itemWidth : child.DesiredSize.Width, |
|
|
|
itemHeightSet ? itemHeight : child.DesiredSize.Height); |
|
|
|
|
|
|
|
var sz = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); |
|
|
|
if (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvFinalSize.U)) // Need to switch to another line
|
|
|
|
{ |
|
|
|
ArrangeLine(accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU); |
|
|
|
|
|
|
|
if (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvFinalSize.U)) //need to switch to another line
|
|
|
|
{ |
|
|
|
arrangeLine(accumulatedV, curLineSize.V, firstInLine, i); |
|
|
|
accumulatedV += curLineSize.V; |
|
|
|
curLineSize = sz; |
|
|
|
|
|
|
|
accumulatedV += curLineSize.V; |
|
|
|
curLineSize = sz; |
|
|
|
if (MathUtilities.GreaterThan(sz.U, uvFinalSize.U)) // The element is wider then the constraint - give it a separate line
|
|
|
|
{ |
|
|
|
// Switch to next line which only contain one element
|
|
|
|
ArrangeLine(accumulatedV, sz.V, i, ++i, useItemU, itemU); |
|
|
|
|
|
|
|
if (MathUtilities.GreaterThan(sz.U, uvFinalSize.U)) //the element is wider then the constraint - give it a separate line
|
|
|
|
accumulatedV += sz.V; |
|
|
|
curLineSize = new UVSize(orientation); |
|
|
|
} |
|
|
|
firstInLine = i; |
|
|
|
} |
|
|
|
else // Continue to accumulate a line
|
|
|
|
{ |
|
|
|
//switch to next line which only contain one element
|
|
|
|
arrangeLine(accumulatedV, sz.V, i, ++i); |
|
|
|
|
|
|
|
accumulatedV += sz.V; |
|
|
|
curLineSize = new UVSize(Orientation); |
|
|
|
curLineSize.U += sz.U; |
|
|
|
curLineSize.V = Max(sz.V, curLineSize.V); |
|
|
|
} |
|
|
|
firstInLine = i; |
|
|
|
} |
|
|
|
else //continue to accumulate a line
|
|
|
|
{ |
|
|
|
curLineSize.U += sz.U; |
|
|
|
curLineSize.V = Max(sz.V, curLineSize.V); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//arrange the last line, if any
|
|
|
|
if (firstInLine < Children.Count) |
|
|
|
// Arrange the last line, if any
|
|
|
|
if (firstInLine < children.Count) |
|
|
|
{ |
|
|
|
arrangeLine(accumulatedV, curLineSize.V, firstInLine, Children.Count); |
|
|
|
ArrangeLine(accumulatedV, curLineSize.V, firstInLine, children.Count, useItemU, itemU); |
|
|
|
} |
|
|
|
|
|
|
|
return finalSize; |
|
|
|
} |
|
|
|
|
|
|
|
private void arrangeLine(double v, double lineV, int start, int end) |
|
|
|
private void ArrangeLine(double v, double lineV, int start, int end, bool useItemU, double itemU) |
|
|
|
{ |
|
|
|
var orientation = Orientation; |
|
|
|
var children = Children; |
|
|
|
double u = 0; |
|
|
|
bool isHorizontal = (Orientation == Orientation.Horizontal); |
|
|
|
bool isHorizontal = orientation == Orientation.Horizontal; |
|
|
|
|
|
|
|
for (int i = start; i < end; i++) |
|
|
|
{ |
|
|
|
var child = Children[i]; |
|
|
|
var child = children[i]; |
|
|
|
if (child != null) |
|
|
|
{ |
|
|
|
UVSize childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height); |
|
|
|
double layoutSlotU = childSize.U; |
|
|
|
var childSize = new UVSize(orientation, child.DesiredSize.Width, child.DesiredSize.Height); |
|
|
|
double layoutSlotU = useItemU ? itemU : childSize.U; |
|
|
|
child.Arrange(new Rect( |
|
|
|
(isHorizontal ? u : v), |
|
|
|
(isHorizontal ? v : u), |
|
|
|
(isHorizontal ? layoutSlotU : lineV), |
|
|
|
(isHorizontal ? lineV : layoutSlotU))); |
|
|
|
isHorizontal ? u : v, |
|
|
|
isHorizontal ? v : u, |
|
|
|
isHorizontal ? layoutSlotU : lineV, |
|
|
|
isHorizontal ? lineV : layoutSlotU)); |
|
|
|
u += layoutSlotU; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -232,12 +288,12 @@ namespace Avalonia.Controls |
|
|
|
|
|
|
|
internal double Width |
|
|
|
{ |
|
|
|
get { return (_orientation == Orientation.Horizontal ? U : V); } |
|
|
|
get { return _orientation == Orientation.Horizontal ? U : V; } |
|
|
|
set { if (_orientation == Orientation.Horizontal) U = value; else V = value; } |
|
|
|
} |
|
|
|
internal double Height |
|
|
|
{ |
|
|
|
get { return (_orientation == Orientation.Horizontal ? V : U); } |
|
|
|
get { return _orientation == Orientation.Horizontal ? V : U; } |
|
|
|
set { if (_orientation == Orientation.Horizontal) V = value; else U = value; } |
|
|
|
} |
|
|
|
} |
|
|
|
|