Browse Source

Moar mess

pull/1945/head
wojciech krysiak 8 years ago
committed by Wojciech Krysiak
parent
commit
2754649edd
  1. 267
      src/Avalonia.Controls/Grid.cs
  2. 6
      src/Avalonia.Controls/Utils/GridLayout.cs

267
src/Avalonia.Controls/Grid.cs

@ -51,147 +51,249 @@ namespace Avalonia.Controls
private sealed class SharedSizeScopeHost : IDisposable private sealed class SharedSizeScopeHost : IDisposable
{ {
private class GridMeasureCache private enum MeasurementState
{ {
public Grid Grid { get; } Invalidated,
public DefinitionBase Definition { get; } Measuring,
public double CachedLength { get; set; } Cached
} }
private readonly AvaloniaList<Grid> _participatingGrids; private class MeasurementCache
{
public MeasurementCache(Grid grid)
{
Grid = grid;
Results = grid.RowDefinitions.Cast<DefinitionBase>()
.Concat(grid.ColumnDefinitions)
.Select(d => new MeasurementResult(d))
.ToList();
}
private Dictionary<string, double> _cachedSize = new Dictionary<string, double>(); public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult)
{
RowResult = rowResult;
ColumnResult = columnResult;
MeasurementState = MeasurementState.Cached;
for (int i = 0; i < rowResult.LengthList.Count; i++)
{
Results[i].MeasuredResult = rowResult.LengthList[i];
}
for (int i = 0; i < columnResult.LengthList.Count; i++)
{
Results[i + rowResult.LengthList.Count].MeasuredResult = columnResult.LengthList[i];
}
}
private Dictionary<string, List<Grid>> _gridsInScopes = new Dictionary<string, List<Grid>>(); public void InvalidateMeasure()
{
MeasurementState = MeasurementState.Invalidated;
Results.ForEach(r => r.MeasuredResult = double.NaN);
}
public Grid Grid { get; }
public GridLayout.MeasureResult RowResult { get; private set; }
public GridLayout.MeasureResult ColumnResult { get; private set; }
public MeasurementState MeasurementState { get; private set; }
private Dictionary<string, List<GridMeasureCache>> _scopeCache; public List<MeasurementResult> Results { get; }
private int _leftToMeasure; }
public SharedSizeScopeHost(Control scope) private readonly AvaloniaList<MeasurementCache> _measurementCaches;
private class MeasurementResult
{ {
_participatingGrids = GetParticipatingGrids(scope); public MeasurementResult(DefinitionBase @base)
foreach (var grid in _participatingGrids)
{ {
grid.InvalidateMeasure(); Definition = @base;
AddGridToScopes(grid); MeasuredResult = double.NaN;
} }
public DefinitionBase Definition { get; }
public double MeasuredResult { get; set; }
} }
private bool _invalidating = false; private enum ScopeType
{
Auto,
Fixed
}
internal void InvalidateMeasure(Grid grid) private class Group
{ {
if (_invalidating) public bool IsFixed { get; set; }
return;
_invalidating = true;
List<Grid> candidates = new List<Grid> {grid}; public List<MeasurementResult> Results { get; }
while (candidates.Any())
{
var scopes = candidates.SelectMany(c => c.RowDefinitions.Select(rd => rd.SharedSizeGroup))
.Concat(candidates.SelectMany(c => c.ColumnDefinitions.Select(rd => rd.SharedSizeGroup))).Distinct();
candidates = scopes.SelectMany(r => _scopeCache[r].Select(gmc => gmc.Grid))
.Distinct().Where(c => c.IsMeasureValid).ToList();
candidates.ForEach(c => c.InvalidateMeasure());
}
_invalidating = false; public double CalculatedLength { get; }
} }
private void AddGridToScopes(Grid grid) private Dictionary<string, Group> _groups = new Dictionary<string, Group>();
public SharedSizeScopeHost(Control scope)
{ {
var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) _measurementCaches = GetParticipatingGrids(scope);
.Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct();
foreach (var scopeName in scopeNames) foreach (var cache in _measurementCaches)
{ {
if (!_gridsInScopes.TryGetValue(scopeName, out var list)) cache.Grid.InvalidateMeasure();
_gridsInScopes.Add(scopeName, list = new List<Grid>() ); AddGridToScopes(cache);
list.Add(grid);
} }
} }
private void RemoveGridFromScopes(Grid grid) internal void InvalidateMeasure(Grid grid)
{ {
var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid));
.Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct(); Debug.Assert(cache != null);
foreach (var scopeName in scopeNames)
{ cache.InvalidateMeasure();
Debug.Assert(_gridsInScopes.TryGetValue(scopeName, out var list));
list.Remove(grid);
if (!list.Any())
_gridsInScopes.Remove(scopeName);
}
} }
internal void UpdateMeasureResult(GridLayout.MeasureResult result, ColumnDefinitions columnDefinitions) internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult)
{ {
for (var i = 0; i < columnDefinitions.Count; i++) var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid));
Debug.Assert(cache != null);
cache.UpdateMeasureResult(rowResult, columnResult);
}
internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult)
{
var rowConventions = rowResult.LeanLengthList.ToList();
var rowLengths = rowResult.LengthList.ToList();
var rowDesiredLength = 0.0;
for (int i = 0; i < grid.RowDefinitions.Count; i++)
{ {
if (string.IsNullOrEmpty(columnDefinitions[i].SharedSizeGroup)) var definition = grid.RowDefinitions[i];
if (string.IsNullOrEmpty(definition.SharedSizeGroup))
{
rowDesiredLength += rowResult.LengthList[i];
continue; continue;
// if any in this group is Absolute we don't care about measured values. }
var group = _groups[definition.SharedSizeGroup];
var length = group.Results.Max(g => g.MeasuredResult);
rowConventions[i] = new GridLayout.LengthConvention(
new GridLength(length),
rowResult.LeanLengthList[i].MinLength,
rowResult.LeanLengthList[i].MaxLength
);
rowLengths[i] = length;
rowDesiredLength += length;
} }
}
internal void UpdateMeasureResult(GridLayout.MeasureResult result, RowDefinitions rowDefinitions) var columnConventions = columnResult.LeanLengthList.ToList();
{ var columnLengths = columnResult.LengthList.ToList();
var columnDesiredLength = 0.0;
for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
{
var definition = grid.ColumnDefinitions[i];
if (string.IsNullOrEmpty(definition.SharedSizeGroup))
{
columnDesiredLength += rowResult.LengthList[i];
continue;
}
var group = _groups[definition.SharedSizeGroup];
var length = group.Results.Max(g => g.MeasuredResult);
columnConventions[i] = new GridLayout.LengthConvention(
new GridLength(length),
columnResult.LeanLengthList[i].MinLength,
columnResult.LeanLengthList[i].MaxLength
);
columnLengths[i] = length;
columnDesiredLength += length;
}
return (
new GridLayout.MeasureResult(
rowResult.ContainerLength,
rowDesiredLength,
rowResult.GreedyDesiredLength,//??
rowConventions,
rowLengths),
new GridLayout.MeasureResult(
columnResult.ContainerLength,
columnDesiredLength,
columnResult.GreedyDesiredLength, //??
columnConventions,
columnLengths)
);
} }
internal double GetExistingLimit(DefinitionBase definition)
private void AddGridToScopes(MeasurementCache cache)
{ {
List<GridMeasureCache> cache = _scopeCache[definition.SharedSizeGroup]; foreach (var result in cache.Results)
{
var scopeName = result.Definition.SharedSizeGroup;
if (!_groups.TryGetValue(scopeName, out var group))
_groups.Add(scopeName, group = new Group());
group.IsFixed |= IsFixed(result.Definition);
return cache.Where(gmc => gmc.Grid.IsMeasureValid) group.Results.Add(result);
.Aggregate(double.NaN, (a, gmc) => Math.Max(a, gmc.CachedLength)); }
} }
internal void UpdateExistingLimit(DefinitionBase definition, double limit) private bool IsFixed(DefinitionBase definition)
{ {
List<GridMeasureCache> cache = _scopeCache[definition.SharedSizeGroup]; return ((definition as ColumnDefinition)?.Width ?? ((RowDefinition)definition).Height).IsAbsolute;
cache.Single(gmc => ReferenceEquals(gmc.Definition, definition)).CachedLength = limit;
// if any other are lower - invalidate the grid.
} }
internal void BeginMeasurePass() private void RemoveGridFromScopes(MeasurementCache cache)
{ {
if (_leftToMeasure == 0) foreach (var result in cache.Results)
{ {
_leftToMeasure = _participatingGrids.Count(g => !g.IsMeasureValid); var scopeName = result.Definition.SharedSizeGroup;
Debug.Assert(_groups.TryGetValue(scopeName, out var group));
group.Results.Remove(result);
if (!group.Results.Any())
_groups.Remove(scopeName);
else
{
group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed);
}
} }
} }
private static AvaloniaList<Grid> GetParticipatingGrids(Control scope) private static AvaloniaList<MeasurementCache> GetParticipatingGrids(Control scope)
{ {
var result = scope.GetVisualDescendants().OfType<Grid>(); var result = scope.GetVisualDescendants().OfType<Grid>();
return new AvaloniaList<Grid>(result.Where(g => g.HasSharedSizeGroups())); return new AvaloniaList<MeasurementCache>(
result.Where(g => g.HasSharedSizeGroups())
.Select(g => new MeasurementCache(g)));
} }
public void Dispose() public void Dispose()
{ {
foreach (var grid in _participatingGrids) foreach (var cache in _measurementCaches)
{ {
grid.SharedScopeChanged(); cache.Grid.SharedScopeChanged();
} }
} }
internal void RegisterGrid(Grid toAdd) internal void RegisterGrid(Grid toAdd)
{ {
Debug.Assert(!_participatingGrids.Contains(toAdd)); Debug.Assert(!_measurementCaches.Any(mc => ReferenceEquals(mc.Grid,toAdd)));
_participatingGrids.Add(toAdd); var cache = new MeasurementCache(toAdd);
AddGridToScopes(toAdd); _measurementCaches.Add(cache);
AddGridToScopes(cache);
} }
internal void UnegisterGrid(Grid toRemove) internal void UnegisterGrid(Grid toRemove)
{ {
Debug.Assert(_participatingGrids.Contains(toRemove)); var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove));
_participatingGrids.Remove(toRemove);
RemoveGridFromScopes(toRemove); Debug.Assert(cache != null);
_measurementCaches.Remove(cache);
RemoveGridFromScopes(cache);
} }
} }
@ -473,6 +575,8 @@ namespace Avalonia.Controls
_rowLayoutCache = rowLayout; _rowLayoutCache = rowLayout;
_columnLayoutCache = columnLayout; _columnLayoutCache = columnLayout;
_sharedSizeHost?.UpdateMeasureStatus(this, rowResult, columnResult);
return new Size(columnResult.DesiredLength, rowResult.DesiredLength); return new Size(columnResult.DesiredLength, rowResult.DesiredLength);
// Measure each child only once. // Measure each child only once.
@ -521,9 +625,12 @@ namespace Avalonia.Controls
var (safeColumns, safeRows) = GetSafeColumnRows(); var (safeColumns, safeRows) = GetSafeColumnRows();
var columnLayout = _columnLayoutCache; var columnLayout = _columnLayoutCache;
var rowLayout = _rowLayoutCache; var rowLayout = _rowLayoutCache;
var (rowCache, columnCache) = _sharedSizeHost?.HandleArrange(this, _rowMeasureCache, _columnMeasureCache) ?? (_rowMeasureCache, _columnMeasureCache);
// Calculate for arrange result. // Calculate for arrange result.
var columnResult = columnLayout.Arrange(finalSize.Width, _columnMeasureCache); var columnResult = columnLayout.Arrange(finalSize.Width, rowCache);
var rowResult = rowLayout.Arrange(finalSize.Height, _rowMeasureCache); var rowResult = rowLayout.Arrange(finalSize.Height, columnCache);
// Arrange the children. // Arrange the children.
foreach (var child in Children.OfType<Control>()) foreach (var child in Children.OfType<Control>())
{ {

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

@ -147,10 +147,10 @@ namespace Avalonia.Controls.Utils
/// The measured result that containing the desired size and all the column/row lengths. /// The measured result that containing the desired size and all the column/row lengths.
/// </returns> /// </returns>
[NotNull, Pure] [NotNull, Pure]
internal MeasureResult Measure(double containerLength) internal MeasureResult Measure(double containerLength, IReadOnlyList<LengthConvention> conventions = null)
{ {
// Prepare all the variables that this method needs to use. // Prepare all the variables that this method needs to use.
var conventions = _conventions.Select(x => x.Clone()).ToList(); conventions = conventions ?? _conventions.Select(x => x.Clone()).ToList();
var starCount = conventions.Where(x => x.Length.IsStar).Sum(x => x.Length.Value); var starCount = conventions.Where(x => x.Length.IsStar).Sum(x => x.Length.Value);
var aggregatedLength = 0.0; var aggregatedLength = 0.0;
double starUnitLength; double starUnitLength;
@ -306,7 +306,7 @@ namespace Avalonia.Controls.Utils
if (finalLength - measure.ContainerLength > LayoutTolerance) if (finalLength - measure.ContainerLength > LayoutTolerance)
{ {
// If the final length is larger, we will rerun the whole measure. // If the final length is larger, we will rerun the whole measure.
measure = Measure(finalLength); measure = Measure(finalLength, measure.LeanLengthList);
} }
else if (finalLength - measure.ContainerLength < -LayoutTolerance) else if (finalLength - measure.ContainerLength < -LayoutTolerance)
{ {

Loading…
Cancel
Save