|
@@ -7,21 +7,15 @@ namespace Avalonia
|
|
|
{
|
|
{
|
|
|
internal class ValueStore : IPriorityValueOwner
|
|
internal class ValueStore : IPriorityValueOwner
|
|
|
{
|
|
{
|
|
|
- private struct Entry
|
|
|
|
|
- {
|
|
|
|
|
- internal int PropertyId;
|
|
|
|
|
- internal object Value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ private readonly AvaloniaPropertyValueStore<object> _propertyValues;
|
|
|
|
|
+ private readonly AvaloniaPropertyValueStore<object> _deferredSetters;
|
|
|
private readonly AvaloniaObject _owner;
|
|
private readonly AvaloniaObject _owner;
|
|
|
- private Entry[] _entries;
|
|
|
|
|
|
|
|
|
|
public ValueStore(AvaloniaObject owner)
|
|
public ValueStore(AvaloniaObject owner)
|
|
|
{
|
|
{
|
|
|
_owner = owner;
|
|
_owner = owner;
|
|
|
-
|
|
|
|
|
- // The last item in the list is always int.MaxValue
|
|
|
|
|
- _entries = new[] { new Entry { PropertyId = int.MaxValue, Value = null } };
|
|
|
|
|
|
|
+ _propertyValues = new AvaloniaPropertyValueStore<object>();
|
|
|
|
|
+ _deferredSetters = new AvaloniaPropertyValueStore<object>();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public IDisposable AddBinding(
|
|
public IDisposable AddBinding(
|
|
@@ -31,7 +25,7 @@ namespace Avalonia
|
|
|
{
|
|
{
|
|
|
PriorityValue priorityValue;
|
|
PriorityValue priorityValue;
|
|
|
|
|
|
|
|
- if (TryGetValue(property, out var v))
|
|
|
|
|
|
|
+ if (_propertyValues.TryGetValue(property, out var v))
|
|
|
{
|
|
{
|
|
|
priorityValue = v as PriorityValue;
|
|
priorityValue = v as PriorityValue;
|
|
|
|
|
|
|
@@ -39,13 +33,13 @@ namespace Avalonia
|
|
|
{
|
|
{
|
|
|
priorityValue = CreatePriorityValue(property);
|
|
priorityValue = CreatePriorityValue(property);
|
|
|
priorityValue.SetValue(v, (int)BindingPriority.LocalValue);
|
|
priorityValue.SetValue(v, (int)BindingPriority.LocalValue);
|
|
|
- SetValueInternal(property, priorityValue);
|
|
|
|
|
|
|
+ _propertyValues.SetValue(property, priorityValue);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
priorityValue = CreatePriorityValue(property);
|
|
priorityValue = CreatePriorityValue(property);
|
|
|
- AddValueInternal(property, priorityValue);
|
|
|
|
|
|
|
+ _propertyValues.AddValue(property, priorityValue);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return priorityValue.Add(source, (int)priority);
|
|
return priorityValue.Add(source, (int)priority);
|
|
@@ -55,7 +49,7 @@ namespace Avalonia
|
|
|
{
|
|
{
|
|
|
PriorityValue priorityValue;
|
|
PriorityValue priorityValue;
|
|
|
|
|
|
|
|
- if (TryGetValue(property, out var v))
|
|
|
|
|
|
|
+ if (_propertyValues.TryGetValue(property, out var v))
|
|
|
{
|
|
{
|
|
|
priorityValue = v as PriorityValue;
|
|
priorityValue = v as PriorityValue;
|
|
|
|
|
|
|
@@ -63,7 +57,7 @@ namespace Avalonia
|
|
|
{
|
|
{
|
|
|
if (priority == (int)BindingPriority.LocalValue)
|
|
if (priority == (int)BindingPriority.LocalValue)
|
|
|
{
|
|
{
|
|
|
- SetValueInternal(property, Validate(property, value));
|
|
|
|
|
|
|
+ _propertyValues.SetValue(property, Validate(property, value));
|
|
|
Changed(property, priority, v, value);
|
|
Changed(property, priority, v, value);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -71,7 +65,7 @@ namespace Avalonia
|
|
|
{
|
|
{
|
|
|
priorityValue = CreatePriorityValue(property);
|
|
priorityValue = CreatePriorityValue(property);
|
|
|
priorityValue.SetValue(v, (int)BindingPriority.LocalValue);
|
|
priorityValue.SetValue(v, (int)BindingPriority.LocalValue);
|
|
|
- SetValueInternal(property, priorityValue);
|
|
|
|
|
|
|
+ _propertyValues.SetValue(property, priorityValue);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -84,14 +78,14 @@ namespace Avalonia
|
|
|
|
|
|
|
|
if (priority == (int)BindingPriority.LocalValue)
|
|
if (priority == (int)BindingPriority.LocalValue)
|
|
|
{
|
|
{
|
|
|
- AddValueInternal(property, Validate(property, value));
|
|
|
|
|
|
|
+ _propertyValues.AddValue(property, Validate(property, value));
|
|
|
Changed(property, priority, AvaloniaProperty.UnsetValue, value);
|
|
Changed(property, priority, AvaloniaProperty.UnsetValue, value);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
priorityValue = CreatePriorityValue(property);
|
|
priorityValue = CreatePriorityValue(property);
|
|
|
- AddValueInternal(property, priorityValue);
|
|
|
|
|
|
|
+ _propertyValues.AddValue(property, priorityValue);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -110,14 +104,9 @@ namespace Avalonia
|
|
|
|
|
|
|
|
public IDictionary<AvaloniaProperty, object> GetSetValues()
|
|
public IDictionary<AvaloniaProperty, object> GetSetValues()
|
|
|
{
|
|
{
|
|
|
- var dict = new Dictionary<AvaloniaProperty, object>(_entries.Length - 1);
|
|
|
|
|
- for (int i = 0; i < _entries.Length - 1; ++i)
|
|
|
|
|
- {
|
|
|
|
|
- dict.Add(AvaloniaPropertyRegistry.Instance.FindRegistered(_entries[i].PropertyId), _entries[i].Value);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return dict;
|
|
|
|
|
|
|
+ return _propertyValues.ToDictionary();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
public void LogError(AvaloniaProperty property, Exception e)
|
|
public void LogError(AvaloniaProperty property, Exception e)
|
|
|
{
|
|
{
|
|
|
_owner.LogBindingError(property, e);
|
|
_owner.LogBindingError(property, e);
|
|
@@ -127,7 +116,7 @@ namespace Avalonia
|
|
|
{
|
|
{
|
|
|
var result = AvaloniaProperty.UnsetValue;
|
|
var result = AvaloniaProperty.UnsetValue;
|
|
|
|
|
|
|
|
- if (TryGetValue(property, out var value))
|
|
|
|
|
|
|
+ if (_propertyValues.TryGetValue(property, out var value))
|
|
|
{
|
|
{
|
|
|
result = (value is PriorityValue priorityValue) ? priorityValue.Value : value;
|
|
result = (value is PriorityValue priorityValue) ? priorityValue.Value : value;
|
|
|
}
|
|
}
|
|
@@ -137,12 +126,12 @@ namespace Avalonia
|
|
|
|
|
|
|
|
public bool IsAnimating(AvaloniaProperty property)
|
|
public bool IsAnimating(AvaloniaProperty property)
|
|
|
{
|
|
{
|
|
|
- return TryGetValue(property, out var value) && value is PriorityValue priority && priority.IsAnimating;
|
|
|
|
|
|
|
+ return _propertyValues.TryGetValue(property, out var value) && value is PriorityValue priority && priority.IsAnimating;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public bool IsSet(AvaloniaProperty property)
|
|
public bool IsSet(AvaloniaProperty property)
|
|
|
{
|
|
{
|
|
|
- if (TryGetValue(property, out var value))
|
|
|
|
|
|
|
+ if (_propertyValues.TryGetValue(property, out var value))
|
|
|
{
|
|
{
|
|
|
return ((value as PriorityValue)?.Value ?? value) != AvaloniaProperty.UnsetValue;
|
|
return ((value as PriorityValue)?.Value ?? value) != AvaloniaProperty.UnsetValue;
|
|
|
}
|
|
}
|
|
@@ -152,7 +141,7 @@ namespace Avalonia
|
|
|
|
|
|
|
|
public void Revalidate(AvaloniaProperty property)
|
|
public void Revalidate(AvaloniaProperty property)
|
|
|
{
|
|
{
|
|
|
- if (TryGetValue(property, out var value))
|
|
|
|
|
|
|
+ if (_propertyValues.TryGetValue(property, out var value))
|
|
|
{
|
|
{
|
|
|
(value as PriorityValue)?.Revalidate();
|
|
(value as PriorityValue)?.Revalidate();
|
|
|
}
|
|
}
|
|
@@ -189,113 +178,28 @@ namespace Avalonia
|
|
|
return value;
|
|
return value;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private DeferredSetter<object> _deferredSetter;
|
|
|
|
|
-
|
|
|
|
|
- public DeferredSetter<object> Setter
|
|
|
|
|
|
|
+ private DeferredSetter<T> GetDeferredSetter<T>(AvaloniaProperty property)
|
|
|
{
|
|
{
|
|
|
- get
|
|
|
|
|
|
|
+ if (_deferredSetters.TryGetValue(property, out var deferredSetter))
|
|
|
{
|
|
{
|
|
|
- return _deferredSetter ??
|
|
|
|
|
- (_deferredSetter = new DeferredSetter<object>());
|
|
|
|
|
|
|
+ return (DeferredSetter<T>)deferredSetter;
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- private bool TryGetValue(AvaloniaProperty property, out object value)
|
|
|
|
|
- {
|
|
|
|
|
- (int index, bool found) = TryFindEntry(property.Id);
|
|
|
|
|
- if (!found)
|
|
|
|
|
- {
|
|
|
|
|
- value = null;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- value = _entries[index].Value;
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void AddValueInternal(AvaloniaProperty property, object value)
|
|
|
|
|
- {
|
|
|
|
|
- Entry[] entries = new Entry[_entries.Length + 1];
|
|
|
|
|
-
|
|
|
|
|
- for (int i = 0; i < _entries.Length; ++i)
|
|
|
|
|
- {
|
|
|
|
|
- if (_entries[i].PropertyId > property.Id)
|
|
|
|
|
- {
|
|
|
|
|
- if (i > 0)
|
|
|
|
|
- {
|
|
|
|
|
- Array.Copy(_entries, 0, entries, 0, i);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var newDeferredSetter = new DeferredSetter<T>();
|
|
|
|
|
|
|
|
- entries[i] = new Entry { PropertyId = property.Id, Value = value };
|
|
|
|
|
- Array.Copy(_entries, i, entries, i + 1, _entries.Length - i);
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _deferredSetters.AddValue(property, newDeferredSetter);
|
|
|
|
|
|
|
|
- _entries = entries;
|
|
|
|
|
|
|
+ return newDeferredSetter;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void SetValueInternal(AvaloniaProperty property, object value)
|
|
|
|
|
|
|
+ public DeferredSetter<object> GetNonDirectDeferredSetter(AvaloniaProperty property)
|
|
|
{
|
|
{
|
|
|
- _entries[TryFindEntry(property.Id).Item1].Value = value;
|
|
|
|
|
|
|
+ return GetDeferredSetter<object>(property);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private (int, bool) TryFindEntry(int propertyId)
|
|
|
|
|
|
|
+ public DeferredSetter<T> GetDirectDeferredSetter<T>(AvaloniaProperty<T> property)
|
|
|
{
|
|
{
|
|
|
- if (_entries.Length <= 12)
|
|
|
|
|
- {
|
|
|
|
|
- // For small lists, we use an optimized linear search. Since the last item in the list
|
|
|
|
|
- // is always int.MaxValue, we can skip a conditional branch in each iteration.
|
|
|
|
|
- // By unrolling the loop, we can skip another unconditional branch in each iteration.
|
|
|
|
|
-
|
|
|
|
|
- if (_entries[0].PropertyId >= propertyId) return (0, _entries[0].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[1].PropertyId >= propertyId) return (1, _entries[1].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[2].PropertyId >= propertyId) return (2, _entries[2].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[3].PropertyId >= propertyId) return (3, _entries[3].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[4].PropertyId >= propertyId) return (4, _entries[4].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[5].PropertyId >= propertyId) return (5, _entries[5].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[6].PropertyId >= propertyId) return (6, _entries[6].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[7].PropertyId >= propertyId) return (7, _entries[7].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[8].PropertyId >= propertyId) return (8, _entries[8].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[9].PropertyId >= propertyId) return (9, _entries[9].PropertyId == propertyId);
|
|
|
|
|
- if (_entries[10].PropertyId >= propertyId) return (10, _entries[10].PropertyId == propertyId);
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- int low = 0;
|
|
|
|
|
- int high = _entries.Length;
|
|
|
|
|
- int id;
|
|
|
|
|
-
|
|
|
|
|
- while (high - low > 3)
|
|
|
|
|
- {
|
|
|
|
|
- int pivot = (high + low) / 2;
|
|
|
|
|
- id = _entries[pivot].PropertyId;
|
|
|
|
|
-
|
|
|
|
|
- if (propertyId == id)
|
|
|
|
|
- return (pivot, true);
|
|
|
|
|
-
|
|
|
|
|
- if (propertyId <= id)
|
|
|
|
|
- high = pivot;
|
|
|
|
|
- else
|
|
|
|
|
- low = pivot + 1;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- do
|
|
|
|
|
- {
|
|
|
|
|
- id = _entries[low].PropertyId;
|
|
|
|
|
-
|
|
|
|
|
- if (id == propertyId)
|
|
|
|
|
- return (low, true);
|
|
|
|
|
-
|
|
|
|
|
- if (id > propertyId)
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- ++low;
|
|
|
|
|
- }
|
|
|
|
|
- while (low < high);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return (0, false);
|
|
|
|
|
|
|
+ return GetDeferredSetter<T>(property);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|