From 2754649edd05acf11cf7d15544a2bc3f79b20dc3 Mon Sep 17 00:00:00 2001 From: wojciech krysiak Date: Wed, 3 Oct 2018 07:49:07 +0200 Subject: [PATCH] Moar mess --- src/Avalonia.Controls/Grid.cs | 267 +++++++++++++++------- src/Avalonia.Controls/Utils/GridLayout.cs | 6 +- 2 files changed, 190 insertions(+), 83 deletions(-) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 71ae414a4f..5dc90414ee 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -51,147 +51,249 @@ namespace Avalonia.Controls private sealed class SharedSizeScopeHost : IDisposable { - private class GridMeasureCache + private enum MeasurementState { - public Grid Grid { get; } - public DefinitionBase Definition { get; } - public double CachedLength { get; set; } + Invalidated, + Measuring, + Cached } - private readonly AvaloniaList _participatingGrids; + private class MeasurementCache + { + public MeasurementCache(Grid grid) + { + Grid = grid; + Results = grid.RowDefinitions.Cast() + .Concat(grid.ColumnDefinitions) + .Select(d => new MeasurementResult(d)) + .ToList(); + } - private Dictionary _cachedSize = new Dictionary(); + 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> _gridsInScopes = new Dictionary>(); + 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> _scopeCache; - private int _leftToMeasure; + public List Results { get; } + } - public SharedSizeScopeHost(Control scope) + private readonly AvaloniaList _measurementCaches; + + private class MeasurementResult { - _participatingGrids = GetParticipatingGrids(scope); - - foreach (var grid in _participatingGrids) + public MeasurementResult(DefinitionBase @base) { - grid.InvalidateMeasure(); - AddGridToScopes(grid); + Definition = @base; + 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) - return; - _invalidating = true; + public bool IsFixed { get; set; } - List candidates = new List {grid}; - 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()); - } + public List Results { get; } - _invalidating = false; + public double CalculatedLength { get; } } - private void AddGridToScopes(Grid grid) + private Dictionary _groups = new Dictionary(); + + + public SharedSizeScopeHost(Control scope) { - var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) - .Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct(); - foreach (var scopeName in scopeNames) + _measurementCaches = GetParticipatingGrids(scope); + + foreach (var cache in _measurementCaches) { - if (!_gridsInScopes.TryGetValue(scopeName, out var list)) - _gridsInScopes.Add(scopeName, list = new List() ); - list.Add(grid); + cache.Grid.InvalidateMeasure(); + AddGridToScopes(cache); } } - private void RemoveGridFromScopes(Grid grid) + internal void InvalidateMeasure(Grid grid) { - var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup) - .Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct(); - foreach (var scopeName in scopeNames) - { - Debug.Assert(_gridsInScopes.TryGetValue(scopeName, out var list)); - list.Remove(grid); - if (!list.Any()) - _gridsInScopes.Remove(scopeName); - } + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); + Debug.Assert(cache != null); + + cache.InvalidateMeasure(); } - 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; - // 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 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) - .Aggregate(double.NaN, (a, gmc) => Math.Max(a, gmc.CachedLength)); + group.Results.Add(result); + } } - internal void UpdateExistingLimit(DefinitionBase definition, double limit) + private bool IsFixed(DefinitionBase definition) { - List cache = _scopeCache[definition.SharedSizeGroup]; - - cache.Single(gmc => ReferenceEquals(gmc.Definition, definition)).CachedLength = limit; - // if any other are lower - invalidate the grid. + return ((definition as ColumnDefinition)?.Width ?? ((RowDefinition)definition).Height).IsAbsolute; } - 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 GetParticipatingGrids(Control scope) + private static AvaloniaList GetParticipatingGrids(Control scope) { var result = scope.GetVisualDescendants().OfType(); - return new AvaloniaList(result.Where(g => g.HasSharedSizeGroups())); + return new AvaloniaList( + result.Where(g => g.HasSharedSizeGroups()) + .Select(g => new MeasurementCache(g))); } public void Dispose() { - foreach (var grid in _participatingGrids) + foreach (var cache in _measurementCaches) { - grid.SharedScopeChanged(); + cache.Grid.SharedScopeChanged(); } } internal void RegisterGrid(Grid toAdd) { - Debug.Assert(!_participatingGrids.Contains(toAdd)); - _participatingGrids.Add(toAdd); - AddGridToScopes(toAdd); + Debug.Assert(!_measurementCaches.Any(mc => ReferenceEquals(mc.Grid,toAdd))); + var cache = new MeasurementCache(toAdd); + _measurementCaches.Add(cache); + AddGridToScopes(cache); } internal void UnegisterGrid(Grid toRemove) { - Debug.Assert(_participatingGrids.Contains(toRemove)); - _participatingGrids.Remove(toRemove); - RemoveGridFromScopes(toRemove); + var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); + + Debug.Assert(cache != null); + _measurementCaches.Remove(cache); + RemoveGridFromScopes(cache); } } @@ -473,6 +575,8 @@ namespace Avalonia.Controls _rowLayoutCache = rowLayout; _columnLayoutCache = columnLayout; + _sharedSizeHost?.UpdateMeasureStatus(this, rowResult, columnResult); + return new Size(columnResult.DesiredLength, rowResult.DesiredLength); // Measure each child only once. @@ -521,9 +625,12 @@ namespace Avalonia.Controls var (safeColumns, safeRows) = GetSafeColumnRows(); var columnLayout = _columnLayoutCache; var rowLayout = _rowLayoutCache; + + var (rowCache, columnCache) = _sharedSizeHost?.HandleArrange(this, _rowMeasureCache, _columnMeasureCache) ?? (_rowMeasureCache, _columnMeasureCache); + // Calculate for arrange result. - var columnResult = columnLayout.Arrange(finalSize.Width, _columnMeasureCache); - var rowResult = rowLayout.Arrange(finalSize.Height, _rowMeasureCache); + var columnResult = columnLayout.Arrange(finalSize.Width, rowCache); + var rowResult = rowLayout.Arrange(finalSize.Height, columnCache); // Arrange the children. foreach (var child in Children.OfType()) { diff --git a/src/Avalonia.Controls/Utils/GridLayout.cs b/src/Avalonia.Controls/Utils/GridLayout.cs index 363428b289..b1dca09be2 100644 --- a/src/Avalonia.Controls/Utils/GridLayout.cs +++ b/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. /// [NotNull, Pure] - internal MeasureResult Measure(double containerLength) + internal MeasureResult Measure(double containerLength, IReadOnlyList conventions = null) { // 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 aggregatedLength = 0.0; double starUnitLength; @@ -306,7 +306,7 @@ namespace Avalonia.Controls.Utils if (finalLength - measure.ContainerLength > LayoutTolerance) { // 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) {