Browse Source

Suporting changes to the rows/columns

wojciech krysiak 7 years ago
parent
commit
f876f14afd
1 changed files with 144 additions and 20 deletions
  1. 144 20
      src/Avalonia.Controls/Grid.cs

+ 144 - 20
src/Avalonia.Controls/Grid.cs

@@ -3,8 +3,13 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
 using System.Diagnostics;
 using System.Linq;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Reactive.Subjects;
 using System.Runtime.CompilerServices;
 using Avalonia.Collections;
 using Avalonia.Controls.Utils;
@@ -58,8 +63,13 @@ namespace Avalonia.Controls
                 Cached
             }
 
-            private class MeasurementCache
+            private sealed class MeasurementCache : IDisposable
             {
+                CompositeDisposable _subscriptions;
+
+                Subject<(string, string, MeasurementResult)> _groupChanged = new Subject<(string, string, MeasurementResult)>();
+
+                public ISubject<(string oldName, string newName, MeasurementResult result)> GroupChanged => _groupChanged;
 
                 public MeasurementCache(Grid grid)
                 {
@@ -69,8 +79,96 @@ namespace Avalonia.Controls
                                  .Select(d => new MeasurementResult(d))
                                  .ToList();
 
-                    grid.RowDefinitions.
+                    grid.RowDefinitions.CollectionChanged += DefinitionsCollectionChanged;
+                    grid.ColumnDefinitions.CollectionChanged += DefinitionsCollectionChanged;
+
+                    _subscriptions = new CompositeDisposable(
+                        Disposable.Create(() => grid.RowDefinitions.CollectionChanged -= DefinitionsCollectionChanged),
+                        Disposable.Create(() => grid.ColumnDefinitions.CollectionChanged -= DefinitionsCollectionChanged),
+                        grid.RowDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged),
+                        grid.ColumnDefinitions.TrackItemPropertyChanged(DefinitionPropertyChanged));
+
+                }
+
+                private void DefinitionPropertyChanged(Tuple<object, PropertyChangedEventArgs> propertyChanged)
+                {
+                    if (propertyChanged.Item2.PropertyName == nameof(DefinitionBase.SharedSizeGroup))
+                    {
+                        var oldName = string.Empty;
+                        var newName = (propertyChanged.Item1 as DefinitionBase).SharedSizeGroup;
+                        var result = Results.Single(mr => ReferenceEquals(mr.Definition, propertyChanged.Item2));
+                        _groupChanged.OnNext((oldName, newName, result));
+                    }
+                }
+
+                private void DefinitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+                {
+                    int offset = 0;
+                    if (sender is ColumnDefinitions)
+                        offset = Grid.RowDefinitions.Count;
+
+                    var newItems = e.NewItems?.OfType<DefinitionBase>().Select(db => new MeasurementResult(db)).ToList() ?? new List<MeasurementResult>();
+                    var oldItems = Results.GetRange(e.OldStartingIndex + offset, e.OldItems?.Count ?? 0);
+
+                    void NotifyNewItems()
+                    {
+                        foreach (var item in newItems)
+                        {
+                            if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup))
+                                continue;
+
+                            _groupChanged.OnNext((null, item.Definition.SharedSizeGroup, item));
+                        }
+                    }
+
+                    void NotifyOldItems()
+                    {
+                        foreach (var item in oldItems)
+                        {
+                            if (string.IsNullOrEmpty(item.Definition.SharedSizeGroup))
+                                continue;
+
+                            _groupChanged.OnNext((item.Definition.SharedSizeGroup, null, item));
+                        }
+                    }
 
