Fix Grid Starred Row/Column regressions (#11903)
* Prevent partial-starred columns from contracting Grid below width (fixes #11742) Account for minimum row sizes when contracting rows (fixes #11835) * Revert android project changes * Drop comments fixes #11742 fixes #11835 fixes #11796
This commit is contained in:
Родитель
916af286a3
Коммит
22f5bf8f5d
|
@ -106,6 +106,171 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
Assert.That(column0Height, Is.LessThan(gridHeight));
|
||||
}
|
||||
|
||||
[Test(Description = "Columns with a Star width less than one should not cause the Grid to contract below the target width; see https://github.com/xamarin/Xamarin.Forms/issues/11742")]
|
||||
public void StarWidthsLessThanOneShouldNotContractGrid()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
VerticalOptions = LayoutOptions.Start,
|
||||
ColumnSpacing = 12
|
||||
};
|
||||
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0.8, GridUnitType.Star) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Star });
|
||||
|
||||
grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
|
||||
|
||||
var label0 = new ColumnTestLabel
|
||||
{
|
||||
VerticalOptions = LayoutOptions.Start,
|
||||
LineBreakMode = LineBreakMode.WordWrap,
|
||||
Text = "This should wrap a bit"
|
||||
};
|
||||
|
||||
var label1 = new ColumnTestLabel
|
||||
{
|
||||
VerticalOptions = LayoutOptions.Start,
|
||||
LineBreakMode = LineBreakMode.WordWrap,
|
||||
Text = "This ought to fit in the space just fine."
|
||||
};
|
||||
|
||||
grid.Children.Add(label0, 0, 0);
|
||||
grid.Children.Add(label1, 1, 0);
|
||||
|
||||
var gridWidth = 411;
|
||||
grid.Measure(gridWidth, 1000);
|
||||
var column0Width = grid.ColumnDefinitions[0].ActualWidth;
|
||||
var column1Width = grid.ColumnDefinitions[1].ActualWidth;
|
||||
|
||||
Assert.That(column0Width, Is.LessThan(column1Width));
|
||||
|
||||
// Having a first column which is a fraction of a Star width should not cause the grid
|
||||
// to contract below the target width
|
||||
Assert.That(column0Width + column1Width, Is.GreaterThanOrEqualTo(gridWidth));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContractionAppliedEquallyOnMultiStarColumns()
|
||||
{
|
||||
var grid = new Grid { ColumnSpacing = 0 };
|
||||
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(2, GridUnitType.Star) });
|
||||
|
||||
grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
|
||||
|
||||
var smaller = new Size(100, 10);
|
||||
var larger = new Size(200, 10);
|
||||
var min = new Size(0, 10);
|
||||
|
||||
var label0 = new FixedSizeLabel(min, smaller)
|
||||
{
|
||||
Text = "label0"
|
||||
};
|
||||
|
||||
var label1 = new FixedSizeLabel(min, larger)
|
||||
{
|
||||
Text = "label1"
|
||||
};
|
||||
|
||||
grid.Children.Add(label0, 0, 0);
|
||||
grid.Children.Add(label1, 1, 0);
|
||||
|
||||
// requested total width is 300, so this will force a contraction to 200
|
||||
grid.Measure(200, 100);
|
||||
var column0Width = grid.ColumnDefinitions[0].ActualWidth;
|
||||
var column1Width = grid.ColumnDefinitions[1].ActualWidth;
|
||||
|
||||
Assert.That(column0Width, Is.EqualTo(column1Width / 2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AllStarColumnsCanOnlyContractToTheLargestMinimum()
|
||||
{
|
||||
var grid = new Grid { ColumnSpacing = 0 };
|
||||
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
|
||||
|
||||
grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
|
||||
|
||||
var leftColumn = new Size(100, 10);
|
||||
var rightColumn = new Size(100, 10);
|
||||
var largerMin = new Size(75, 10);
|
||||
var smallerMin = new Size(50, 10);
|
||||
|
||||
var leftLabel = new FixedSizeLabel(largerMin, leftColumn)
|
||||
{
|
||||
Text = "label0"
|
||||
};
|
||||
|
||||
var rightLabel = new FixedSizeLabel(smallerMin, rightColumn)
|
||||
{
|
||||
Text = "label1"
|
||||
};
|
||||
|
||||
grid.Children.Add(leftLabel, 0, 0);
|
||||
grid.Children.Add(rightLabel, 1, 0);
|
||||
|
||||
// requested total width is 200, so this will force an attemped contraction to 100
|
||||
grid.Measure(100, 100);
|
||||
var column0Width = grid.ColumnDefinitions[0].ActualWidth;
|
||||
var column1Width = grid.ColumnDefinitions[1].ActualWidth;
|
||||
|
||||
Assert.That(column0Width, Is.EqualTo(column1Width));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContractionAppliedEquallyOnMultiStarRows()
|
||||
{
|
||||
var grid = new Grid { ColumnSpacing = 0, RowSpacing = 0 };
|
||||
|
||||
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
|
||||
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(2, GridUnitType.Star) });
|
||||
|
||||
var smaller = new Size(100, 100);
|
||||
var larger = new Size(100, 200);
|
||||
var min = new Size(100, 0);
|
||||
|
||||
var label0 = new FixedSizeLabel(min, smaller)
|
||||
{
|
||||
Text = "label0"
|
||||
};
|
||||
|
||||
var label1 = new FixedSizeLabel(min, larger)
|
||||
{
|
||||
Text = "label1"
|
||||
};
|
||||
|
||||
grid.Children.Add(label0, 0, 0);
|
||||
grid.Children.Add(label1, 1, 0);
|
||||
|
||||
// requested total height is 300, so this will force a contraction to 200
|
||||
grid.Measure(200, 200);
|
||||
var column0Height = grid.RowDefinitions[0].ActualHeight;
|
||||
var column1Height = grid.RowDefinitions[1].ActualHeight;
|
||||
|
||||
Assert.That(column0Height, Is.EqualTo(column1Height / 2));
|
||||
}
|
||||
|
||||
class FixedSizeLabel : Label
|
||||
{
|
||||
readonly Size _minimumSize;
|
||||
readonly Size _requestedSize;
|
||||
|
||||
public FixedSizeLabel(Size minimumSize, Size requestedSize)
|
||||
{
|
||||
IsPlatformEnabled = true;
|
||||
_minimumSize = minimumSize;
|
||||
_requestedSize = requestedSize;
|
||||
}
|
||||
|
||||
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
return new SizeRequest(_requestedSize, _minimumSize);
|
||||
}
|
||||
}
|
||||
|
||||
class ColumnTestLabel : Label
|
||||
{
|
||||
public ColumnTestLabel()
|
||||
|
|
|
@ -220,7 +220,7 @@ namespace Xamarin.Forms
|
|||
return rowHeightSum;
|
||||
}
|
||||
|
||||
Size ComputeCurrentSize()
|
||||
Size ComputeCurrentSize()
|
||||
{
|
||||
var columnWidthSum = ComputeColumnWidthSum();
|
||||
var rowHeightSum = ComputeRowHeightSum();
|
||||
|
@ -318,7 +318,14 @@ namespace Xamarin.Forms
|
|||
continue;
|
||||
}
|
||||
|
||||
starColumnWidth = column.ActualWidth;
|
||||
if (starColumnWidth == 0)
|
||||
{
|
||||
starColumnWidth = column.ActualWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
starColumnWidth = Math.Min(column.ActualWidth, starColumnWidth);
|
||||
}
|
||||
|
||||
if (column.MinimumWidth > starColumnMinWidth)
|
||||
{
|
||||
|
@ -335,13 +342,10 @@ namespace Xamarin.Forms
|
|||
|
||||
// contract as much as we can but no more
|
||||
double contractionNeeded = Math.Min(contractionSpace, Math.Max(request.Width - targetWidth, 0));
|
||||
double contractFactor = contractionNeeded / contractionSpace;
|
||||
var delta = contractFactor * starColumnWidth;
|
||||
|
||||
if (starColumnWidth - delta <= starColumnMinWidth)
|
||||
{
|
||||
delta = starColumnWidth - starColumnMinWidth;
|
||||
}
|
||||
double contractionFactor = contractionNeeded / contractionSpace;
|
||||
var delta = contractionFactor >= 1
|
||||
? starColumnWidth - starColumnMinWidth
|
||||
: contractionFactor * (starColumnWidth - starColumnMinWidth);
|
||||
|
||||
for (var index = 0; index < _columns.Count; index++)
|
||||
{
|
||||
|
@ -351,11 +355,11 @@ namespace Xamarin.Forms
|
|||
continue;
|
||||
}
|
||||
|
||||
column.ActualWidth -= delta;
|
||||
column.ActualWidth -= delta * column.Width.Value;
|
||||
}
|
||||
}
|
||||
|
||||
void ContractStarRowsIfNeeded(double targetHeight)
|
||||
void ContractStarRowsIfNeeded(double targetHeight)
|
||||
{
|
||||
var request = ComputeCurrentSize();
|
||||
|
||||
|
@ -377,7 +381,14 @@ namespace Xamarin.Forms
|
|||
continue;
|
||||
}
|
||||
|
||||
starRowHeight = row.ActualHeight;
|
||||
if (starRowHeight == 0)
|
||||
{
|
||||
starRowHeight = row.ActualHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
starRowHeight = Math.Min(row.ActualHeight, starRowHeight);
|
||||
}
|
||||
|
||||
if (row.MinimumHeight > starRowMinHeight)
|
||||
{
|
||||
|
@ -394,12 +405,9 @@ namespace Xamarin.Forms
|
|||
|
||||
double contractionNeeded = Math.Min(contractionSpace, Math.Max(request.Height - targetHeight, 0));
|
||||
double contractionFactor = contractionNeeded / contractionSpace;
|
||||
var delta = contractionFactor * starRowHeight;
|
||||
|
||||
if (starRowHeight - delta <= starRowMinHeight)
|
||||
{
|
||||
delta = starRowHeight - starRowMinHeight;
|
||||
}
|
||||
var delta = contractionFactor >= 1
|
||||
? starRowHeight - starRowMinHeight
|
||||
: contractionFactor * (starRowHeight - starRowMinHeight);
|
||||
|
||||
for (var index = 0; index < _rows.Count; index++)
|
||||
{
|
||||
|
@ -409,7 +417,7 @@ namespace Xamarin.Forms
|
|||
continue;
|
||||
}
|
||||
|
||||
row.ActualHeight -= delta;
|
||||
row.ActualHeight -= delta * row.Height.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,7 +543,7 @@ namespace Xamarin.Forms
|
|||
void MeasureAndContractStarredRows(double width, double height, double totalStarsHeight)
|
||||
{
|
||||
double starRowHeight;
|
||||
starRowHeight = MeasureStarredRows();
|
||||
starRowHeight = MeasureStarredRows(width, height);
|
||||
|
||||
if (!double.IsPositiveInfinity(height) && double.IsPositiveInfinity(width))
|
||||
{
|
||||
|
@ -558,59 +566,7 @@ namespace Xamarin.Forms
|
|||
|
||||
ContractStarRowsIfNeeded(height);
|
||||
}
|
||||
|
||||
double MeasuredStarredColumns(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
double starColWidth;
|
||||
for (var iteration = 0; iteration < 2; iteration++)
|
||||
{
|
||||
for (var colspan = 1; colspan <= _columns.Count; colspan++)
|
||||
{
|
||||
for (var i = 0; i < _columns.Count; i++)
|
||||
{
|
||||
ColumnDefinition col = _columns[i];
|
||||
if (!col.Width.IsStar)
|
||||
continue;
|
||||
if (col.ActualWidth >= 0) // if Actual is already set (by a smaller span), skip
|
||||
continue;
|
||||
|
||||
double actualWidth = col.ActualWidth;
|
||||
double minimumWidth = col.MinimumWidth;
|
||||
for (var index = 0; index < InternalChildren.Count; index++)
|
||||
{
|
||||
var child = (View)InternalChildren[index];
|
||||
if (!child.IsVisible || GetColumnSpan(child) != colspan || !IsInColumn(child, i) || NumberOfUnsetColumnWidth(child) > 1)
|
||||
continue;
|
||||
double assignedWidth = GetAssignedColumnWidth(child);
|
||||
|
||||
// Can we start with a more reasonable constraint here? if our starred column count is greater than 1, widthConstraint _has_ to be too big
|
||||
|
||||
SizeRequest sizeRequest = child.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
|
||||
actualWidth = Math.Max(actualWidth, sizeRequest.Request.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
|
||||
minimumWidth = Math.Max(minimumWidth, sizeRequest.Minimum.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
|
||||
}
|
||||
if (actualWidth >= 0)
|
||||
col.ActualWidth = actualWidth;
|
||||
|
||||
if (minimumWidth >= 0)
|
||||
col.MinimumWidth = minimumWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Measure the stars
|
||||
starColWidth = 1;
|
||||
for (var index = 0; index < _columns.Count; index++)
|
||||
{
|
||||
ColumnDefinition col = _columns[index];
|
||||
if (!col.Width.IsStar)
|
||||
continue;
|
||||
starColWidth = col.Width.Value != 0 ? Math.Max(starColWidth, col.ActualWidth / col.Width.Value) : 0;
|
||||
}
|
||||
|
||||
return starColWidth;
|
||||
}
|
||||
|
||||
|
||||
void MeasureGrid(double width, double height, bool requestSize = false)
|
||||
{
|
||||
EnsureRowsColumnsInitialized();
|
||||
|
@ -657,7 +613,56 @@ namespace Xamarin.Forms
|
|||
ExpandLastAutoColumnIfNeeded(width, requestSize);
|
||||
}
|
||||
|
||||
double MeasureStarredRows()
|
||||
double MeasuredStarredColumns(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
double starColWidth;
|
||||
for (var iteration = 0; iteration < 2; iteration++)
|
||||
{
|
||||
for (var colspan = 1; colspan <= _columns.Count; colspan++)
|
||||
{
|
||||
for (var i = 0; i < _columns.Count; i++)
|
||||
{
|
||||
ColumnDefinition col = _columns[i];
|
||||
if (!col.Width.IsStar)
|
||||
continue;
|
||||
if (col.ActualWidth >= 0) // if Actual is already set (by a smaller span), skip
|
||||
continue;
|
||||
|
||||
double actualWidth = col.ActualWidth;
|
||||
double minimumWidth = col.MinimumWidth;
|
||||
for (var index = 0; index < InternalChildren.Count; index++)
|
||||
{
|
||||
var child = (View)InternalChildren[index];
|
||||
if (!child.IsVisible || GetColumnSpan(child) != colspan || !IsInColumn(child, i) || NumberOfUnsetColumnWidth(child) > 1)
|
||||
continue;
|
||||
double assignedWidth = GetAssignedColumnWidth(child);
|
||||
|
||||
SizeRequest sizeRequest = child.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
|
||||
actualWidth = Math.Max(actualWidth, sizeRequest.Request.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
|
||||
minimumWidth = Math.Max(minimumWidth, sizeRequest.Minimum.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
|
||||
}
|
||||
if (actualWidth >= 0)
|
||||
col.ActualWidth = actualWidth;
|
||||
|
||||
if (minimumWidth >= 0)
|
||||
col.MinimumWidth = minimumWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
starColWidth = 1;
|
||||
for (var index = 0; index < _columns.Count; index++)
|
||||
{
|
||||
ColumnDefinition col = _columns[index];
|
||||
if (!col.Width.IsStar)
|
||||
continue;
|
||||
starColWidth = col.Width.Value != 0 ? Math.Max(starColWidth, col.ActualWidth / col.Width.Value) : 0;
|
||||
}
|
||||
|
||||
return starColWidth;
|
||||
}
|
||||
|
||||
double MeasureStarredRows(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
double starRowHeight;
|
||||
for (var iteration = 0; iteration < 2; iteration++)
|
||||
|
@ -680,11 +685,10 @@ namespace Xamarin.Forms
|
|||
if (!child.IsVisible || GetRowSpan(child) != rowspan || !IsInRow(child, i) || NumberOfUnsetRowHeight(child) > 1)
|
||||
continue;
|
||||
double assignedHeight = GetAssignedRowHeight(child);
|
||||
double assignedWidth = GetAssignedColumnWidth(child);
|
||||
|
||||
SizeRequest sizeRequest = child.Measure(assignedWidth, double.PositiveInfinity, MeasureFlags.IncludeMargins);
|
||||
actualHeight = Math.Max(actualHeight, sizeRequest.Request.Height - assignedHeight - RowSpacing * (GetRowSpan(child) - 1));
|
||||
minimumHeight = Math.Max(minimumHeight, sizeRequest.Minimum.Height - assignedHeight - RowSpacing * (GetRowSpan(child) - 1));
|
||||
SizeRequest sizeRequest = child.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
|
||||
actualHeight = Math.Max(actualHeight, sizeRequest.Request.Height - assignedHeight - (GetRowSpan(child) - 1) * RowSpacing);
|
||||
minimumHeight = Math.Max(minimumHeight, sizeRequest.Minimum.Height - assignedHeight - (GetRowSpan(child) - 1) * RowSpacing);
|
||||
}
|
||||
if (actualHeight >= 0)
|
||||
row.ActualHeight = actualHeight;
|
||||
|
@ -695,9 +699,6 @@ namespace Xamarin.Forms
|
|||
}
|
||||
}
|
||||
|
||||
// 3. Star columns:
|
||||
|
||||
//Measure the stars
|
||||
starRowHeight = 1;
|
||||
for (var index = 0; index < _rows.Count; index++)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче