зеркало из https://github.com/DeGsoft/maui-linux.git
GridLayout with absolute and auto (#513)
* Grid stuff, in progress * Clean up after rebase; fix arrange call in layout manager * Adding some notes * Update ArrangeChildren method calls * Add missing TypeConverters * Update test values in light of new Arrange call * Formatting fix Co-authored-by: Rui Marinho <me@ruimarinho.net>
This commit is contained in:
Родитель
05ba6f7df0
Коммит
fccf7cc07f
|
@ -32,6 +32,9 @@ namespace Maui.Controls.Sample.Pages
|
|||
var verticalStack = new VerticalStackLayout() { Spacing = 5, BackgroundColor = Color.AntiqueWhite };
|
||||
var horizontalStack = new HorizontalStackLayout() { Spacing = 2, BackgroundColor = Color.CornflowerBlue };
|
||||
|
||||
|
||||
verticalStack.Add(CreateSampleGrid());
|
||||
|
||||
verticalStack.Add(new Label { Text = " ", Padding = new Thickness(10) });
|
||||
var label = new Label { Text = "End-aligned text", BackgroundColor = Color.Fuchsia, HorizontalTextAlignment = TextAlignment.End };
|
||||
label.Margin = new Thickness(15, 10, 20, 15);
|
||||
|
@ -47,7 +50,6 @@ namespace Maui.Controls.Sample.Pages
|
|||
verticalStack.Add(new Label { Text = loremIpsum, MaxLines = 2, LineBreakMode = LineBreakMode.TailTruncation });
|
||||
verticalStack.Add(new Label { Text = "This should have five times the line height!", LineHeight = 5 });
|
||||
|
||||
|
||||
var paddingButton = new Button
|
||||
{
|
||||
Padding = new Thickness(40),
|
||||
|
@ -158,6 +160,7 @@ namespace Maui.Controls.Sample.Pages
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
void SetupCompatibilityLayout()
|
||||
{
|
||||
var verticalStack = new StackLayout() { Spacing = 5, BackgroundColor = Color.AntiqueWhite };
|
||||
|
@ -202,5 +205,36 @@ namespace Maui.Controls.Sample.Pages
|
|||
}
|
||||
|
||||
public IView View { get => (IView)Content; set => Content = (View)value; }
|
||||
|
||||
IView CreateSampleGrid()
|
||||
{
|
||||
var layout = new Microsoft.Maui.Controls.Layout2.GridLayout() { ColumnSpacing = 5, RowSpacing = 8 };
|
||||
|
||||
layout.AddRowDefinition(new RowDefinition() { Height = new GridLength(40) });
|
||||
layout.AddRowDefinition(new RowDefinition() { Height = GridLength.Auto });
|
||||
|
||||
layout.AddColumnDefinition(new ColumnDefinition() { Width = new GridLength(100) });
|
||||
layout.AddColumnDefinition(new ColumnDefinition() { Width = new GridLength(100) });
|
||||
|
||||
var topLeft = new Label { Text = "Top Left", BackgroundColor = Color.LightBlue };
|
||||
layout.Add(topLeft);
|
||||
|
||||
var bottomLeft = new Label { Text = "Bottom Left", BackgroundColor = Color.Lavender };
|
||||
layout.Add(bottomLeft);
|
||||
layout.SetRow(bottomLeft, 1);
|
||||
|
||||
var topRight = new Label { Text = "Top Right", BackgroundColor = Color.Orange };
|
||||
layout.Add(topRight);
|
||||
layout.SetColumn(topRight, 1);
|
||||
|
||||
var bottomRight = new Label { Text = "Bottom Right", BackgroundColor = Color.MediumPurple };
|
||||
layout.Add(bottomRight);
|
||||
layout.SetRow(bottomRight, 1);
|
||||
layout.SetColumn(bottomRight, 1);
|
||||
|
||||
layout.BackgroundColor = Color.Chartreuse;
|
||||
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ using System;
|
|||
|
||||
namespace Microsoft.Maui.Controls
|
||||
{
|
||||
public sealed class ColumnDefinition : BindableObject, IDefinition
|
||||
public sealed class ColumnDefinition : BindableObject, IDefinition, IGridColumnDefinition
|
||||
{
|
||||
public static readonly BindableProperty WidthProperty = BindableProperty.Create("Width", typeof(GridLength), typeof(ColumnDefinition), new GridLength(1, GridUnitType.Star),
|
||||
propertyChanged: (bindable, oldValue, newValue) => ((ColumnDefinition)bindable).OnSizeChanged());
|
||||
|
@ -12,6 +12,7 @@ namespace Microsoft.Maui.Controls
|
|||
MinimumWidth = -1;
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(GridLengthTypeConverter))]
|
||||
public GridLength Width
|
||||
{
|
||||
get { return (GridLength)GetValue(WidthProperty); }
|
||||
|
|
|
@ -5,9 +5,10 @@ using System.ComponentModel;
|
|||
using System.Linq;
|
||||
using Microsoft.Maui.Controls.Internals;
|
||||
|
||||
|
||||
namespace Microsoft.Maui.Controls
|
||||
{
|
||||
public partial class Grid : Layout<View>, IGridController, IElementConfiguration<Grid>
|
||||
public partial class Grid : Layout<View>, IGridController, IElementConfiguration<Grid>, IGridLayout
|
||||
{
|
||||
public static readonly BindableProperty RowProperty = BindableProperty.CreateAttached("Row", typeof(int), typeof(Grid), default(int), validateValue: (bindable, value) => (int)value >= 0);
|
||||
|
||||
|
@ -381,5 +382,48 @@ namespace Microsoft.Maui.Controls
|
|||
Parent.ColumnDefinitions.Count
|
||||
);
|
||||
}
|
||||
|
||||
int IGridLayout.GetRow(IView view)
|
||||
{
|
||||
if (view is BindableObject bo)
|
||||
{
|
||||
return GetRow(bo);
|
||||
}
|
||||
|
||||
throw new InvalidEnumArgumentException($"{nameof(view)} must be a BindableObject");
|
||||
}
|
||||
|
||||
int IGridLayout.GetColumn(IView view)
|
||||
{
|
||||
if (view is BindableObject bo)
|
||||
{
|
||||
return GetColumn(bo);
|
||||
}
|
||||
|
||||
throw new InvalidEnumArgumentException($"{nameof(view)} must be a BindableObject");
|
||||
}
|
||||
|
||||
int IGridLayout.GetRowSpan(IView view)
|
||||
{
|
||||
if (view is BindableObject bo)
|
||||
{
|
||||
return GetRowSpan(bo);
|
||||
}
|
||||
|
||||
throw new InvalidEnumArgumentException($"{nameof(view)} must be a BindableObject");
|
||||
}
|
||||
|
||||
int IGridLayout.GetColumnSpan(IView view)
|
||||
{
|
||||
if (view is BindableObject bo)
|
||||
{
|
||||
return GetColumnSpan(bo);
|
||||
}
|
||||
|
||||
throw new InvalidEnumArgumentException($"{nameof(view)} must be a BindableObject");
|
||||
}
|
||||
|
||||
IReadOnlyList<IGridRowDefinition> IGridLayout.RowDefinitions => RowDefinitions.ToList();
|
||||
IReadOnlyList<IGridColumnDefinition> IGridLayout.ColumnDefinitions => ColumnDefinitions.ToList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Maui.Layouts;
|
||||
|
||||
// This is a temporary namespace until we rename everything and move the legacy layouts
|
||||
namespace Microsoft.Maui.Controls.Layout2
|
||||
{
|
||||
public class GridLayout : Layout, IGridLayout
|
||||
{
|
||||
List<IGridRowDefinition> _rowDefinitions = new List<IGridRowDefinition>();
|
||||
List<IGridColumnDefinition> _columnDefinitions = new List<IGridColumnDefinition>();
|
||||
|
||||
public IReadOnlyList<IGridRowDefinition> RowDefinitions => _rowDefinitions;
|
||||
public IReadOnlyList<IGridColumnDefinition> ColumnDefinitions => _columnDefinitions;
|
||||
|
||||
public double RowSpacing { get; set; }
|
||||
public double ColumnSpacing { get; set; }
|
||||
|
||||
Dictionary<IView, GridInfo> _viewInfo = new Dictionary<IView, GridInfo>();
|
||||
|
||||
// TODO ezhart This needs to override Remove and clean up any row/column/span info for the removed child
|
||||
|
||||
public int GetColumn(IView view)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
return gridInfo.Col;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int GetColumnSpan(IView view)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
return gridInfo.ColSpan;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int GetRow(IView view)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
return gridInfo.Row;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int GetRowSpan(IView view)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
return gridInfo.RowSpan;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected override ILayoutManager CreateLayoutManager() => new GridLayoutManager(this);
|
||||
|
||||
public void AddRowDefinition(IGridRowDefinition gridRowDefinition)
|
||||
{
|
||||
_rowDefinitions.Add(gridRowDefinition);
|
||||
}
|
||||
|
||||
public void AddColumnDefinition(IGridColumnDefinition gridColumnDefinition)
|
||||
{
|
||||
_columnDefinitions.Add(gridColumnDefinition);
|
||||
}
|
||||
|
||||
public void SetRow(IView view, int row)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
gridInfo.Row = row;
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewInfo[view] = new GridInfo { Row = row };
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRowSpan(IView view, int span)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
gridInfo.RowSpan = span;
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewInfo[view] = new GridInfo { RowSpan = span };
|
||||
}
|
||||
}
|
||||
|
||||
public void SetColumn(IView view, int col)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
gridInfo.Col = col;
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewInfo[view] = new GridInfo { Col = col };
|
||||
}
|
||||
}
|
||||
|
||||
public void SetColumnSpan(IView view, int span)
|
||||
{
|
||||
if (_viewInfo.TryGetValue(view, out GridInfo gridInfo))
|
||||
{
|
||||
gridInfo.ColSpan = span;
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewInfo[view] = new GridInfo { ColSpan = span };
|
||||
}
|
||||
}
|
||||
|
||||
class GridInfo
|
||||
{
|
||||
public int Row { get; set; }
|
||||
public int Col { get; set; }
|
||||
public int RowSpan { get; set; } = 1;
|
||||
public int ColSpan { get; set; } = 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,6 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Maui.Layouts;
|
||||
|
||||
|
||||
|
||||
// This is a temporary namespace until we rename everything and move the legacy layouts
|
||||
namespace Microsoft.Maui.Controls.Layout2
|
||||
{
|
||||
|
@ -61,7 +59,7 @@ namespace Microsoft.Maui.Controls.Layout2
|
|||
|
||||
Arrange(bounds);
|
||||
|
||||
LayoutManager.Arrange(Frame);
|
||||
LayoutManager.ArrangeChildren(Frame);
|
||||
IsArrangeValid = true;
|
||||
Handler?.SetFrame(Frame);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Linq;
|
||||
|
||||
using System.Linq;
|
||||
|
||||
// This is a temporary namespace until we rename everything and move the legacy layouts
|
||||
namespace Microsoft.Maui.Controls.Layout2
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using Microsoft.Maui.Handlers;
|
||||
using Microsoft.Maui.Layouts;
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
|||
|
||||
namespace Microsoft.Maui.Controls
|
||||
{
|
||||
public sealed class RowDefinition : BindableObject, IDefinition
|
||||
public sealed class RowDefinition : BindableObject, IDefinition, IGridRowDefinition
|
||||
{
|
||||
public static readonly BindableProperty HeightProperty = BindableProperty.Create("Height", typeof(GridLength), typeof(RowDefinition), new GridLength(1, GridUnitType.Star),
|
||||
propertyChanged: (bindable, oldValue, newValue) => ((RowDefinition)bindable).OnSizeChanged());
|
||||
|
@ -12,6 +12,7 @@ namespace Microsoft.Maui.Controls
|
|||
MinimumHeight = -1;
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(GridLengthTypeConverter))]
|
||||
public GridLength Height
|
||||
{
|
||||
get { return (GridLength)GetValue(HeightProperty); }
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
namespace Microsoft.Maui
|
||||
{
|
||||
public interface IGridColumnDefinition
|
||||
{
|
||||
GridLength Width { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public interface IGridLayout : ILayout
|
||||
{
|
||||
IReadOnlyList<IGridRowDefinition> RowDefinitions { get; }
|
||||
IReadOnlyList<IGridColumnDefinition> ColumnDefinitions { get; }
|
||||
|
||||
double RowSpacing { get; }
|
||||
double ColumnSpacing { get; }
|
||||
|
||||
int GetRow(IView view);
|
||||
int GetRowSpan(IView view);
|
||||
|
||||
int GetColumn(IView view);
|
||||
int GetColumnSpan(IView view);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Microsoft.Maui
|
||||
{
|
||||
public interface IGridRowDefinition
|
||||
{
|
||||
GridLength Height { get; }
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -0,0 +1,464 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Maui.Layouts
|
||||
{
|
||||
public class GridLayoutManager : LayoutManager
|
||||
{
|
||||
public GridLayoutManager(IGridLayout layout) : base(layout)
|
||||
{
|
||||
Grid = layout;
|
||||
}
|
||||
|
||||
public IGridLayout Grid { get; }
|
||||
|
||||
public override Size Measure(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
var structure = new GridStructure(Grid, widthConstraint, heightConstraint);
|
||||
|
||||
return new Size(structure.GridWidth(), structure.GridHeight());
|
||||
}
|
||||
|
||||
public override void ArrangeChildren(Rectangle childBounds)
|
||||
{
|
||||
var structure = new GridStructure(Grid, childBounds.Width, childBounds.Height);
|
||||
|
||||
foreach (var view in Grid.Children)
|
||||
{
|
||||
var cell = structure.ComputeFrameFor(view);
|
||||
view.Arrange(cell);
|
||||
}
|
||||
}
|
||||
|
||||
class GridStructure
|
||||
{
|
||||
readonly IGridLayout _grid;
|
||||
readonly double _gridWidthConstraint;
|
||||
readonly double _gridHeightConstraint;
|
||||
|
||||
Row[] _rows { get; }
|
||||
Column[] _columns { get; }
|
||||
Cell[] _cells { get; }
|
||||
|
||||
readonly Dictionary<SpanKey, Span> _spans = new Dictionary<SpanKey, Span>();
|
||||
|
||||
public GridStructure(IGridLayout grid, double widthConstraint, double heightConstraint)
|
||||
{
|
||||
_grid = grid;
|
||||
_gridWidthConstraint = widthConstraint;
|
||||
_gridHeightConstraint = heightConstraint;
|
||||
_rows = new Row[_grid.RowDefinitions.Count];
|
||||
|
||||
for (int n = 0; n < _grid.RowDefinitions.Count; n++)
|
||||
{
|
||||
_rows[n] = new Row(_grid.RowDefinitions[n]);
|
||||
}
|
||||
|
||||
_columns = new Column[_grid.ColumnDefinitions.Count];
|
||||
|
||||
for (int n = 0; n < _grid.ColumnDefinitions.Count; n++)
|
||||
{
|
||||
_columns[n] = new Column(_grid.ColumnDefinitions[n]);
|
||||
}
|
||||
|
||||
_cells = new Cell[_grid.Children.Count];
|
||||
|
||||
InitializeCells();
|
||||
|
||||
MeasureCells();
|
||||
}
|
||||
|
||||
void InitializeCells()
|
||||
{
|
||||
for (int n = 0; n < _grid.Children.Count; n++)
|
||||
{
|
||||
var view = _grid.Children[n];
|
||||
var column = _grid.GetColumn(view);
|
||||
var columnSpan = _grid.GetColumnSpan(view);
|
||||
|
||||
var columnGridLengthType = GridLengthType.None;
|
||||
|
||||
for (int columnIndex = column; columnIndex < column + columnSpan; columnIndex++)
|
||||
{
|
||||
columnGridLengthType |= ToGridLengthType(_columns[columnIndex].ColumnDefinition.Width.GridUnitType);
|
||||
}
|
||||
|
||||
var row = _grid.GetRow(view);
|
||||
var rowSpan = _grid.GetRowSpan(view);
|
||||
|
||||
var rowGridLengthType = GridLengthType.None;
|
||||
|
||||
for (int rowIndex = row; rowIndex < row + rowSpan; rowIndex++)
|
||||
{
|
||||
rowGridLengthType |= ToGridLengthType(_rows[rowIndex].RowDefinition.Height.GridUnitType);
|
||||
}
|
||||
|
||||
_cells[n] = new Cell(n, row, column, rowSpan, columnSpan, columnGridLengthType, rowGridLengthType);
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle ComputeFrameFor(IView view)
|
||||
{
|
||||
var firstColumn = _grid.GetColumn(view);
|
||||
var lastColumn = firstColumn + _grid.GetColumnSpan(view);
|
||||
|
||||
var firstRow = _grid.GetRow(view);
|
||||
var lastRow = firstRow + _grid.GetRowSpan(view);
|
||||
|
||||
double top = TopEdgeOfRow(firstRow);
|
||||
double left = LeftEdgeOfColumn(firstColumn);
|
||||
|
||||
double width = 0;
|
||||
|
||||
for (int n = firstColumn; n < lastColumn; n++)
|
||||
{
|
||||
width += _columns[n].Size;
|
||||
}
|
||||
|
||||
double height = 0;
|
||||
|
||||
for (int n = firstRow; n < lastRow; n++)
|
||||
{
|
||||
height += _rows[n].Size;
|
||||
}
|
||||
|
||||
// TODO ezhart this isn't correctly accounting for row spacing when spanning multiple rows
|
||||
// (and column spacing is probably wrong, too)
|
||||
|
||||
return new Rectangle(left, top, width, height);
|
||||
}
|
||||
|
||||
public double GridHeight()
|
||||
{
|
||||
return SumDefinitions(_rows, _grid.RowSpacing);
|
||||
}
|
||||
|
||||
public double GridWidth()
|
||||
{
|
||||
return SumDefinitions(_columns, _grid.ColumnSpacing);
|
||||
}
|
||||
|
||||
double SumDefinitions(Definition[] definitions, double spacing)
|
||||
{
|
||||
double sum = 0;
|
||||
|
||||
for (int n = 0; n < definitions.Length; n++)
|
||||
{
|
||||
var current = definitions[n].Size;
|
||||
|
||||
if (current <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sum += current;
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
sum += spacing;
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
void MeasureCells()
|
||||
{
|
||||
for (int n = 0; n < _cells.Length; n++)
|
||||
{
|
||||
var cell = _cells[n];
|
||||
|
||||
if (cell.ColumnGridLengthType == GridLengthType.Absolute
|
||||
&& cell.RowGridLengthType == GridLengthType.Absolute)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var availableWidth = _gridWidthConstraint - GridWidth();
|
||||
var availableHeight = _gridHeightConstraint - GridHeight();
|
||||
|
||||
var measure = _grid.Children[cell.ViewIndex].Measure(availableWidth, availableHeight);
|
||||
|
||||
if (cell.IsColumnSpanAuto)
|
||||
{
|
||||
if (cell.ColumnSpan == 1)
|
||||
{
|
||||
_columns[cell.Column].Update(measure.Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
var span = new Span(cell.Column, cell.ColumnSpan, true, measure.Width);
|
||||
TrackSpan(span);
|
||||
}
|
||||
}
|
||||
|
||||
if (cell.IsRowSpanAuto)
|
||||
{
|
||||
if (cell.RowSpan == 1)
|
||||
{
|
||||
_rows[cell.Row].Update(measure.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
var span = new Span(cell.Row, cell.RowSpan, false, measure.Height);
|
||||
TrackSpan(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResolveSpans();
|
||||
}
|
||||
|
||||
void TrackSpan(Span span)
|
||||
{
|
||||
if (_spans.TryGetValue(span.Key, out Span? otherSpan))
|
||||
{
|
||||
// This span may replace an equivalent but smaller span
|
||||
if (span.Requested > otherSpan.Requested)
|
||||
{
|
||||
_spans[span.Key] = span;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_spans[span.Key] = span;
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveSpans()
|
||||
{
|
||||
foreach (var span in _spans.Values)
|
||||
{
|
||||
if (span.IsColumn)
|
||||
{
|
||||
ResolveSpan(_columns, span.Start, span.Length, _grid.ColumnSpacing, span.Requested);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResolveSpan(_rows, span.Start, span.Length, _grid.RowSpacing, span.Requested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveSpan(Definition[] definitions, int start, int length, double spacing, double requestedSize)
|
||||
{
|
||||
double currentSize = 0;
|
||||
var end = start + length;
|
||||
|
||||
// Determine how large the spanned area currently is
|
||||
for (int n = start; n < end; n++)
|
||||
{
|
||||
currentSize += definitions[n].Size;
|
||||
|
||||
if (n > start)
|
||||
{
|
||||
currentSize += spacing;
|
||||
}
|
||||
}
|
||||
|
||||
if (requestedSize <= currentSize)
|
||||
{
|
||||
// If our request fits in the current size, we're good
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out how much more space we need in this span
|
||||
double required = requestedSize - currentSize;
|
||||
|
||||
// And how many parts of the span to distribute that space over
|
||||
int autoCount = 0;
|
||||
for (int n = start; n < end; n++)
|
||||
{
|
||||
if (definitions[n].IsAuto)
|
||||
{
|
||||
autoCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
double distribution = required / autoCount;
|
||||
|
||||
// And distribute that over the rows/columns in the span
|
||||
for (int n = start; n < end; n++)
|
||||
{
|
||||
if (definitions[n].IsAuto)
|
||||
{
|
||||
definitions[n].Size += distribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double LeftEdgeOfColumn(int column)
|
||||
{
|
||||
double left = 0;
|
||||
|
||||
for (int n = 0; n < column; n++)
|
||||
{
|
||||
left += _columns[n].Size;
|
||||
left += _grid.ColumnSpacing;
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
double TopEdgeOfRow(int row)
|
||||
{
|
||||
double top = 0;
|
||||
|
||||
for (int n = 0; n < row; n++)
|
||||
{
|
||||
top += _rows[n].Size;
|
||||
top += _grid.RowSpacing;
|
||||
}
|
||||
|
||||
return top;
|
||||
}
|
||||
}
|
||||
|
||||
class Span
|
||||
{
|
||||
public int Start { get; }
|
||||
public int Length { get; }
|
||||
public bool IsColumn { get; }
|
||||
public double Requested { get; }
|
||||
|
||||
public SpanKey Key { get; }
|
||||
|
||||
public Span(int start, int length, bool isColumn, double value)
|
||||
{
|
||||
Start = start;
|
||||
Length = length;
|
||||
IsColumn = isColumn;
|
||||
Requested = value;
|
||||
|
||||
Key = new SpanKey(Start, Length, IsColumn);
|
||||
}
|
||||
}
|
||||
|
||||
class SpanKey
|
||||
{
|
||||
public SpanKey(int start, int length, bool isColumn)
|
||||
{
|
||||
Start = start;
|
||||
Length = length;
|
||||
IsColumn = isColumn;
|
||||
}
|
||||
|
||||
public int Start { get; }
|
||||
public int Length { get; }
|
||||
public bool IsColumn { get; }
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is SpanKey key &&
|
||||
Start == key.Start &&
|
||||
Length == key.Length &&
|
||||
IsColumn == key.IsColumn;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Start.GetHashCode() ^ Length.GetHashCode() ^ IsColumn.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
class Cell
|
||||
{
|
||||
public int ViewIndex { get; }
|
||||
public int Row { get; }
|
||||
public int Column { get; }
|
||||
public int RowSpan { get; }
|
||||
public int ColumnSpan { get; }
|
||||
|
||||
public GridLengthType ColumnGridLengthType { get; }
|
||||
public GridLengthType RowGridLengthType { get; }
|
||||
|
||||
public Cell(int viewIndex, int row, int column, int rowSpan, int columnSpan,
|
||||
GridLengthType columnGridLengthType, GridLengthType rowGridLengthType)
|
||||
{
|
||||
ViewIndex = viewIndex;
|
||||
Row = row;
|
||||
Column = column;
|
||||
RowSpan = rowSpan;
|
||||
ColumnSpan = columnSpan;
|
||||
ColumnGridLengthType = columnGridLengthType;
|
||||
RowGridLengthType = rowGridLengthType;
|
||||
}
|
||||
|
||||
public bool IsColumnSpanAuto => HasFlag(ColumnGridLengthType, GridLengthType.Auto);
|
||||
public bool IsRowSpanAuto => HasFlag(RowGridLengthType, GridLengthType.Auto);
|
||||
|
||||
bool HasFlag(GridLengthType a, GridLengthType b)
|
||||
{
|
||||
// Avoiding Enum.HasFlag here for performance reasons; we don't need the type check
|
||||
return (a & b) == b;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum GridLengthType
|
||||
{
|
||||
None = 0,
|
||||
Absolute = 1,
|
||||
Auto = 2,
|
||||
Star = 4
|
||||
}
|
||||
|
||||
static GridLengthType ToGridLengthType(GridUnitType gridUnitType)
|
||||
{
|
||||
return gridUnitType switch
|
||||
{
|
||||
GridUnitType.Absolute => GridLengthType.Absolute,
|
||||
GridUnitType.Star => GridLengthType.Star,
|
||||
GridUnitType.Auto => GridLengthType.Auto,
|
||||
_ => GridLengthType.None,
|
||||
};
|
||||
}
|
||||
|
||||
abstract class Definition
|
||||
{
|
||||
public double Size { get; set; }
|
||||
|
||||
public void Update(double size)
|
||||
{
|
||||
if (size > Size)
|
||||
{
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract bool IsAuto { get; }
|
||||
}
|
||||
|
||||
class Column : Definition
|
||||
{
|
||||
public IGridColumnDefinition ColumnDefinition { get; set; }
|
||||
|
||||
public override bool IsAuto => ColumnDefinition.Width.IsAuto;
|
||||
|
||||
public Column(IGridColumnDefinition columnDefinition)
|
||||
{
|
||||
ColumnDefinition = columnDefinition;
|
||||
if (columnDefinition.Width.IsAbsolute)
|
||||
{
|
||||
Size = columnDefinition.Width.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Row : Definition
|
||||
{
|
||||
public IGridRowDefinition RowDefinition { get; set; }
|
||||
|
||||
public override bool IsAuto => RowDefinition.Height.IsAuto;
|
||||
|
||||
public Row(IGridRowDefinition rowDefinition)
|
||||
{
|
||||
RowDefinition = rowDefinition;
|
||||
if (rowDefinition.Height.IsAbsolute)
|
||||
{
|
||||
Size = rowDefinition.Height.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ namespace Microsoft.Maui.Layouts
|
|||
return new Size(finalWidth, measure.Height);
|
||||
}
|
||||
|
||||
public override void Arrange(Rectangle bounds)
|
||||
public override void ArrangeChildren(Rectangle childBounds)
|
||||
{
|
||||
if (Stack.FlowDirection == FlowDirection.LeftToRight)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,6 @@ namespace Microsoft.Maui.Layouts
|
|||
public interface ILayoutManager
|
||||
{
|
||||
Size Measure(double widthConstraint, double heightConstraint);
|
||||
void Arrange(Rectangle bounds);
|
||||
void ArrangeChildren(Rectangle childBounds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Maui.Layouts
|
|||
public ILayout Layout { get; }
|
||||
|
||||
public abstract Size Measure(double widthConstraint, double heightConstraint);
|
||||
public abstract void Arrange(Rectangle bounds);
|
||||
public abstract void ArrangeChildren(Rectangle childBounds);
|
||||
|
||||
public static double ResolveConstraints(double externalConstraint, double desiredLength)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Microsoft.Maui.Layouts
|
|||
return new Size(measure.Width, finalHeight);
|
||||
}
|
||||
|
||||
public override void Arrange(Rectangle bounds) => Arrange(Stack.Spacing, Stack.Children);
|
||||
public override void ArrangeChildren(Rectangle childBounds) => Arrange(Stack.Spacing, Stack.Children);
|
||||
|
||||
static Size Measure(double widthConstraint, int spacing, IReadOnlyList<IView> views)
|
||||
{
|
||||
|
@ -52,6 +52,5 @@ namespace Microsoft.Maui.Layouts
|
|||
stackHeight += destination.Height + spacing;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Maui.Controls
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
[TypeConverter(typeof(GridLengthTypeConverter))]
|
||||
[DebuggerDisplay("{Value}.{GridUnitType}")]
|
||||
public struct GridLength
|
||||
{
|
||||
|
@ -51,7 +50,7 @@ namespace Microsoft.Maui.Controls
|
|||
GridUnitType = type;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj != null && obj is GridLength && Equals((GridLength)obj);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace Microsoft.Maui.Controls
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public enum GridUnitType
|
||||
{
|
|
@ -0,0 +1,688 @@
|
|||
using System.Collections.Generic;
|
||||
using NSubstitute;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Layouts;
|
||||
using static Microsoft.Maui.UnitTests.Layouts.LayoutTestHelpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Maui.UnitTests.Layouts
|
||||
{
|
||||
[Category(TestCategory.Layout)]
|
||||
public class GridLayoutManagerTests
|
||||
{
|
||||
const string GridSpacing = "GridSpacing";
|
||||
const string GridAutoSizing = "GridAutoSizing";
|
||||
const string GridStarSizing = "GridStarSizing";
|
||||
const string GridAbsoluteSizing = "GridAbsoluteSizing";
|
||||
const string GridSpan = "GridSpan";
|
||||
|
||||
IGridLayout CreateGridLayout(int rowSpacing = 0, int colSpacing = 0,
|
||||
string rows = null, string columns = null)
|
||||
{
|
||||
IEnumerable<IGridRowDefinition> rowDefs = null;
|
||||
IEnumerable<IGridColumnDefinition> colDefs = null;
|
||||
|
||||
if (rows != null)
|
||||
{
|
||||
rowDefs = CreateTestRows(rows.Split(","));
|
||||
}
|
||||
|
||||
if (columns != null)
|
||||
{
|
||||
colDefs = CreateTestColumns(columns.Split(","));
|
||||
}
|
||||
|
||||
var grid = Substitute.For<IGridLayout>();
|
||||
|
||||
grid.RowSpacing.Returns(rowSpacing);
|
||||
grid.ColumnSpacing.Returns(colSpacing);
|
||||
|
||||
SubRowDefs(grid, rowDefs);
|
||||
SubColDefs(grid, colDefs);
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
void SubRowDefs(IGridLayout grid, IEnumerable<IGridRowDefinition> rows = null)
|
||||
{
|
||||
if (rows == null)
|
||||
{
|
||||
var rowDef = Substitute.For<IGridRowDefinition>();
|
||||
rowDef.Height.Returns(GridLength.Auto);
|
||||
var rowDefs = new List<IGridRowDefinition>
|
||||
{
|
||||
rowDef
|
||||
};
|
||||
grid.RowDefinitions.Returns(rowDefs);
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.RowDefinitions.Returns(rows);
|
||||
}
|
||||
}
|
||||
|
||||
void SubColDefs(IGridLayout grid, IEnumerable<IGridColumnDefinition> cols = null)
|
||||
{
|
||||
if (cols == null)
|
||||
{
|
||||
var colDefs = CreateTestColumns("auto");
|
||||
grid.ColumnDefinitions.Returns(colDefs);
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.ColumnDefinitions.Returns(cols);
|
||||
}
|
||||
}
|
||||
|
||||
List<IGridColumnDefinition> CreateTestColumns(params string[] columnWidths)
|
||||
{
|
||||
var converter = new GridLengthTypeConverter();
|
||||
|
||||
var colDefs = new List<IGridColumnDefinition>();
|
||||
|
||||
foreach (var width in columnWidths)
|
||||
{
|
||||
var gridLength = converter.ConvertFromInvariantString(width);
|
||||
var colDef = Substitute.For<IGridColumnDefinition>();
|
||||
colDef.Width.Returns(gridLength);
|
||||
colDefs.Add(colDef);
|
||||
}
|
||||
|
||||
return colDefs;
|
||||
}
|
||||
|
||||
List<IGridRowDefinition> CreateTestRows(params string[] rowHeights)
|
||||
{
|
||||
var converter = new GridLengthTypeConverter();
|
||||
|
||||
var rowDefs = new List<IGridRowDefinition>();
|
||||
|
||||
foreach (var height in rowHeights)
|
||||
{
|
||||
var gridLength = converter.ConvertFromInvariantString(height);
|
||||
var rowDef = Substitute.For<IGridRowDefinition>();
|
||||
rowDef.Height.Returns(gridLength);
|
||||
rowDefs.Add(rowDef);
|
||||
}
|
||||
|
||||
return rowDefs;
|
||||
}
|
||||
|
||||
void SetLocation(IGridLayout grid, IView view, int row = 0, int col = 0, int rowSpan = 1, int colSpan = 1)
|
||||
{
|
||||
grid.GetRow(view).Returns(row);
|
||||
grid.GetRowSpan(view).Returns(rowSpan);
|
||||
grid.GetColumn(view).Returns(col);
|
||||
grid.GetColumnSpan(view).Returns(colSpan);
|
||||
}
|
||||
|
||||
Size MeasureAndArrange(IGridLayout grid, double widthConstraint = double.PositiveInfinity, double heightConstraint = double.PositiveInfinity)
|
||||
{
|
||||
var manager = new GridLayoutManager(grid);
|
||||
var measuredSize = manager.Measure(widthConstraint, heightConstraint);
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measuredSize));
|
||||
|
||||
return measuredSize;
|
||||
}
|
||||
|
||||
void AssertArranged(IView view, double x, double y, double width, double height)
|
||||
{
|
||||
var expected = new Rectangle(x, y, width, height);
|
||||
view.Received().Arrange(Arg.Is(expected));
|
||||
}
|
||||
|
||||
[Category(GridAutoSizing)]
|
||||
[Fact]
|
||||
public void OneAutoRowOneAutoColumn()
|
||||
{
|
||||
// A one-row, one-column grid
|
||||
var grid = CreateGridLayout();
|
||||
|
||||
// A 100x100 IView
|
||||
var view = CreateTestView(new Size(100, 100));
|
||||
|
||||
// Set up the grid to have a single child
|
||||
AddChildren(grid, view);
|
||||
|
||||
// Set up the row/column values and spans
|
||||
SetLocation(grid, view);
|
||||
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
// We expect that the only child of the grid will be given its full size
|
||||
AssertArranged(view, 0, 0, 100, 100);
|
||||
}
|
||||
|
||||
[Category(GridAbsoluteSizing)]
|
||||
[Fact]
|
||||
public void TwoAbsoluteColumnsOneAbsoluteRow()
|
||||
{
|
||||
var grid = CreateGridLayout(columns: "100, 100", rows: "10");
|
||||
|
||||
var viewSize = new Size(10, 10);
|
||||
|
||||
var view0 = CreateTestView(viewSize);
|
||||
var view1 = CreateTestView(viewSize);
|
||||
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, col: 1);
|
||||
|
||||
// Assuming no constraints on space
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.NegativeInfinity);
|
||||
|
||||
// Column width is 100, viewSize is less than that, so it should be able to layout out at full size
|
||||
AssertArranged(view0, 0, 0, 100, 10);
|
||||
|
||||
// Since the first column is 100 wide, we expect the view in the second column to start at x = 100
|
||||
AssertArranged(view1, 100, 0, 100, 10);
|
||||
}
|
||||
|
||||
[Category(GridAbsoluteSizing)]
|
||||
[Fact]
|
||||
public void TwoAbsoluteRowsAndColumns()
|
||||
{
|
||||
var grid = CreateGridLayout(columns: "100, 100", rows: "10, 30");
|
||||
|
||||
var viewSize = new Size(10, 10);
|
||||
|
||||
var view0 = CreateTestView(viewSize);
|
||||
var view1 = CreateTestView(viewSize);
|
||||
var view2 = CreateTestView(viewSize);
|
||||
var view3 = CreateTestView(viewSize);
|
||||
|
||||
AddChildren(grid, view0, view1, view2, view3);
|
||||
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, col: 1);
|
||||
SetLocation(grid, view2, row: 1);
|
||||
SetLocation(grid, view3, row: 1, col: 1);
|
||||
|
||||
// Assuming no constraints on space
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.NegativeInfinity);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 10);
|
||||
|
||||
// Since the first column is 100 wide, we expect the view in the second column to start at x = 100
|
||||
AssertArranged(view1, 100, 0, 100, 10);
|
||||
|
||||
// First column, second row, so y should be 10
|
||||
AssertArranged(view2, 0, 10, 100, 30);
|
||||
|
||||
// Second column, second row, so 100, 10
|
||||
AssertArranged(view3, 100, 10, 100, 30);
|
||||
}
|
||||
|
||||
[Category(GridAbsoluteSizing), Category(GridAutoSizing)]
|
||||
[Fact]
|
||||
public void TwoAbsoluteColumnsOneAutoRow()
|
||||
{
|
||||
var grid = CreateGridLayout(columns: "100, 100");
|
||||
|
||||
var viewSize = new Size(10, 10);
|
||||
|
||||
var view0 = CreateTestView(viewSize);
|
||||
var view1 = CreateTestView(viewSize);
|
||||
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, col: 1);
|
||||
|
||||
// Assuming no constraints on space
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.NegativeInfinity);
|
||||
|
||||
// Column width is 100, viewSize is less, so it should be able to layout at full size
|
||||
AssertArranged(view0, 0, 0, 100, viewSize.Height);
|
||||
|
||||
// Since the first column is 100 wide, we expect the view in the second column to start at x = 100
|
||||
AssertArranged(view1, 100, 0, 100, viewSize.Height);
|
||||
}
|
||||
|
||||
[Category(GridAbsoluteSizing), Category(GridAutoSizing)]
|
||||
[Fact]
|
||||
public void TwoAbsoluteRowsOneAutoColumn()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "100, 100");
|
||||
|
||||
var viewSize = new Size(10, 10);
|
||||
|
||||
var view0 = CreateTestView(viewSize);
|
||||
var view1 = CreateTestView(viewSize);
|
||||
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, row: 1);
|
||||
|
||||
// Assuming no constraints on space
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.NegativeInfinity);
|
||||
|
||||
// Row height is 100, so full view should fit
|
||||
AssertArranged(view0, 0, 0, viewSize.Width, 100);
|
||||
|
||||
// Since the first row is 100 tall, we expect the view in the second row to start at y = 100
|
||||
AssertArranged(view1, 0, 100, viewSize.Width, 100);
|
||||
}
|
||||
|
||||
[Category(GridSpacing)]
|
||||
[Fact(DisplayName = "Row spacing shouldn't affect a single-row grid")]
|
||||
public void SingleRowIgnoresRowSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(rowSpacing: 10);
|
||||
var view = CreateTestView(new Size(100, 100));
|
||||
AddChildren(grid, view);
|
||||
SetLocation(grid, view);
|
||||
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.PositiveInfinity);
|
||||
AssertArranged(view, 0, 0, 100, 100);
|
||||
}
|
||||
|
||||
[Category(GridSpacing)]
|
||||
[Fact(DisplayName = "Two rows should include the row spacing once")]
|
||||
public void TwoRowsWithSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "100, 100", rowSpacing: 10);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(100, 100));
|
||||
AddChildren(grid, view0, view1);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, row: 1);
|
||||
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.PositiveInfinity);
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
|
||||
// With column width 100 and spacing of 10, we expect the second column to start at 110
|
||||
AssertArranged(view1, 0, 110, 100, 100);
|
||||
}
|
||||
|
||||
[Category(GridSpacing)]
|
||||
[Fact(DisplayName = "Measure should include row spacing")]
|
||||
public void MeasureTwoRowsWithSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "100, 100", rowSpacing: 10);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(100, 100));
|
||||
AddChildren(grid, view0, view1);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, row: 1);
|
||||
|
||||
var manager = new GridLayoutManager(grid);
|
||||
var measure = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
Assert.Equal(100 + 100 + 10, measure.Height);
|
||||
}
|
||||
|
||||
[Category(GridAutoSizing)]
|
||||
[Fact(DisplayName = "Auto rows without content have height zero")]
|
||||
public void EmptyAutoRowsHaveNoHeight()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "100, auto, 100");
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view2 = CreateTestView(new Size(100, 100));
|
||||
|
||||
AddChildren(grid, view0, view2);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view2, row: 2);
|
||||
|
||||
var manager = new GridLayoutManager(grid);
|
||||
var measure = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
manager.ArrangeChildren(new Rectangle(0, 0, measure.Width, measure.Height));
|
||||
|
||||
// Because the auto row has no content, we expect it to have height zero
|
||||
Assert.Equal(100 + 100, measure.Height);
|
||||
|
||||
// Verify the offset for the third row
|
||||
AssertArranged(view2, 0, 100, 100, 100);
|
||||
}
|
||||
|
||||
[Category(GridSpacing)]
|
||||
[Fact(DisplayName = "Empty rows should not incur additional row spacing")]
|
||||
public void RowSpacingForEmptyRows()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "100, auto, 100", rowSpacing: 10);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view2 = CreateTestView(new Size(100, 100));
|
||||
|
||||
AddChildren(grid, view0, view2);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view2, row: 2);
|
||||
|
||||
var manager = new GridLayoutManager(grid);
|
||||
var measure = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
// Because the auto row has no content, we expect it to have height zero
|
||||
// and we expect that it won't add more row spacing
|
||||
Assert.Equal(100 + 100 + 10, measure.Height);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Column spacing shouldn't affect a single-column grid")]
|
||||
public void SingleColumnIgnoresColumnSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(colSpacing: 10);
|
||||
var view = CreateTestView(new Size(100, 100));
|
||||
AddChildren(grid, view);
|
||||
SetLocation(grid, view);
|
||||
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.PositiveInfinity);
|
||||
AssertArranged(view, 0, 0, 100, 100);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Two columns should include the column spacing once")]
|
||||
public void TwoColumnsWithSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(columns: "100, 100", colSpacing: 10);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(100, 100));
|
||||
AddChildren(grid, view0, view1);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, col: 1);
|
||||
|
||||
MeasureAndArrange(grid, double.PositiveInfinity, double.PositiveInfinity);
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
|
||||
// With column width 100 and spacing of 10, we expect the second column to start at 110
|
||||
AssertArranged(view1, 110, 0, 100, 100);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Measure should include column spacing")]
|
||||
public void MeasureTwoColumnsWithSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(columns: "100, 100", colSpacing: 10);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(100, 100));
|
||||
AddChildren(grid, view0, view1);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view1, col: 1);
|
||||
|
||||
var manager = new GridLayoutManager(grid);
|
||||
var measure = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
Assert.Equal(100 + 100 + 10, measure.Width);
|
||||
}
|
||||
|
||||
[Category(GridAutoSizing)]
|
||||
[Fact(DisplayName = "Auto columns without content have width zero")]
|
||||
public void EmptyAutoColumnsHaveNoWidth()
|
||||
{
|
||||
var grid = CreateGridLayout(columns: "100, auto, 100");
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view2 = CreateTestView(new Size(100, 100));
|
||||
|
||||
AddChildren(grid, view0, view2);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view2, col: 2);
|
||||
|
||||
var manager = new GridLayoutManager(grid);
|
||||
var measure = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
manager.ArrangeChildren(new Rectangle(0, 0, measure.Width, measure.Height));
|
||||
|
||||
// Because the auto column has no content, we expect it to have width zero
|
||||
Assert.Equal(100 + 100, measure.Width);
|
||||
|
||||
// Verify the offset for the third column
|
||||
AssertArranged(view2, 100, 0, 100, 100);
|
||||
}
|
||||
|
||||
[Category(GridSpacing)]
|
||||
[Fact(DisplayName = "Empty columns should not incur additional column spacing")]
|
||||
public void ColumnSpacingForEmptyColumns()
|
||||
{
|
||||
var grid = CreateGridLayout(columns: "100, auto, 100", colSpacing: 10);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view2 = CreateTestView(new Size(100, 100));
|
||||
|
||||
AddChildren(grid, view0, view2);
|
||||
SetLocation(grid, view0);
|
||||
SetLocation(grid, view2, col: 2);
|
||||
|
||||
var manager = new GridLayoutManager(grid);
|
||||
var measure = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
// Because the auto column has no content, we expect it to have height zero
|
||||
// and we expect that it won't add more row spacing
|
||||
Assert.Equal(100 + 100 + 10, measure.Width);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Simple row spanning")]
|
||||
public void ViewSpansRows()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto");
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
AddChildren(grid, view0);
|
||||
SetLocation(grid, view0, rowSpan: 2);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
Assert.Equal(100, measuredSize.Width);
|
||||
|
||||
// We expect the rows to each get half the view height
|
||||
Assert.Equal(100, measuredSize.Height);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Simple row spanning with multiple views")]
|
||||
public void ViewSpansRowsWhenOtherViewsPresent()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, auto");
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(50, 50));
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0, rowSpan: 2);
|
||||
SetLocation(grid, view1, row: 1, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(100 + 50, measuredSize.Width);
|
||||
Assert.Equal(100, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
AssertArranged(view1, 100, 25, 50, 75);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Category(GridSpacing)]
|
||||
[Fact(DisplayName = "Row spanning with row spacing")]
|
||||
public void RowSpanningShouldAccountForSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, auto", rowSpacing: 5);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(50, 50));
|
||||
var view2 = CreateTestView(new Size(50, 50));
|
||||
AddChildren(grid, view0, view1, view2);
|
||||
|
||||
SetLocation(grid, view0, rowSpan: 2);
|
||||
SetLocation(grid, view1, row: 0, col: 1);
|
||||
SetLocation(grid, view2, row: 1, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(150, measuredSize.Width);
|
||||
Assert.Equal(50 + 50 + 5, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
AssertArranged(view1, 100, 0, 50, 50);
|
||||
AssertArranged(view2, 100, 55, 50, 50);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Simple column spanning with multiple views")]
|
||||
public void ViewSpansColumnsWhenOtherViewsPresent()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, auto");
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(50, 50));
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0, colSpan: 2);
|
||||
SetLocation(grid, view1, row: 1, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(100, measuredSize.Width);
|
||||
Assert.Equal(100 + 50, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
AssertArranged(view1, 25, 100, 75, 50);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Category(GridSpacing)]
|
||||
[Fact(DisplayName = "Column spanning with column spacing")]
|
||||
public void ColumnSpanningShouldAccountForSpacing()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, auto", colSpacing: 5);
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(50, 50));
|
||||
var view2 = CreateTestView(new Size(50, 50));
|
||||
AddChildren(grid, view0, view1, view2);
|
||||
|
||||
SetLocation(grid, view0, colSpan: 2);
|
||||
SetLocation(grid, view1, row: 1, col: 0);
|
||||
SetLocation(grid, view2, row: 1, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(50 + 50 + 5, measuredSize.Width);
|
||||
Assert.Equal(100 + 50, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
AssertArranged(view1, 0, 100, 50, 50);
|
||||
AssertArranged(view2, 55, 100, 50, 50);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Row-spanning views smaller than the views confined to the row should not affect row size")]
|
||||
public void SmallerSpanningViewsShouldNotAffectRowSize()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, auto");
|
||||
var view0 = CreateTestView(new Size(30, 30));
|
||||
var view1 = CreateTestView(new Size(50, 50));
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0, rowSpan: 2);
|
||||
SetLocation(grid, view1, row: 0, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(30 + 50, measuredSize.Width);
|
||||
Assert.Equal(50, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 30, 50);
|
||||
AssertArranged(view1, 30, 0, 50, 50);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Column-spanning views smaller than the views confined to the column should not affect column size")]
|
||||
public void SmallerSpanningViewsShouldNotAffectColumnSize()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, auto");
|
||||
var view0 = CreateTestView(new Size(30, 30));
|
||||
var view1 = CreateTestView(new Size(50, 50));
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0, colSpan: 2);
|
||||
SetLocation(grid, view1, row: 1, col: 0);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(50, measuredSize.Width);
|
||||
Assert.Equal(30 + 50, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 50, 30);
|
||||
AssertArranged(view1, 0, 30, 50, 50);
|
||||
}
|
||||
|
||||
|
||||
[Category(GridAbsoluteSizing)]
|
||||
[Fact(DisplayName = "Empty absolute rows/columns still affect Grid size")]
|
||||
public void EmptyAbsoluteRowsAndColumnsAffectSize()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "10, 40", columns: "15, 85");
|
||||
var view0 = CreateTestView(new Size(30, 30));
|
||||
AddChildren(grid, view0);
|
||||
|
||||
SetLocation(grid, view0, row: 1, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(15 + 85, measuredSize.Width);
|
||||
Assert.Equal(10 + 40, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 15, 10, 85, 40);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Row and column spans should be able to mix")]
|
||||
public void MixedRowAndColumnSpans()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, auto");
|
||||
var view0 = CreateTestView(new Size(60, 30));
|
||||
var view1 = CreateTestView(new Size(30, 60));
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0, row: 0, col: 0, colSpan: 2);
|
||||
SetLocation(grid, view1, row: 0, col: 1, rowSpan: 2);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(60, measuredSize.Width);
|
||||
Assert.Equal(60, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 60, 45);
|
||||
AssertArranged(view1, 15, 0, 45, 60);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Row span including absolute row should not modify absolute size")]
|
||||
public void RowSpanShouldNotModifyAbsoluteRowSize()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, 20", columns: "auto, auto");
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(50, 10));
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0, rowSpan: 2);
|
||||
SetLocation(grid, view1, row: 1, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(100 + 50, measuredSize.Width);
|
||||
Assert.Equal(100, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
|
||||
// The item in the second row starts at y = 80 because the auto row above had to distribute
|
||||
// all the extra space into row 0; row 1 is absolute, so no tinkering with it to make stuff fit
|
||||
AssertArranged(view1, 100, 80, 50, 20);
|
||||
}
|
||||
|
||||
[Category(GridSpan)]
|
||||
[Fact(DisplayName = "Column span including absolute column should not modify absolute size")]
|
||||
public void ColumnSpanShouldNotModifyAbsoluteColumnSize()
|
||||
{
|
||||
var grid = CreateGridLayout(rows: "auto, auto", columns: "auto, 20");
|
||||
var view0 = CreateTestView(new Size(100, 100));
|
||||
var view1 = CreateTestView(new Size(50, 10));
|
||||
AddChildren(grid, view0, view1);
|
||||
|
||||
SetLocation(grid, view0, colSpan: 2);
|
||||
SetLocation(grid, view1, row: 1, col: 1);
|
||||
|
||||
var measuredSize = MeasureAndArrange(grid);
|
||||
|
||||
Assert.Equal(100, measuredSize.Width);
|
||||
Assert.Equal(100 + 10, measuredSize.Height);
|
||||
|
||||
AssertArranged(view0, 0, 0, 100, 100);
|
||||
|
||||
// The item in the second row starts at x = 80 because the auto column before it had to distribute
|
||||
// all the extra space into column 0; column 1 is absolute, so no tinkering with it to make stuff fit
|
||||
AssertArranged(view1, 80, 100, 20, 10);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
var manager = new HorizontalStackLayoutManager(stack);
|
||||
|
||||
var measuredSize = manager.Measure(double.PositiveInfinity, 100);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measuredSize));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measuredSize));
|
||||
|
||||
var expectedRectangle = new Rectangle(0, 0, 100, 100);
|
||||
stack.Children[0].Received().Arrange(Arg.Is(expectedRectangle));
|
||||
|
@ -53,7 +53,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
var manager = new HorizontalStackLayoutManager(stack);
|
||||
|
||||
var measuredSize = manager.Measure(double.PositiveInfinity, 100);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measuredSize));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measuredSize));
|
||||
|
||||
var expectedRectangle0 = new Rectangle(0, 0, 100, 100);
|
||||
stack.Children[0].Received().Arrange(Arg.Is(expectedRectangle0));
|
||||
|
@ -70,7 +70,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
{
|
||||
var stack = CreateTestLayout();
|
||||
|
||||
var view = CreateTestView(new Size(viewWidth, 100));
|
||||
var view = LayoutTestHelpers.CreateTestView(new Size(viewWidth, 100));
|
||||
|
||||
var children = new List<IView>() { view }.AsReadOnly();
|
||||
|
||||
|
@ -88,14 +88,14 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
var stack = CreateTestLayout();
|
||||
var manager = new HorizontalStackLayoutManager(stack);
|
||||
|
||||
var view1 = CreateTestView(new Size(100, 200));
|
||||
var view2 = CreateTestView(new Size(100, 150));
|
||||
var view1 = LayoutTestHelpers.CreateTestView(new Size(100, 200));
|
||||
var view2 = LayoutTestHelpers.CreateTestView(new Size(100, 150));
|
||||
|
||||
var children = new List<IView>() { view1, view2 }.AsReadOnly();
|
||||
stack.Children.Returns(children);
|
||||
|
||||
var measurement = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measurement));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measurement));
|
||||
|
||||
// The tallest IView is 200, so the stack should be that tall
|
||||
Assert.Equal(200, measurement.Height);
|
||||
|
@ -117,7 +117,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
|
||||
var manager = new HorizontalStackLayoutManager(stack);
|
||||
var measuredSize = manager.Measure(double.PositiveInfinity, 100);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measuredSize));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measuredSize));
|
||||
|
||||
// We expect that the starting view (0) should be arranged on the left,
|
||||
// and the next rectangle (1) should be on the right
|
||||
|
@ -136,7 +136,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
|
||||
var manager = new HorizontalStackLayoutManager(stack);
|
||||
var measuredSize = manager.Measure(double.PositiveInfinity, 100);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measuredSize));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measuredSize));
|
||||
|
||||
// We expect that the starting view (0) should be arranged on the right,
|
||||
// and the next rectangle (1) should be on the left
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
using System.Collections.Generic;
|
||||
using NSubstitute;
|
||||
|
||||
namespace Microsoft.Maui.UnitTests.Layouts
|
||||
{
|
||||
public static class LayoutTestHelpers
|
||||
{
|
||||
public static IView CreateTestView()
|
||||
{
|
||||
var view = Substitute.For<IView>();
|
||||
|
||||
view.Height.Returns(-1);
|
||||
view.Width.Returns(-1);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public static IView CreateTestView(Size viewSize)
|
||||
{
|
||||
var view = CreateTestView();
|
||||
|
||||
view.Measure(Arg.Any<double>(), Arg.Any<double>()).Returns(viewSize);
|
||||
view.DesiredSize.Returns(viewSize);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public static void AddChildren(ILayout layout, params IView[] views)
|
||||
{
|
||||
var children = new List<IView>(views);
|
||||
layout.Children.Returns(children.AsReadOnly());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Maui;
|
||||
using NSubstitute;
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
|
||||
for (int n = 0; n < viewCount; n++)
|
||||
{
|
||||
var view = CreateTestView(new Size(viewWidth, viewHeight));
|
||||
var view = LayoutTestHelpers.CreateTestView(new Size(viewWidth, viewHeight));
|
||||
children.Add(view);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
var manager = new VerticalStackLayoutManager(stack);
|
||||
|
||||
var measuredSize = manager.Measure(100, double.PositiveInfinity);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measuredSize));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measuredSize));
|
||||
|
||||
var expectedRectangle = new Rectangle(0, 0, 100, 100);
|
||||
stack.Children[0].Received().Arrange(Arg.Is(expectedRectangle));
|
||||
|
@ -53,7 +53,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
var manager = new VerticalStackLayoutManager(stack);
|
||||
|
||||
var measuredSize = manager.Measure(double.PositiveInfinity, 100);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measuredSize));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measuredSize));
|
||||
|
||||
var expectedRectangle0 = new Rectangle(0, 0, 100, 100);
|
||||
stack.Children[0].Received().Arrange(Arg.Is(expectedRectangle0));
|
||||
|
@ -70,7 +70,7 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
{
|
||||
var stack = CreateTestLayout();
|
||||
|
||||
var view = CreateTestView(new Size(100, viewHeight));
|
||||
var view = LayoutTestHelpers.CreateTestView(new Size(100, viewHeight));
|
||||
|
||||
var children = new List<IView>() { view }.AsReadOnly();
|
||||
|
||||
|
@ -88,14 +88,14 @@ namespace Microsoft.Maui.UnitTests.Layouts
|
|||
var stack = CreateTestLayout();
|
||||
var manager = new VerticalStackLayoutManager(stack);
|
||||
|
||||
var view1 = CreateTestView(new Size(200, 100));
|
||||
var view2 = CreateTestView(new Size(150, 100));
|
||||
var view1 = LayoutTestHelpers.CreateTestView(new Size(200, 100));
|
||||
var view2 = LayoutTestHelpers.CreateTestView(new Size(150, 100));
|
||||
|
||||
var children = new List<IView>() { view1, view2 }.AsReadOnly();
|
||||
stack.Children.Returns(children);
|
||||
|
||||
var measurement = manager.Measure(double.PositiveInfinity, double.PositiveInfinity);
|
||||
manager.Arrange(new Rectangle(Point.Zero, measurement));
|
||||
manager.ArrangeChildren(new Rectangle(Point.Zero, measurement));
|
||||
|
||||
// The widest IView is 200, so the stack should be that wide
|
||||
Assert.Equal(200, measurement.Width);
|
||||
|
|
Загрузка…
Ссылка в новой задаче