Browse Source

Try to use new algorithm to measure and arrange Grid.

pull/1517/head
walterlv 8 years ago
parent
commit
b135f988e4
  1. 462
      src/Avalonia.Controls/Grid.cs
  2. 115
      src/Avalonia.Controls/Utils/GridLayout.cs
  3. 91
      tests/Avalonia.Controls.UnitTests/GridLayoutTests.cs
  4. 26
      tests/Avalonia.Controls.UnitTests/GridTests.cs

462
src/Avalonia.Controls/Grid.cs

@ -4,7 +4,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Avalonia.Collections;
using Avalonia.Controls.Utils;
using JetBrains.Annotations;
namespace Avalonia.Controls
{
@ -190,299 +193,97 @@ namespace Avalonia.Controls
/// <returns>The desired size of the control.</returns>
protected override Size MeasureOverride(Size constraint)
{
Size totalSize = constraint;
int colCount = ColumnDefinitions.Count;
int rowCount = RowDefinitions.Count;
double totalStarsX = 0;
double totalStarsY = 0;
bool emptyRows = rowCount == 0;
bool emptyCols = colCount == 0;
bool hasChildren = Children.Count > 0;
if (emptyRows)
{
rowCount = 1;
}
if (emptyCols)
{
colCount = 1;
}
CreateMatrices(rowCount, colCount);
if (emptyRows)
{
_rowMatrix[0, 0] = new Segment(0, 0, double.PositiveInfinity, GridUnitType.Star);
_rowMatrix[0, 0].Stars = 1.0;
totalStarsY += 1.0;
}
else
{
for (int i = 0; i < rowCount; i++)
// +------- 1. Prepare the children status -------+
// + +
// +------- ------------------------------ -------+
// Normalize the column/columnspan and row/rowspan.
var columnCount = ColumnDefinitions.Count;
var rowCount = RowDefinitions.Count;
var safeColumns = Children.OfType<Control>().ToDictionary(child => child,
child => GetSafeSpan(columnCount, GetColumn(child), GetColumnSpan(child)));
var safeRows = Children.OfType<Control>().ToDictionary(child => child,
child => GetSafeSpan(rowCount, GetRow(child), GetRowSpan(child)));
// +------- 2. -------+
// + +
// +------- ------------------------------ -------+
// Find out the children that should be Measure first (those rows/columns are Auto size.)
var columnLayout = new GridLayout(ColumnDefinitions);
var rowLayout = new GridLayout(RowDefinitions);
var autoSizeColumns = columnLayout.Prepare();
var autoSizeRows = rowLayout.Prepare();
foreach (var pair in safeColumns)
{
var child = pair.Key;
var (column, columnSpan) = pair.Value;
var columnLast = column + columnSpan - 1;
if (autoSizeColumns.Contains(columnLast))
{
RowDefinition rowdef = RowDefinitions[i];
GridLength height = rowdef.Height;
rowdef.ActualHeight = double.PositiveInfinity;
_rowMatrix[i, i] = new Segment(0, rowdef.MinHeight, rowdef.MaxHeight, height.GridUnitType);
if (height.GridUnitType == GridUnitType.Pixel)
{
_rowMatrix[i, i].OfferedSize = Clamp(height.Value, _rowMatrix[i, i].Min, _rowMatrix[i, i].Max);
_rowMatrix[i, i].DesiredSize = _rowMatrix[i, i].OfferedSize;
rowdef.ActualHeight = _rowMatrix[i, i].OfferedSize;
}
else if (height.GridUnitType == GridUnitType.Star)
{
_rowMatrix[i, i].OfferedSize = Clamp(0, _rowMatrix[i, i].Min, _rowMatrix[i, i].Max);
_rowMatrix[i, i].DesiredSize = _rowMatrix[i, i].OfferedSize;
_rowMatrix[i, i].Stars = height.Value;
totalStarsY += height.Value;
}
else if (height.GridUnitType == GridUnitType.Auto)
{
_rowMatrix[i, i].OfferedSize = Clamp(0, _rowMatrix[i, i].Min, _rowMatrix[i, i].Max);
_rowMatrix[i, i].DesiredSize = _rowMatrix[i, i].OfferedSize;
}
}
}
if (emptyCols)
// Calculate row height list and column width list.
var widthList = columnLayout.Measure(constraint.Width);
var heightList = rowLayout.Measure(constraint.Height);
// Calculate the available width list and height list for every child.
var childrenAvailableWidths = Children.OfType<Control>().ToDictionary(child => child, child =>
{
_colMatrix[0, 0] = new Segment(0, 0, double.PositiveInfinity, GridUnitType.Star);
_colMatrix[0, 0].Stars = 1.0;
totalStarsX += 1.0;
}
else
var (column, columnSpan) = GetSafeSpan(widthList.Count, GetColumn(child), GetColumnSpan(child));
return Span(widthList, column, columnSpan).Sum();
});
var childrenAvailableHeights = Children.OfType<Control>().ToDictionary(child => child, child =>
{
for (int i = 0; i < colCount; i++)
{
ColumnDefinition coldef = ColumnDefinitions[i];
GridLength width = coldef.Width;
coldef.ActualWidth = double.PositiveInfinity;
_colMatrix[i, i] = new Segment(0, coldef.MinWidth, coldef.MaxWidth, width.GridUnitType);
if (width.GridUnitType == GridUnitType.Pixel)
{
_colMatrix[i, i].OfferedSize = Clamp(width.Value, _colMatrix[i, i].Min, _colMatrix[i, i].Max);
_colMatrix[i, i].DesiredSize = _colMatrix[i, i].OfferedSize;
coldef.ActualWidth = _colMatrix[i, i].OfferedSize;
}
else if (width.GridUnitType == GridUnitType.Star)
{
_colMatrix[i, i].OfferedSize = Clamp(0, _colMatrix[i, i].Min, _colMatrix[i, i].Max);
_colMatrix[i, i].DesiredSize = _colMatrix[i, i].OfferedSize;
_colMatrix[i, i].Stars = width.Value;
totalStarsX += width.Value;
}
else if (width.GridUnitType == GridUnitType.Auto)
{
_colMatrix[i, i].OfferedSize = Clamp(0, _colMatrix[i, i].Min, _colMatrix[i, i].Max);
_colMatrix[i, i].DesiredSize = _colMatrix[i, i].OfferedSize;
}
}
}
List<GridNode> sizes = new List<GridNode>();
GridNode node;
GridNode separator = new GridNode(null, 0, 0, 0);
int separatorIndex;
sizes.Add(separator);
var (row, rowSpan) = GetSafeSpan(heightList.Count, GetRow(child), GetRowSpan(child));
return Span(heightList, row, rowSpan).Sum();
});
// Pre-process the grid children so that we know what types of elements we have so
// we can apply our special measuring rules.
GridWalker gridWalker = new GridWalker(this, _rowMatrix, _colMatrix);
// Measure the children.
var availableWidth = constraint.Width;
var availableHeight = constraint.Height;
var desiredWidth = 0.0;
var desiredHeight = 0.0;
var sortedChildren = Children.OfType<Control>()
.OrderBy(GetColumn).ThenBy(GetRow)
.ToDictionary(child => child, child => (GetColumn(child), GetRow(child)));
for (int i = 0; i < 6; i++)
var currentDesiredWidth = 0.0;
var currentDesiredHeight = 0.0;
var currentColumn = 0;
var currentRow = 0;
foreach (var pair in sortedChildren)
{
// These bools tell us which grid element type we should be measuring. i.e.
// 'star/auto' means we should measure elements with a star row and auto col
bool autoAuto = i == 0;
bool starAuto = i == 1;
bool autoStar = i == 2;
bool starAutoAgain = i == 3;
bool nonStar = i == 4;
bool remainingStar = i == 5;
var child = pair.Key;
var (column, row) = pair.Value;
child.Measure(new Size(childrenAvailableWidths[child], childrenAvailableHeights[child]));
var desiredSize = child.DesiredSize;
if (hasChildren)
if (column == currentColumn)
{
ExpandStarCols(totalSize);
ExpandStarRows(totalSize);
currentDesiredWidth = Math.Max(desiredSize.Width, currentDesiredWidth);
}
foreach (Control child in Children)
else
{
int col, row;
int colspan, rowspan;
double childSizeX = 0;
double childSizeY = 0;
bool starCol = false;
bool starRow = false;
bool autoCol = false;
bool autoRow = false;
col = Math.Min(GetColumn(child), colCount - 1);
row = Math.Min(GetRow(child), rowCount - 1);
colspan = Math.Min(GetColumnSpan(child), colCount - col);
rowspan = Math.Min(GetRowSpan(child), rowCount - row);
for (int r = row; r < row + rowspan; r++)
{
starRow |= _rowMatrix[r, r].Type == GridUnitType.Star;
autoRow |= _rowMatrix[r, r].Type == GridUnitType.Auto;
}
for (int c = col; c < col + colspan; c++)
{
starCol |= _colMatrix[c, c].Type == GridUnitType.Star;
autoCol |= _colMatrix[c, c].Type == GridUnitType.Auto;
}
// This series of if statements checks whether or not we should measure
// the current element and also if we need to override the sizes
// passed to the Measure call.
// If the element has Auto rows and Auto columns and does not span Star
// rows/cols it should only be measured in the auto_auto phase.
// There are similar rules governing auto/star and star/auto elements.
// NOTE: star/auto elements are measured twice. The first time with
// an override for height, the second time without it.
if (autoRow && autoCol && !starRow && !starCol)
{
if (!autoAuto)
{
continue;
}
childSizeX = double.PositiveInfinity;
childSizeY = double.PositiveInfinity;
}
else if (starRow && autoCol && !starCol)
{
if (!(starAuto || starAutoAgain))
{
continue;
}
if (starAuto && gridWalker.HasAutoStar)
{
childSizeY = double.PositiveInfinity;
}
childSizeX = double.PositiveInfinity;
}
else if (autoRow && starCol && !starRow)
{
if (!autoStar)
{
continue;
}
childSizeY = double.PositiveInfinity;
}
else if ((autoRow || autoCol) && !(starRow || starCol))
{
if (!nonStar)
{
continue;
}
if (autoRow)
{
childSizeY = double.PositiveInfinity;
}
if (autoCol)
{
childSizeX = double.PositiveInfinity;
}
}
else if (!(starRow || starCol))
{
if (!nonStar)
{
continue;
}
}
else
{
if (!remainingStar)
{
continue;
}
}
for (int r = row; r < row + rowspan; r++)
{
childSizeY += _rowMatrix[r, r].OfferedSize;
}
for (int c = col; c < col + colspan; c++)
{
childSizeX += _colMatrix[c, c].OfferedSize;
}
child.Measure(new Size(childSizeX, childSizeY));
Size desired = child.DesiredSize;
// Elements distribute their height based on two rules:
// 1) Elements with rowspan/colspan == 1 distribute their height first
// 2) Everything else distributes in a LIFO manner.
// As such, add all UIElements with rowspan/colspan == 1 after the separator in
// the list and everything else before it. Then to process, just keep popping
// elements off the end of the list.
if (!starAuto)
{
node = new GridNode(_rowMatrix, row + rowspan - 1, row, desired.Height);
separatorIndex = sizes.IndexOf(separator);
sizes.Insert(node.Row == node.Column ? separatorIndex + 1 : separatorIndex, node);
}
node = new GridNode(_colMatrix, col + colspan - 1, col, desired.Width);
separatorIndex = sizes.IndexOf(separator);
sizes.Insert(node.Row == node.Column ? separatorIndex + 1 : separatorIndex, node);
currentDesiredWidth = desiredSize.Width;
currentColumn = column;
availableWidth -= desiredSize.Width;
desiredHeight -= desiredSize.Width;
}
sizes.Remove(separator);
while (sizes.Count > 0)
if (availableWidth < desiredSize.Width)
{
node = sizes.Last();
node.Matrix[node.Row, node.Column].DesiredSize = Math.Max(node.Matrix[node.Row, node.Column].DesiredSize, node.Size);
AllocateDesiredSize(rowCount, colCount);
sizes.Remove(node);
}
sizes.Add(separator);
availableHeight -= desiredSize.Height;
desiredHeight -= desiredSize.Width;
}
// Once we have measured and distributed all sizes, we have to store
// the results. Every time we want to expand the rows/cols, this will
// be used as the baseline.
SaveMeasureResults();
sizes.Remove(separator);
double gridSizeX = 0;
double gridSizeY = 0;
for (int c = 0; c < colCount; c++)
{
gridSizeX += _colMatrix[c, c].DesiredSize;
}
for (int r = 0; r < rowCount; r++)
{
gridSizeY += _rowMatrix[r, r].DesiredSize;
}
return new Size(gridSizeX, gridSizeY);
return constraint;
}
/// <summary>
@ -492,84 +293,77 @@ namespace Avalonia.Controls
/// <returns>The space taken.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
int colCount = ColumnDefinitions.Count;
int rowCount = RowDefinitions.Count;
int colMatrixDim = _colMatrix.GetLength(0);
int rowMatrixDim = _rowMatrix.GetLength(0);
RestoreMeasureResults();
// Calculate row height list and column width list.
var rowLayout = new GridLayout(RowDefinitions);
var columnLayout = new GridLayout(ColumnDefinitions);
var heightList = rowLayout.Measure(finalSize.Height);
var widthList = columnLayout.Measure(finalSize.Width);
double totalConsumedX = 0;
double totalConsumedY = 0;
for (int c = 0; c < colMatrixDim; c++)
var rowMeasure = new Dictionary<Control, (double row, double rowspan)>();
var columnMeasure = new Dictionary<Control, (double column, double columnspan)>();
foreach (var child in Children.OfType<Control>().OrderBy(GetRow))
{
_colMatrix[c, c].OfferedSize = _colMatrix[c, c].DesiredSize;
totalConsumedX += _colMatrix[c, c].OfferedSize;
var (row, rowSpan) = GetSafeSpan(heightList.Count, GetRow(child), GetRowSpan(child));
rowMeasure.Add(child, (row, rowSpan));
}
for (int r = 0; r < rowMatrixDim; r++)
foreach (var child in Children.OfType<Control>().OrderBy(GetColumn))
{
_rowMatrix[r, r].OfferedSize = _rowMatrix[r, r].DesiredSize;
totalConsumedY += _rowMatrix[r, r].OfferedSize;
var (column, columnSpan) = GetSafeSpan(widthList.Count, GetColumn(child), GetColumnSpan(child));
columnMeasure.Add(child, (column, columnSpan));
}
if (totalConsumedX != finalSize.Width)
{
ExpandStarCols(finalSize);
}
}
if (totalConsumedY != finalSize.Height)
/// <summary>
/// Gets the safe row/column and rowspan/columnspan for a specified range.
/// The user may assign the row/column properties out of the row count or column cout, this method helps to keep them in.
/// </summary>
/// <param name="length">The rows count or the columns count.</param>
/// <param name="userIndex">The row or column that the user assigned.</param>
/// <param name="userSpan">The rowspan or columnspan that the user assigned.</param>
/// <returns>The safe row/column and rowspan/columnspan.</returns>
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
private static (int index, int span) GetSafeSpan(int length, int userIndex, int userSpan)
{
var index = userIndex;
var span = userSpan;
if (userIndex > length)
{
ExpandStarRows(finalSize);
index = length;
span = 1;
}
for (int c = 0; c < colCount; c++)
else if (userIndex + userSpan > length)
{
ColumnDefinitions[c].ActualWidth = _colMatrix[c, c].OfferedSize;
span = length - userIndex + 1;
}
for (int r = 0; r < rowCount; r++)
return (index, span);
}
/// <summary>
/// Return part of a list from the specified start index and its span length.
/// If Avalonia upgrade .NET Core to 2.1 and introduce C# 7.2, we can use Span to do this.
/// </summary>
[Pure]
private static IEnumerable<double> Span(IList<double> list, int index, int span)
{
#if DEBUG
// We do not verify arguments in RELEASE because this is a private method,
// and we must write the correct code before publishing.
if (index >= list.Count) throw new ArgumentOutOfRangeException(nameof(index));
if (span <= 1) throw new ArgumentException("Argument span should not be smaller than 1.", nameof(span));
if (index + span > list.Count) throw new ArgumentOutOfRangeException(nameof(index));
#endif
if (span == 1)
{
RowDefinitions[r].ActualHeight = _rowMatrix[r, r].OfferedSize;
yield return list[index];
yield break;
}
foreach (Control child in Children)
for (var i = index; i < index + span; i++)
{
int col = Math.Min(GetColumn(child), colMatrixDim - 1);
int row = Math.Min(GetRow(child), rowMatrixDim - 1);
int colspan = Math.Min(GetColumnSpan(child), colMatrixDim - col);
int rowspan = Math.Min(GetRowSpan(child), rowMatrixDim - row);
double childFinalX = 0;
double childFinalY = 0;
double childFinalW = 0;
double childFinalH = 0;
for (int c = 0; c < col; c++)
{
childFinalX += _colMatrix[c, c].OfferedSize;
}
for (int c = col; c < col + colspan; c++)
{
childFinalW += _colMatrix[c, c].OfferedSize;
}
for (int r = 0; r < row; r++)
{
childFinalY += _rowMatrix[r, r].OfferedSize;
}
for (int r = row; r < row + rowspan; r++)
{
childFinalH += _rowMatrix[r, r].OfferedSize;
}
child.Arrange(new Rect(childFinalX, childFinalY, childFinalW, childFinalH));
yield return list[i];
}
return finalSize;
}
private static double Clamp(double val, double min, double max)

