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 System.Collections.Generic;
using Avalonia.Utilities;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
internal static class DictionaryPool<TKey, TValue> internal static class AvaloniaPropertyDictionaryPool<TValue>
where TKey : notnull
{ {
private const int MaxPoolSize = 4; 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(); 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) if (_pool.Count < MaxPoolSize)
{ {

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

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

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

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

Loading…
Cancel
Save