+                    switch (e.Action)
+                    {
+                        case NotifyCollectionChangedAction.Add:
+                            Results.InsertRange(e.NewStartingIndex + offset, newItems);
+                            NotifyNewItems();
+                            break;
+
+                        case NotifyCollectionChangedAction.Remove:
+                            Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count);
+                            NotifyOldItems();
+                            break;
+
+                        case NotifyCollectionChangedAction.Move:
+                            Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count);
+                            Results.InsertRange(e.NewStartingIndex + offset, oldItems);
+                            break;
+
+                        case NotifyCollectionChangedAction.Replace:
+                            Results.RemoveRange(e.OldStartingIndex + offset, oldItems.Count);
+                            Results.InsertRange(e.NewStartingIndex + offset, newItems);
+
+                            NotifyOldItems();
+                            NotifyNewItems();
+                            
+                            break;
+                           
+                        case NotifyCollectionChangedAction.Reset:
+                            oldItems = Results;
+                            newItems = Results = Grid.RowDefinitions.Cast<DefinitionBase>()
+                                                     .Concat(Grid.ColumnDefinitions)
+                                                     .Select(d => new MeasurementResult(d))
+                                                     .ToList();
+                            NotifyOldItems();
+                            NotifyNewItems();
+
+                            break;
+                    }
                 }
 
                 public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult)
@@ -93,10 +191,16 @@ namespace Avalonia.Controls
                     Results.ForEach(r => r.MeasuredResult = double.NaN);
                 }
 
+                public void Dispose()
+                {
+                    _subscriptions.Dispose();
+                    _groupChanged.OnCompleted();
+                }
+
                 public Grid Grid { get; }
                 public MeasurementState MeasurementState { get; private set; }
 
-                public List<MeasurementResult> Results { get; }
+                public List<MeasurementResult> Results { get; private set; }
             }
 
             private readonly AvaloniaList<MeasurementCache> _measurementCaches;
@@ -133,9 +237,17 @@ namespace Avalonia.Controls
                 {
                     cache.Grid.InvalidateMeasure();
                     AddGridToScopes(cache);
+
+                    cache.GroupChanged.Subscribe(SharedGroupChanged);
                 }
             }
 
+            void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change)
+            {
+                RemoveFromGroup(change.oldName, change.result);
+                AddToGroup(change.newName, change.result);
+            }
+
             internal void InvalidateMeasure(Grid grid)
             {
                 var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid));
@@ -247,15 +359,21 @@ namespace Avalonia.Controls
                 foreach (var result in cache.Results)
                 {
                     var scopeName = result.Definition.SharedSizeGroup;
-                    if (string.IsNullOrEmpty(scopeName))
-                        continue;
-                    if (!_groups.TryGetValue(scopeName, out var group))
-                        _groups.Add(scopeName, group = new Group());
+                    AddToGroup(scopeName, result);
+                }
+            }
 
-                    group.IsFixed |= IsFixed(result.Definition);
+            private void AddToGroup(string scopeName, MeasurementResult result)
+            {
+                if (string.IsNullOrEmpty(scopeName))
+                    return;
 
-                    group.Results.Add(result);
-                }
+                if (!_groups.TryGetValue(scopeName, out var group))
+                    _groups.Add(scopeName, group = new Group());
+
+                group.IsFixed |= IsFixed(result.Definition);
+
+                group.Results.Add(result);
             }
 
             private bool IsFixed(DefinitionBase definition)
@@ -268,17 +386,23 @@ namespace Avalonia.Controls
                 foreach (var result in cache.Results)
                 {
                     var scopeName = result.Definition.SharedSizeGroup;
-                    if (string.IsNullOrEmpty(scopeName))
-                        continue;
-                    Debug.Assert(_groups.TryGetValue(scopeName, out var group));
+                    RemoveFromGroup(scopeName, result);
+                }
+            }
 
-                    group.Results.Remove(result);
-                    if (!group.Results.Any())
-                        _groups.Remove(scopeName);
-                    else
-                    {
-                        group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed);
-                    }
+            private void RemoveFromGroup(string scopeName, MeasurementResult result)
+            {
+                if (string.IsNullOrEmpty(scopeName))
+                    return;
+
+                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);
                 }
             }