Browse Source

Initial port of WPF WrapPanel

pull/2596/head
Wiesław Šoltés 7 years ago
parent
commit
2b6fdc1531
  1. 195
      src/Avalonia.Controls/WrapPanel.cs

195
src/Avalonia.Controls/WrapPanel.cs

@ -92,109 +92,127 @@ namespace Avalonia.Controls
} }
} }
private UVSize CreateUVSize(Size size) => new UVSize(Orientation, size);
private UVSize CreateUVSize() => new UVSize(Orientation);
/// <inheritdoc/> /// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size constraint)
{ {
var desiredSize = CreateUVSize(); var curLineSize = new UVSize(Orientation);
var lineSize = CreateUVSize(); var panelSize = new UVSize(Orientation);
var uvAvailableSize = CreateUVSize(availableSize); var uvConstraint = new UVSize(Orientation, constraint.Width, constraint.Height);
foreach (var child in Children) var childConstraint = new Size(constraint.Width, constraint.Height);
for (int i = 0, count = Children.Count; i < count; i++)
{ {
child.Measure(availableSize); var child = Children[i];
var childSize = CreateUVSize(child.DesiredSize); if (child == null) continue;
if (lineSize.U + childSize.U <= uvAvailableSize.U) // same line
//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 ((curLineSize.U + sz.U) > uvConstraint.U) //need to switch to another line
{ {
lineSize.U += childSize.U; panelSize.U = Max(curLineSize.U, panelSize.U);
lineSize.V = Max(lineSize.V, childSize.V); panelSize.V += curLineSize.V;
curLineSize = sz;
if (sz.U > uvConstraint.U) //the element is wider then the constrint - give it a separate line
{
panelSize.U = Max(sz.U, panelSize.U);
panelSize.V += sz.V;
curLineSize = new UVSize(Orientation);
}
} }
else // moving to next line else //continue to accumulate a line
{ {
desiredSize.U = Max(lineSize.U, uvAvailableSize.U); curLineSize.U += sz.U;
desiredSize.V += lineSize.V; curLineSize.V = Max(sz.V, curLineSize.V);
lineSize = childSize;
} }
} }
// last element
desiredSize.U = Max(lineSize.U, desiredSize.U);
desiredSize.V += lineSize.V;
return desiredSize.ToSize(); //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
return new Size(panelSize.Width, panelSize.Height);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
int firstInLine = 0;
double accumulatedV = 0; double accumulatedV = 0;
var uvFinalSize = CreateUVSize(finalSize); UVSize curLineSize = new UVSize(Orientation);
var lineSize = CreateUVSize(); UVSize uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height);
int firstChildInLineIndex = 0;
for (int index = 0; index < Children.Count; index++) for (int i = 0; i < Children.Count; i++)
{ {
var child = Children[index]; var child = Children[i];
var childSize = CreateUVSize(child.DesiredSize); if (child == null) continue;
if (lineSize.U + childSize.U <= uvFinalSize.U) // same line
var sz = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
if ((curLineSize.U + sz.U) > uvFinalSize.U) //need to switch to another line
{ {
lineSize.U += childSize.U; arrangeLine(accumulatedV, curLineSize.V, firstInLine, i);
lineSize.V = Max(lineSize.V, childSize.V);
accumulatedV += curLineSize.V;
curLineSize = sz;
if (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);
accumulatedV += sz.V;
curLineSize = new UVSize(Orientation);
}
firstInLine = i;
} }
else // moving to next line else //continue to accumulate a line
{ {
var controlsInLine = GetControlsBetween(firstChildInLineIndex, index); curLineSize.U += sz.U;
ArrangeLine(accumulatedV, lineSize.V, controlsInLine); curLineSize.V = Max(sz.V, curLineSize.V);
accumulatedV += lineSize.V;
lineSize = childSize;
firstChildInLineIndex = index;
} }
} }
if (firstChildInLineIndex < Children.Count) //arrange the last line, if any
if (firstInLine < Children.Count)
{ {
var controlsInLine = GetControlsBetween(firstChildInLineIndex, Children.Count); arrangeLine(accumulatedV, curLineSize.V, firstInLine, Children.Count);
ArrangeLine(accumulatedV, lineSize.V, controlsInLine);
} }
return finalSize;
}
private IEnumerable<IControl> GetControlsBetween(int first, int last) return finalSize;
{
return Children.Skip(first).Take(last - first);
} }
private void ArrangeLine(double v, double lineV, IEnumerable<IControl> controls) private void arrangeLine(double v, double lineV, int start, int end)
{ {
double u = 0; double u = 0;
bool isHorizontal = (Orientation == Orientation.Horizontal); bool isHorizontal = (Orientation == Orientation.Horizontal);
foreach (var child in controls)
for (int i = 0, count = Children.Count; i < count; i++)
{ {
var childSize = CreateUVSize(child.DesiredSize); var child = Children[i];
var x = isHorizontal ? u : v; if (child != null)
var y = isHorizontal ? v : u; {
var width = isHorizontal ? childSize.U : lineV; UVSize childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
var height = isHorizontal ? lineV : childSize.U; double layoutSlotU = childSize.U;
child.Arrange(new Rect(x, y, width, height)); child.Arrange(new Rect(
u += childSize.U; (isHorizontal ? u : v),
(isHorizontal ? v : u),
(isHorizontal ? layoutSlotU : lineV),
(isHorizontal ? lineV : layoutSlotU)));
u += layoutSlotU;
}
} }
} }
/// <summary>
/// Used to not not write separate code for horizontal and vertical orientation.
/// U is direction in line. (x if orientation is horizontal)
/// V is direction of lines. (y if orientation is horizontal)
/// </summary>
[DebuggerDisplay("U = {U} V = {V}")]
private struct UVSize private struct UVSize
{ {
private readonly Orientation _orientation; internal UVSize(Orientation orientation, double width, double height)
internal double U;
internal double V;
private UVSize(Orientation orientation, double width, double height)
{ {
U = V = 0d; U = V = 0d;
_orientation = orientation; _orientation = orientation;
@ -202,52 +220,25 @@ namespace Avalonia.Controls
Height = height; Height = height;
} }
internal UVSize(Orientation orientation, Size size)
: this(orientation, size.Width, size.Height)
{
}
internal UVSize(Orientation orientation) internal UVSize(Orientation orientation)
{ {
U = V = 0d; U = V = 0d;
_orientation = orientation; _orientation = orientation;
} }
private double Width internal double U;
internal double V;
private Orientation _orientation;
internal double Width
{ {
get { return (_orientation == Orientation.Horizontal ? U : V); } get { return (_orientation == Orientation.Horizontal ? U : V); }
set set { if (_orientation == Orientation.Horizontal) U = value; else V = value; }
{
if (_orientation == Orientation.Horizontal)
{
U = value;
}
else
{
V = value;
}
}
} }
internal double Height
private double Height
{ {
get { return (_orientation == Orientation.Horizontal ? V : U); } get { return (_orientation == Orientation.Horizontal ? V : U); }
set set { if (_orientation == Orientation.Horizontal) V = value; else U = value; }
{
if (_orientation == Orientation.Horizontal)
{
V = value;
}
else
{
U = value;
}
}
}
public Size ToSize()
{
return new Size(Width, Height);
} }
} }
} }

Loading…
Cancel
Save