115
src/Avalonia.Controls/Utils/GridLayout.cs

@ -15,6 +15,115 @@ namespace Avalonia.Controls.Utils
private readonly LengthDefinitions _lengths;
/// <summary>
/// Find out which rows/columns should be measured first. These rows/columns are those that marked with "Auto" size.<para/>
/// These "Auto" size rows/columns behavior like fix-size rows/columns but they can only be determined after Measure.
/// </summary>
/// <returns>The row/column numbers that should be Measure first.</returns>
internal List<int> Prepare()
{
var lengths = _lengths;
return Find().ToList();
IEnumerable<int> Find()
{
for (var i = 0; i < lengths.Count; i++)
{
var unitType = lengths[i].Length.GridUnitType;
if (unitType == GridUnitType.Auto)
{
yield return i;
}
}
}
}
/// <summary>
/// Try to calculate the lengths that will be used to measure the children.<para/>
/// If the <paramref name="containerLength"/> is not enough, we'll even not compress the measure length.
/// So you'd better call <see cref="Prepare"/> first to find out the rows/columns that should be excluded first.
/// </summary>
/// <param name="containerLength">
/// The container length (width or height) excluding some rows/columns.
/// Call <see cref="Prepare"/> first to find out the rows/columns that should be excluded.
/// </param>
/// <returns>The lengths that can be used to measure the children.</returns>
[Pure]
internal List<double> Measure(double containerLength)
{
var lengths = _lengths.Clone();
// Exclude all the pixel lengths, so that we can calculate the star lengths.
containerLength -= lengths
.Where(x => x.Length.IsAbsolute)
.Aggregate(0.0, (sum, add) => sum + add.Length.Value);
// Aggregate the star count, so that we can determine the length of each star unit.
var starCount = lengths
.Where(x => x.Length.IsStar)
.Aggregate(0.0, (sum, add) => sum + add.Length.Value);
// There is no need to care the (starCount == 0). If this happens, we'll ignore all the stars.
var starUnitLength = containerLength / starCount;
// If there is no stars, just return all pixels.
if (Equals(starCount, 0.0))
{
return lengths.Select(x => x.Length.IsAuto ? double.PositiveInfinity : x.Length.Value).ToList();
}
// ---
// Warning! The code below will start to change the lengths item value.
// ---
// Exclude the star unit if its min/max length range does not contain the calculated star length.
var intermediateStarLengths = lengths.Where(x => x.Length.IsStar).ToList();
// Indicate whether all star lengths are in range of min and max or not.
var allInRange = false;
while (!allInRange)
{
foreach (var length in intermediateStarLengths)
{
// Find out if there is any length out of min to max.
var (star, min, max) = (length.Length.Value, length.MinLength, length.MaxLength);
var starLength = star * starUnitLength;
if (starLength < min || starLength > max)
{
// If the star length is out of min to max, change it to a pixel unit.
if (starLength < min)
{
length.Update(min);
starLength = min;
}
else if (starLength > max)
{
length.Update(max);
starLength = max;
}
// Update the rest star length info.
intermediateStarLengths.Remove(length);
containerLength -= starLength;
starCount -= star;
starUnitLength = containerLength / starCount;
break;
}
}
// All lengths are in range, so that we have enough lengths to measure children.
allInRange = true;
foreach (var length in intermediateStarLengths)
{
length.Update(length.Length.Value * starUnitLength);
}
}
// Return the modified lengths as measuring lengths.
return lengths.Select(x =>
x.Length.GridUnitType == GridUnitType.Auto
? double.PositiveInfinity
: x.Length.Value).ToList();
}
/// <summary>
/// Try to calculate the lengths that will be used to measure the children.
/// If the <paramref name="containerLength"/> is not enough, we'll even not compress the measure length.
@ -22,7 +131,7 @@ namespace Avalonia.Controls.Utils
/// <param name="containerLength">The container length, width or height.</param>
/// <returns>The lengths that can be used to measure the children.</returns>
[Pure]
internal List<double> Measure(double containerLength)
internal List<double> Arrange(double containerLength)
{
var lengths = _lengths.Clone();
@ -106,6 +215,10 @@ namespace Avalonia.Controls.Utils
_lengths = lengths.ToList();
}
public LengthDefinition this[int index] => _lengths[index];
public int Count => _lengths.Count;
public IEnumerator<LengthDefinition> GetEnumerator() => _lengths.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

