Browse Source

MessCommit

pull/1945/head
wojciech krysiak 8 years ago
committed by Wojciech Krysiak
parent
commit
269fc2a5d2
  1. 223
      src/Avalonia.Controls/Grid.cs

223
src/Avalonia.Controls/Grid.cs

@ -3,10 +3,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Avalonia.Collections;
using Avalonia.Controls.Utils;
using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls
@ -44,6 +46,189 @@ namespace Avalonia.Controls
public static readonly AttachedProperty<int> RowSpanProperty =
AvaloniaProperty.RegisterAttached<Grid, Control, int>("RowSpan", 1);
public static readonly AttachedProperty<bool> IsSharedSizeScopeProperty =
AvaloniaProperty.RegisterAttached<Grid, Control, bool>("IsSharedSizeScope", false);
private sealed class SharedSizeScopeHost : IDisposable
{
private class GridMeasureCache
{
public Grid Grid { get; }
public DefinitionBase Definition { get; }
public double CachedLength { get; set; }
}
private readonly AvaloniaList<Grid> _participatingGrids;
private Dictionary<string, double> _cachedSize = new Dictionary<string, double>();
private Dictionary<string, List<Grid>> _gridsInScopes = new Dictionary<string, List<Grid>>();
private Dictionary<string, List<GridMeasureCache>> _scopeCache;
private int _leftToMeasure;
public SharedSizeScopeHost(Control scope)
{
_participatingGrids = GetParticipatingGrids(scope);
foreach (var grid in _participatingGrids)
{
grid.InvalidateMeasure();
AddGridToScopes(grid);
}
}
private bool _invalidating = false;
internal void InvalidateMeasure(Grid grid)
{
if (_invalidating)
return;
_invalidating = true;
List<Grid> candidates = new List<Grid> {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());
}
_invalidating = false;
}
private void AddGridToScopes(Grid grid)
{
var scopeNames = grid.ColumnDefinitions.Select(g => g.SharedSizeGroup)
.Concat(grid.RowDefinitions.Select(g => g.SharedSizeGroup)).Distinct();
foreach (var scopeName in scopeNames)
{
if (!_gridsInScopes.TryGetValue(scopeName, out var list))
_gridsInScopes.Add(scopeName, list = new List<Grid>() );
list.Add(grid);
}
}
private void RemoveGridFromScopes(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);
}
}
internal void UpdateMeasureResult(GridLayout.MeasureResult result, ColumnDefinitions columnDefinitions)
{
for (var i = 0; i < columnDefinitions.Count; i++)
{
if (string.IsNullOrEmpty(columnDefinitions[i].SharedSizeGroup))
continue;
// if any in this group is Absolute we don't care about measured values.
}
}
internal void UpdateMeasureResult(GridLayout.MeasureResult result, RowDefinitions rowDefinitions)
{
}
internal double GetExistingLimit(DefinitionBase definition)
{
List<GridMeasureCache> cache = _scopeCache[definition.SharedSizeGroup];
return cache.Where(gmc => gmc.Grid.IsMeasureValid)
.Aggregate(double.NaN, (a, gmc) => Math.Max(a, gmc.CachedLength));
}
internal void UpdateExistingLimit(DefinitionBase definition, double limit)
{
List<GridMeasureCache> cache = _scopeCache[definition.SharedSizeGroup];
cache.Single(gmc => ReferenceEquals(gmc.Definition, definition)).CachedLength = limit;
// if any other are lower - invalidate the grid.
}
internal void BeginMeasurePass()
{
if (_leftToMeasure == 0)
{
_leftToMeasure = _participatingGrids.Count(g => !g.IsMeasureValid);
}
}
private static AvaloniaList<Grid> GetParticipatingGrids(Control scope)
{
var result = scope.GetVisualDescendants().OfType<Grid>();
return new AvaloniaList<Grid>(result.Where(g => g.HasSharedSizeGroups()));
}
public void Dispose()
{
foreach (var grid in _participatingGrids)
{
grid.SharedScopeChanged();
}
}
internal void RegisterGrid(Grid toAdd)
{
Debug.Assert(!_participatingGrids.Contains(toAdd));
_participatingGrids.Add(toAdd);
AddGridToScopes(toAdd);
}
internal void UnegisterGrid(Grid toRemove)
{
Debug.Assert(_participatingGrids.Contains(toRemove));
_participatingGrids.Remove(toRemove);
RemoveGridFromScopes(toRemove);
}
}
protected override void OnMeasureInvalidated()
{
base.OnMeasureInvalidated();
_sharedSizeHost?.InvalidateMeasure(this);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
var scope = this.GetVisualAncestors().OfType<Control>()
.FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty));
Debug.Assert(_sharedSizeHost == null);
if (scope != null)
{
_sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty);
_sharedSizeHost.RegisterGrid(this);
}
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_sharedSizeHost?.UnegisterGrid(this);
_sharedSizeHost = null;
}
private SharedSizeScopeHost _sharedSizeHost;
private static readonly AttachedProperty<SharedSizeScopeHost> s_sharedSizeScopeHostProperty =
AvaloniaProperty.RegisterAttached<Grid, Control, SharedSizeScopeHost>("&&SharedSizeScopeHost", null);
private ColumnDefinitions _columnDefinitions;
private RowDefinitions _rowDefinitions;
@ -51,6 +236,23 @@ namespace Avalonia.Controls
static Grid()
{
AffectsParentMeasure<Grid>(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty);
IsSharedSizeScopeProperty.Changed.AddClassHandler<Control>(IsSharedSizeScopeChanged);
}
private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2)
{
if ((bool)arg2.NewValue)
{
Debug.Assert(source.GetValue(s_sharedSizeScopeHostProperty) == null);
source.SetValue(IsSharedSizeScopeProperty, new SharedSizeScopeHost(source));
}
else
{
var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost;
Debug.Assert(host != null);
host.Dispose();
source.SetValue(IsSharedSizeScopeProperty, null);
}
}
/// <summary>
@ -426,5 +628,26 @@ namespace Avalonia.Controls
return value;
}
internal bool HasSharedSizeGroups()
{
return ColumnDefinitions.Any(cd => !string.IsNullOrEmpty(cd.SharedSizeGroup)) ||
RowDefinitions.Any(rd => !string.IsNullOrEmpty(rd.SharedSizeGroup));
}
internal void SharedScopeChanged()
{
_sharedSizeHost = null;
var scope = this.GetVisualAncestors().OfType<Control>()
.FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty));
if (scope != null)
{
_sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty);
_sharedSizeHost.RegisterGrid(this);
}
InvalidateMeasure();
}
}
}

Loading…
Cancel
Save