|
|
@@ -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;
|
|
|
}
|