|
|
|
@ -1,5 +1,6 @@ |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Diagnostics.CodeAnalysis; |
|
|
|
using Avalonia.Data; |
|
|
|
using Avalonia.PropertyStore; |
|
|
|
using Avalonia.Utilities; |
|
|
|
@ -56,7 +57,7 @@ namespace Avalonia |
|
|
|
|
|
|
|
public bool IsAnimating(AvaloniaProperty property) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
return slot.Priority < BindingPriority.LocalValue; |
|
|
|
} |
|
|
|
@ -66,7 +67,7 @@ namespace Avalonia |
|
|
|
|
|
|
|
public bool IsSet(AvaloniaProperty property) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
return slot.GetValue().HasValue; |
|
|
|
} |
|
|
|
@ -79,7 +80,7 @@ namespace Avalonia |
|
|
|
BindingPriority maxPriority, |
|
|
|
out T value) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
var v = ((IValue<T>)slot).GetValue(maxPriority); |
|
|
|
|
|
|
|
@ -103,7 +104,7 @@ namespace Avalonia |
|
|
|
|
|
|
|
IDisposable? result = null; |
|
|
|
|
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
result = SetExisting(slot, property, value, priority); |
|
|
|
} |
|
|
|
@ -138,7 +139,7 @@ namespace Avalonia |
|
|
|
IObservable<BindingValue<T>> source, |
|
|
|
BindingPriority priority) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
return BindExisting(slot, property, source, priority); |
|
|
|
} |
|
|
|
@ -160,7 +161,7 @@ namespace Avalonia |
|
|
|
|
|
|
|
public void ClearLocalValue<T>(StyledPropertyBase<T> property) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
if (slot is PriorityValue<T> p) |
|
|
|
{ |
|
|
|
@ -173,7 +174,7 @@ namespace Avalonia |
|
|
|
// During batch update values can't be removed immediately because they're needed to raise
|
|
|
|
// a correctly-typed _sink.ValueChanged notification. They instead mark themselves for removal
|
|
|
|
// by setting their priority to Unset.
|
|
|
|
if (_batchUpdate is null) |
|
|
|
if (!IsBatchUpdating()) |
|
|
|
{ |
|
|
|
_values.Remove(property); |
|
|
|
} |
|
|
|
@ -198,7 +199,7 @@ namespace Avalonia |
|
|
|
|
|
|
|
public void CoerceValue<T>(StyledPropertyBase<T> property) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
if (slot is PriorityValue<T> p) |
|
|
|
{ |
|
|
|
@ -209,7 +210,7 @@ namespace Avalonia |
|
|
|
|
|
|
|
public Diagnostics.AvaloniaPropertyValue? GetDiagnostic(AvaloniaProperty property) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var slot)) |
|
|
|
if (TryGetValue(property, out var slot)) |
|
|
|
{ |
|
|
|
var slotValue = slot.GetValue(); |
|
|
|
return new Diagnostics.AvaloniaPropertyValue( |
|
|
|
@ -242,6 +243,7 @@ namespace Avalonia |
|
|
|
IPriorityValueEntry entry, |
|
|
|
Optional<T> oldValue) |
|
|
|
{ |
|
|
|
// We need to include remove sentinels here so call `_values.TryGetValue` directly.
|
|
|
|
if (_values.TryGetValue(property, out var slot) && slot == entry) |
|
|
|
{ |
|
|
|
if (_batchUpdate is null) |
|
|
|
@ -285,7 +287,7 @@ namespace Avalonia |
|
|
|
else |
|
|
|
{ |
|
|
|
var priorityValue = new PriorityValue<T>(_owner, property, this, l); |
|
|
|
if (_batchUpdate is object) |
|
|
|
if (IsBatchUpdating()) |
|
|
|
priorityValue.BeginBatchUpdate(); |
|
|
|
result = priorityValue.SetValue(value, priority); |
|
|
|
_values.SetValue(property, priorityValue); |
|
|
|
@ -311,7 +313,7 @@ namespace Avalonia |
|
|
|
{ |
|
|
|
priorityValue = new PriorityValue<T>(_owner, property, this, e); |
|
|
|
|
|
|
|
if (_batchUpdate is object) |
|
|
|
if (IsBatchUpdating()) |
|
|
|
{ |
|
|
|
priorityValue.BeginBatchUpdate(); |
|
|
|
} |
|
|
|
@ -338,7 +340,7 @@ namespace Avalonia |
|
|
|
private void AddValue(AvaloniaProperty property, IValue value) |
|
|
|
{ |
|
|
|
_values.AddValue(property, value); |
|
|
|
if (_batchUpdate is object && value is IBatchUpdate batch) |
|
|
|
if (IsBatchUpdating() && value is IBatchUpdate batch) |
|
|
|
batch.BeginBatchUpdate(); |
|
|
|
value.Start(); |
|
|
|
} |
|
|
|
@ -364,6 +366,21 @@ namespace Avalonia |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private bool IsBatchUpdating() => _batchUpdate?.IsBatchUpdating == true; |
|
|
|
|
|
|
|
private bool TryGetValue(AvaloniaProperty property, [MaybeNullWhen(false)] out IValue value) |
|
|
|
{ |
|
|
|
return _values.TryGetValue(property, out value) && !IsRemoveSentinel(value); |
|
|
|
} |
|
|
|
|
|
|
|
private static bool IsRemoveSentinel(IValue value) |
|
|
|
{ |
|
|
|
// Local value entries are optimized and contain only a single value field to save space,
|
|
|
|
// so there's no way to mark them for removal at the end of a batch update. Instead a
|
|
|
|
// ConstantValueEntry with a priority of Unset is used as a sentinel value.
|
|
|
|
return value is IConstantValueEntry t && t.Priority == BindingPriority.Unset; |
|
|
|
} |
|
|
|
|
|
|
|
private class BatchUpdate |
|
|
|
{ |
|
|
|
private ValueStore _owner; |
|
|
|
@ -373,6 +390,8 @@ namespace Avalonia |
|
|
|
|
|
|
|
public BatchUpdate(ValueStore owner) => _owner = owner; |
|
|
|
|
|
|
|
public bool IsBatchUpdating => _batchUpdateCount > 0; |
|
|
|
|
|
|
|
public void Begin() |
|
|
|
{ |
|
|
|
if (_batchUpdateCount++ == 0) |
|
|
|
@ -437,8 +456,10 @@ namespace Avalonia |
|
|
|
|
|
|
|
// During batch update values can't be removed immediately because they're needed to raise
|
|
|
|
// the _sink.ValueChanged notification. They instead mark themselves for removal by setting
|
|
|
|
// their priority to Unset.
|
|
|
|
if (slot.Priority == BindingPriority.Unset) |
|
|
|
// their priority to Unset. We need to re-read the slot here because raising ValueChanged
|
|
|
|
// could have caused it to be updated.
|
|
|
|
if (values.TryGetValue(entry.property, out var updatedSlot) && |
|
|
|
updatedSlot.Priority == BindingPriority.Unset) |
|
|
|
{ |
|
|
|
values.Remove(entry.property); |
|
|
|
} |
|
|
|
|