91
tests/Avalonia.Controls.UnitTests/GridLayoutTests.cs

@ -95,5 +95,96 @@ namespace Avalonia.Controls.UnitTests
// Assert
Assert.Equal(measure, new[] { 100d, 200d, 300d });
}
//[Fact]
//public void Arrange_AllPixelLength_Correct()
//{
// // Arrange
// var layout = new GridLayout(new RowDefinitions("100,200,300"));
// // Action
// var arrange = layout.Arrange(800);
// // Assert
// Assert.Equal(arrange, new[] { 100d, 200d, 300d });
//}
//[Fact]
//public void Arrange_AllStarLength_Correct()
//{
// // Arrange
// var layout = new GridLayout(new RowDefinitions("*,2*,3*"));
// // Action
// var arrange = layout.Arrange(600);
// // Assert
// Assert.Equal(arrange, new[] { 100d, 200d, 300d });
//}
//[Fact]
//public void Arrange_MixStarPixelLength_Correct()
//{
// // Arrange
// var layout = new GridLayout(new RowDefinitions("100,2*,3*"));
// // Action
// var arrange = layout.Arrange(600);
// // Assert
// Assert.Equal(arrange, new[] { 100d, 200d, 300d });
//}
//[Fact]
//public void Arrange_MixAutoPixelLength_Correct()
//{
// // Arrange
// var layout = new GridLayout(new RowDefinitions("100,200,Auto"));
// // Action
// var arrange = layout.Arrange(600);
// // Assert
// Assert.Equal(arrange, new[] { 100d, 200d, 300d });
//}
//[Fact]
//public void Arrange_MixAutoStarLength_Correct()
//{
// // Arrange
// var layout = new GridLayout(new RowDefinitions("*,2*,Auto"));
// // Action
// var arrange = layout.Arrange(600);
// // Assert
// Assert.Equal(arrange, new[] { 200d, 400d, double.PositiveInfinity });
//}
//[Fact]
//public void Arrange_MixAutoStarPixelLength_Correct()
//{
// // Arrange
// var layout = new GridLayout(new RowDefinitions("*,200,Auto"));
// // Action
// var arrange = layout.Arrange(600);
// // Assert
// Assert.Equal(arrange, new[] { 400d, 200d, double.PositiveInfinity });
//}
//[Fact]
//public void Arrange_AllPixelLengthButNotEnough_Correct()
//{
// // Arrange
// var layout = new GridLayout(new RowDefinitions("100,200,300"));
// // Action
// var arrange = layout.Arrange(400);
// // Assert
// Assert.Equal(arrange, new[] { 100d, 200d, 300d });
//}
}
}

