Browse Source

Use new AvaloniaPropertyDictionary.

refactor/style-priorities
Steven Kirk 4 years ago
parent
commit
988e26ed9b
  1. 10
      src/Avalonia.Base/PropertyStore/AvaloniaPropertyDictionaryPool.cs
  2. 8
      src/Avalonia.Base/PropertyStore/ValueFrame.cs
  3. 135
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  4. 3
      tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs

10
src/Avalonia.Base/PropertyStore/DictionaryPool.cs → src/Avalonia.Base/PropertyStore/AvaloniaPropertyDictionaryPool.cs

@ -1,19 +1,19 @@
using System.Collections.Generic;
using Avalonia.Utilities;
namespace Avalonia.PropertyStore
{
internal static class DictionaryPool<TKey, TValue>
where TKey : notnull
internal static class AvaloniaPropertyDictionaryPool<TValue>
{
private const int MaxPoolSize = 4;
private static Stack<Dictionary<TKey, TValue>> _pool = new();
private static Stack<AvaloniaPropertyDictionary<TValue>> _pool = new();
public static Dictionary<TKey, TValue> Get()
public static AvaloniaPropertyDictionary<TValue> Get()
{
return _pool.Count == 0 ? new() : _pool.Pop();
}
public static void Release(Dictionary<TKey, TValue> dictionary)
public static void Release(AvaloniaPropertyDictionary<TValue> dictionary)
{
if (_pool.Count < MaxPoolSize)
{

8
src/Avalonia.Base/PropertyStore/ValueFrame.cs

@ -7,14 +7,14 @@ namespace Avalonia.PropertyStore
{
internal abstract class ValueFrame
{
private readonly AvaloniaPropertyValueStore<IValueEntry> _entries = new();
private AvaloniaPropertyDictionary<IValueEntry> _entries = new();
public int EntryCount => _entries.Count;
public abstract bool IsActive { get; }
public ValueStore? Owner { get; private set; }
public BindingPriority Priority { get; protected set; }
public bool Contains(AvaloniaProperty property) => _entries.Contains(property);
public bool Contains(AvaloniaProperty property) => _entries.ContainsKey(property);
public IValueEntry GetEntry(int index) => _entries[index];
@ -40,10 +40,10 @@ namespace Avalonia.PropertyStore
protected void Add(IValueEntry value)
{
Debug.Assert(!value.Property.IsDirect);
_entries.AddValue(value.Property, value);
_entries.Add(value.Property, value);
}
protected void Remove(AvaloniaProperty property) => _entries.Remove(property);
protected void Set(IValueEntry value) => _entries.SetValue(value.Property, value);
protected void Set(IValueEntry value) => _entries[value.Property] = value;
}
}

135
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -6,6 +6,7 @@ using Avalonia.Collections.Pooled;
using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Logging;
using Avalonia.Utilities;
namespace Avalonia.PropertyStore
{
@ -13,7 +14,7 @@ namespace Avalonia.PropertyStore
{
private readonly List<ValueFrame> _frames = new();
private Dictionary<int, IDisposable>? _localValueBindings;
private Dictionary<AvaloniaProperty, EffectiveValue>? _effectiveValues;
private AvaloniaPropertyDictionary<EffectiveValue> _effectiveValues;
private int _inheritedValueCount;
private int _frameGeneration;
private int _styling;
@ -158,7 +159,7 @@ namespace Avalonia.PropertyStore
public object? GetValue(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
if (_effectiveValues.TryGetValue(property, out var v))
return v.Value;
if (property.Inherits && TryGetInheritedValue(property, out v))
return v.Value;
@ -168,7 +169,7 @@ namespace Avalonia.PropertyStore
public T GetValue<T>(StyledPropertyBase<T> property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
if (_effectiveValues.TryGetValue(property, out var v))
return ((EffectiveValue<T>)v).Value;
if (property.Inherits && TryGetInheritedValue(property, out v))
return ((EffectiveValue<T>)v).Value;
@ -177,21 +178,21 @@ namespace Avalonia.PropertyStore
public bool IsAnimating(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
if (_effectiveValues.TryGetValue(property, out var v))
return v.Priority <= BindingPriority.Animation;
return false;
}
public bool IsSet(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
if (_effectiveValues.TryGetValue(property, out var v))
return v.Priority < BindingPriority.Inherited;
return false;
}
public void CoerceValue(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
if (_effectiveValues.TryGetValue(property, out var v))
v.CoerceValue(this, property);
}
@ -227,7 +228,7 @@ namespace Avalonia.PropertyStore
public void SetInheritanceParent(AvaloniaObject? oldParent, AvaloniaObject? newParent)
{
var values = DictionaryPool<AvaloniaProperty, OldNewValue>.Get();
var values = AvaloniaPropertyDictionaryPool<OldNewValue>.Get();
var oldAncestor = InheritanceAncestor;
var newAncestor = newParent?.GetValueStore();
@ -243,15 +244,13 @@ namespace Avalonia.PropertyStore
while (f is not null)
{
Debug.Assert(f._effectiveValues is not null);
var count = f._effectiveValues.Count;
if (f._effectiveValues is not null)
for (var i = 0; i < count; ++i)
{
foreach (var i in f._effectiveValues)
{
if (i.Key.Inherits)
values.TryAdd(i.Key, new(i.Value));
}
f._effectiveValues.GetKeyValue(i, out var key, out var value);
if (key.Inherits)
values.TryAdd(key, new(value));
}
f = f.InheritanceAncestor;
@ -262,17 +261,19 @@ namespace Avalonia.PropertyStore
// Get the new values from the new inheritance ancestor.
while (f is not null)
{
Debug.Assert(f._effectiveValues is not null);
var count = f._effectiveValues.Count;
foreach (var i in f._effectiveValues)
for (var i = 0; i < count; ++i)
{
if (i.Key.Inherits)
{
if (values.TryGetValue(i.Key, out var existing))
values[i.Key] = existing.WithNewValue(i.Value);
else
values.Add(i.Key, new(null, i.Value));
}
f._effectiveValues.GetKeyValue(i, out var key, out var value);
if (!key.Inherits)
continue;
if (values.TryGetValue(key, out var existing))
values[key] = existing.WithNewValue(value);
else
values.Add(key, new(null, value));
}
f = f.InheritanceAncestor;
@ -281,16 +282,20 @@ namespace Avalonia.PropertyStore
OnInheritanceAncestorChanged(newAncestor);
// Raise PropertyChanged events where necessary on this object and inheritance children.
foreach (var i in values)
{
var oldValue = i.Value.OldValue;
var newValue = i.Value.NewValue;
var count = values.Count;
for (var i = 0; i < count; ++i)
{
values.GetKeyValue(i, out var key, out var v);
var oldValue = v.OldValue;
var newValue = v.NewValue;
if (oldValue != newValue)
InheritedValueChanged(i.Key, oldValue, newValue);
if (oldValue != newValue)
InheritedValueChanged(key, oldValue, newValue);
}
}
DictionaryPool<AvaloniaProperty, OldNewValue>.Release(values);
AvaloniaPropertyDictionaryPool<OldNewValue>.Release(values);
}
/// <summary>
@ -497,7 +502,7 @@ namespace Avalonia.PropertyStore
Debug.Assert(property.Inherits);
// If the inherited value is set locally, propagation stops here.
if (_effectiveValues is not null && _effectiveValues.ContainsKey(property))
if (_effectiveValues.ContainsKey(property))
return;
using var notifying = PropertyNotifying.Start(Owner, property);
@ -626,7 +631,6 @@ namespace Avalonia.PropertyStore
private void AddEffectiveValue(AvaloniaProperty property, EffectiveValue effectiveValue)
{
_effectiveValues ??= new();
_effectiveValues.Add(property, effectiveValue);
if (property.Inherits && _inheritedValueCount++ == 0)
@ -668,7 +672,7 @@ namespace Avalonia.PropertyStore
private bool RemoveEffectiveValue(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.Remove(property))
if (_effectiveValues.Remove(property))
{
if (property.Inherits && --_inheritedValueCount == 0)
OnInheritanceAncestorChanged(InheritanceAncestor);
@ -680,7 +684,7 @@ namespace Avalonia.PropertyStore
private bool RemoveEffectiveValue(AvaloniaProperty property, [NotNullWhen(true)] out EffectiveValue? result)
{
if (_effectiveValues is not null && _effectiveValues.Remove(property, out result))
if (_effectiveValues.Remove(property, out result))
{
if (property.Inherits && --_inheritedValueCount == 0)
OnInheritanceAncestorChanged(InheritanceAncestor);
@ -793,7 +797,7 @@ namespace Avalonia.PropertyStore
Debug.Assert(oldValue is not null || newValue is not null);
// If the value is set locally, propagaton ends here.
if (_effectiveValues?.ContainsKey(property) == true)
if (_effectiveValues.ContainsKey(property) == true)
return;
using var notifying = PropertyNotifying.Start(Owner, property);
@ -823,19 +827,17 @@ namespace Avalonia.PropertyStore
return;
var generation = _frameGeneration;
var count = _effectiveValues.Count;
// Reset all non-LocalValue effective values to Unset priority.
if (_effectiveValues is not null)
for (var i = 0; i < count; ++i)
{
foreach (var v in _effectiveValues)
{
var e = v.Value;
var e = _effectiveValues[i];
if (e.Priority != BindingPriority.LocalValue)
e.SetPriority(BindingPriority.Unset);
if (e.BasePriority != BindingPriority.LocalValue)
e.SetBasePriority(BindingPriority.Unset);
}
if (e.Priority != BindingPriority.LocalValue)
e.SetPriority(BindingPriority.Unset);
if (e.BasePriority != BindingPriority.LocalValue)
e.SetBasePriority(BindingPriority.Unset);
}
// Iterate the frames, setting and creating effective values.
@ -847,16 +849,17 @@ namespace Avalonia.PropertyStore
continue;
var priority = frame.Priority;
var count = frame.EntryCount;
count = frame.EntryCount;
for (var j = 0; j < count; ++j)
{
var entry = frame.GetEntry(j);
var property = entry.Property;
EffectiveValue? effectiveValue = null;
EffectiveValue? effectiveValue;
// Skip if we already have a value/base value for this property.
if (_effectiveValues?.TryGetValue(property, out effectiveValue) == true &&
if (_effectiveValues.TryGetValue(property, out effectiveValue) == true &&
effectiveValue.BasePriority < BindingPriority.Unset)
continue;
@ -880,39 +883,37 @@ namespace Avalonia.PropertyStore
}
// Remove all effective values that are still unset.
if (_effectiveValues is not null)
PooledList<AvaloniaProperty>? remove = null;
count = _effectiveValues.Count;
for (var i = 0; i < count; ++i)
{
PooledList<AvaloniaProperty>? remove = null;
_effectiveValues.GetKeyValue(i, out var key, out var e);
foreach (var v in _effectiveValues)
if (e.Priority == BindingPriority.Unset)
{
var e = v.Value;
if (e.Priority == BindingPriority.Unset)
{
remove ??= new();
remove.Add(v.Key);
}
remove ??= new();
remove.Add(key);
}
}
if (remove is not null)
if (remove is not null)
{
foreach (var v in remove)
{
foreach (var v in remove)
{
if (RemoveEffectiveValue(v, out var e))
e.DisposeAndRaiseUnset(this, v);
}
remove.Dispose();
if (RemoveEffectiveValue(v, out var e))
e.DisposeAndRaiseUnset(this, v);
}
remove.Dispose();
}
}
[MemberNotNullWhen(true, nameof(_effectiveValues))]
private bool TryGetEffectiveValue(
AvaloniaProperty property,
[NotNullWhen(true)] out EffectiveValue? value)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out value))
if (_effectiveValues.TryGetValue(property, out value))
return true;
value = null;
return false;
@ -920,7 +921,7 @@ namespace Avalonia.PropertyStore
private EffectiveValue? GetEffectiveValue(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var value))
if (_effectiveValues.TryGetValue(property, out var value))
return value;
return null;
}

3
tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs

@ -188,6 +188,9 @@ namespace Avalonia.Base.UnitTests.Utilities
[MemberData(nameof(Counts))]
public void Remove_Removes_Value(int count)
{
if (count == 0)
return;
var target = CreateTarget(count);
var index = count / 2;
var property = TestProperties[index];

Loading…
Cancel
Save