26
tests/Avalonia.Controls.UnitTests/GridTests.cs

@ -1,8 +1,12 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using Avalonia.Controls;
using Xunit;
@ -124,5 +128,27 @@ namespace Avalonia.Controls.UnitTests
GridAssert.ChildrenHeight(rowGrid, 200, 50, 50);
GridAssert.ChildrenWidth(columnGrid, 200, 50, 50);
}
[Fact]
public void Layout_StarRowColumnWithMaxLength_BoundsCorrect()
{
// Arrange & Action
var rowGrid = GridMock.New(new RowDefinitions
{
new RowDefinition(1, GridUnitType.Star) { MaxHeight = 200 },
new RowDefinition(1, GridUnitType.Star),
new RowDefinition(1, GridUnitType.Star),
}, arrange: 800);
var columnGrid = GridMock.New(new ColumnDefinitions
{
new ColumnDefinition(1, GridUnitType.Star) { MaxWidth = 200 },
new ColumnDefinition(1, GridUnitType.Star),
new ColumnDefinition(1, GridUnitType.Star),
}, arrange: 800);
// Assert
GridAssert.ChildrenHeight(rowGrid, 200, 300, 300);
GridAssert.ChildrenWidth(columnGrid, 200, 300, 300);
}
}
}

Loading…
Cancel
Save