| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using Avalonia.Collections.Pooled;
- using Avalonia.Data;
- using Avalonia.Diagnostics;
- using Avalonia.Logging;
- using Avalonia.Utilities;
- using JetBrains.Annotations;
- namespace Avalonia.PropertyStore
- {
- internal class ValueStore
- {
- private readonly List<ValueFrame> _frames = new();
- private Dictionary<int, IDisposable>? _localValueBindings;
- private AvaloniaPropertyDictionary<EffectiveValue> _effectiveValues;
- private int _inheritedValueCount;
- private int _frameGeneration;
- private int _styling;
- public ValueStore(AvaloniaObject owner) => Owner = owner;
- public AvaloniaObject Owner { get; }
- public ValueStore? InheritanceAncestor { get; private set; }
- public bool IsEvaluating { get; private set; }
- public IReadOnlyList<ValueFrame> Frames => _frames;
- public void BeginStyling() => ++_styling;
- public void EndStyling()
- {
- if (--_styling == 0)
- ReevaluateEffectiveValues();
- }
- public void AddFrame(ValueFrame style)
- {
- InsertFrame(style);
- ReevaluateEffectiveValues();
- }
- public IDisposable AddBinding<T>(
- StyledPropertyBase<T> property,
- IObservable<BindingValue<T>> source,
- BindingPriority priority)
- {
- if (priority == BindingPriority.LocalValue)
- {
- var observer = new LocalValueBindingObserver<T>(this, property);
- DisposeExistingLocalValueBinding(property);
- _localValueBindings ??= new();
- _localValueBindings[property.Id] = observer;
- observer.Start(source);
- return observer;
- }
- else
- {
- var effective = GetEffectiveValue(property);
- var frame = GetOrCreateImmediateValueFrame(property, priority, out var frameIndex);
- var result = frame.AddBinding(property, source);
- if (effective is null || priority <= effective.Priority)
- result.Start();
- return result;
- }
- }
- public IDisposable AddBinding<T>(
- StyledPropertyBase<T> property,
- IObservable<T> source,
- BindingPriority priority)
- {
- if (priority == BindingPriority.LocalValue)
- {
- var observer = new LocalValueBindingObserver<T>(this, property);
- DisposeExistingLocalValueBinding(property);
- _localValueBindings ??= new();
- _localValueBindings[property.Id] = observer;
- observer.Start(source);
- return observer;
- }
- else
- {
- var effective = GetEffectiveValue(property);
- var frame = GetOrCreateImmediateValueFrame(property, priority, out var frameIndex);
- var result = frame.AddBinding(property, source);
- if (effective is null || priority <= effective.Priority)
- result.Start();
- return result;
- }
- }
- public IDisposable AddBinding<T>(
- StyledPropertyBase<T> property,
- IObservable<object?> source,
- BindingPriority priority)
- {
- if (priority == BindingPriority.LocalValue)
- {
- var observer = new LocalValueUntypedBindingObserver<T>(this, property);
- DisposeExistingLocalValueBinding(property);
- _localValueBindings ??= new();
- _localValueBindings[property.Id] = observer;
- observer.Start(source);
- return observer;
- }
- else
- {
- var effective = GetEffectiveValue(property);
- var frame = GetOrCreateImmediateValueFrame(property, priority, out var frameIndex);
- var result = frame.AddBinding(property, source);
- if (effective is null || priority <= effective.Priority)
- result.Start();
- return result;
- }
- }
- public IDisposable AddBinding<T>(DirectPropertyBase<T> property, IObservable<BindingValue<T>> source)
- {
- var observer = new DirectBindingObserver<T>(this, property);
- DisposeExistingLocalValueBinding(property);
- _localValueBindings ??= new();
- _localValueBindings[property.Id] = observer;
- observer.Start(source);
- return observer;
- }
- public IDisposable AddBinding<T>(DirectPropertyBase<T> property, IObservable<T> source)
- {
- var observer = new DirectBindingObserver<T>(this, property);
- DisposeExistingLocalValueBinding(property);
- _localValueBindings ??= new();
- _localValueBindings[property.Id] = observer;
- observer.Start(source);
- return observer;
- }
- public IDisposable AddBinding<T>(DirectPropertyBase<T> property, IObservable<object?> source)
- {
- var observer = new DirectUntypedBindingObserver<T>(this, property);
- DisposeExistingLocalValueBinding(property);
- _localValueBindings ??= new();
- _localValueBindings[property.Id] = observer;
- observer.Start(source);
- return observer;
- }
- public void ClearLocalValue(AvaloniaProperty property)
- {
- if (TryGetEffectiveValue(property, out var effective) &&
- effective.Priority == BindingPriority.LocalValue)
- {
- ReevaluateEffectiveValue(property, effective, ignoreLocalValue: true);
- }
- }
- public IDisposable? SetValue<T>(StyledPropertyBase<T> property, T value, BindingPriority priority)
- {
- if (property.ValidateValue?.Invoke(value) == false)
- {
- throw new ArgumentException($"{value} is not a valid value for '{property.Name}.");
- }
- if (priority != BindingPriority.LocalValue)
- {
- var frame = GetOrCreateImmediateValueFrame(property, priority, out var frameIndex);
- var result = frame.AddValue(property, value);
- if (TryGetEffectiveValue(property, out var existing))
- {
- var effective = (EffectiveValue<T>)existing;
- effective.SetAndRaise(this, result, priority);
- }
- else
- {
- var defaultValue = property.GetDefaultValue(Owner.GetType());
- var effectiveValue = new EffectiveValue<T>(Owner, property, defaultValue, BindingPriority.Unset);
- AddEffectiveValue(property, effectiveValue);
- effectiveValue.SetAndRaise(this, result, priority);
- }
- return result;
- }
- else
- {
- if (TryGetEffectiveValue(property, out var existing))
- {
- var effective = (EffectiveValue<T>)existing;
- effective.SetLocalValueAndRaise(this, property, value);
- }
- else
- {
- var defaultValue = property.GetDefaultValue(Owner.GetType());
- var effectiveValue = new EffectiveValue<T>(Owner, property, defaultValue, BindingPriority.Unset);
- AddEffectiveValue(property, effectiveValue);
- effectiveValue.SetLocalValueAndRaise(this, property, value);
- }
- return null;
- }
- }
- public object? GetValue(AvaloniaProperty property)
- {
- if (_effectiveValues.TryGetValue(property, out var v))
- return v.Value;
- if (property.Inherits && TryGetInheritedValue(property, out v))
- return v.Value;
- return GetDefaultValue(property);
- }
- public T GetValue<T>(StyledPropertyBase<T> property)
- {
- if (_effectiveValues.TryGetValue(property, out var v))
- return ((EffectiveValue<T>)v).Value;
- if (property.Inherits && TryGetInheritedValue(property, out v))
- return ((EffectiveValue<T>)v).Value;
- return property.GetDefaultValue(Owner.GetType());
- }
- public bool IsAnimating(AvaloniaProperty property)
- {
- if (_effectiveValues.TryGetValue(property, out var v))
- return v.Priority <= BindingPriority.Animation;
- return false;
- }
- public bool IsSet(AvaloniaProperty property)
- {
- if (_effectiveValues.TryGetValue(property, out var v))
- return v.Priority < BindingPriority.Inherited;
- return false;
- }
- public void CoerceValue(AvaloniaProperty property)
- {
- if (_effectiveValues.TryGetValue(property, out var v))
- v.CoerceValue(this, property);
- }
- public Optional<T> GetBaseValue<T>(StyledPropertyBase<T> property)
- {
- if (TryGetEffectiveValue(property, out var v) &&
- ((EffectiveValue<T>)v).TryGetBaseValue(out var baseValue))
- {
- return baseValue;
- }
- return default;
- }
- public bool TryGetInheritedValue(
- AvaloniaProperty property,
- [NotNullWhen(true)] out EffectiveValue? result)
- {
- Debug.Assert(property.Inherits);
- var i = InheritanceAncestor;
- while (i is not null)
- {
- if (i.TryGetEffectiveValue(property, out result))
- return true;
- i = i.InheritanceAncestor;
- }
- result = null;
- return false;
- }
- public void SetInheritanceParent(AvaloniaObject? oldParent, AvaloniaObject? newParent)
- {
- var values = AvaloniaPropertyDictionaryPool<OldNewValue>.Get();
- var oldAncestor = InheritanceAncestor;
- var newAncestor = newParent?.GetValueStore();
- if (newAncestor?._inheritedValueCount == 0)
- newAncestor = newAncestor.InheritanceAncestor;
- // The old and new inheritance ancestors are the same, nothing to do here.
- if (oldAncestor == newAncestor)
- return;
- // First get the old values from the old inheritance ancestor.
- var f = oldAncestor;
- while (f is not null)
- {
- var count = f._effectiveValues.Count;
- for (var i = 0; i < count; ++i)
- {
- f._effectiveValues.GetKeyValue(i, out var key, out var value);
- if (key.Inherits)
- values.TryAdd(key, new(value));
- }
- f = f.InheritanceAncestor;
- }
- f = newAncestor;
- // Get the new values from the new inheritance ancestor.
- while (f is not null)
- {
- var count = f._effectiveValues.Count;
- for (var i = 0; i < count; ++i)
- {
- f._effectiveValues.GetKeyValue(i, out var key, out var value);
- if (!key.Inherits)
- continue;
- if (values.TryGetValue(key, out var existing))
- {
- if (existing.NewValue is null)
- values[key] = existing.WithNewValue(value);
- }
- else
- {
- values.Add(key, new(null, value));
- }
- }
- f = f.InheritanceAncestor;
- }
- OnInheritanceAncestorChanged(newAncestor);
- // Raise PropertyChanged events where necessary on this object and inheritance children.
- {
- 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(key, oldValue, newValue);
- }
- }
- AvaloniaPropertyDictionaryPool<OldNewValue>.Release(values);
- }
- /// <summary>
- /// Called by non-LocalValue binding entries to re-evaluate the effective value when the
- /// binding produces a new value.
- /// </summary>
- /// <param name="entry">The binding entry.</param>
- /// <param name="priority">The priority of binding which produced a new value.</param>
- public void OnBindingValueChanged(
- IValueEntry entry,
- BindingPriority priority)
- {
- Debug.Assert(priority != BindingPriority.LocalValue);
- var property = entry.Property;
- if (TryGetEffectiveValue(property, out var existing))
- {
- if (priority <= existing.Priority)
- ReevaluateEffectiveValue(property, existing);
- }
- else
- {
- AddEffectiveValueAndRaise(property, entry, priority);
- }
- }
- /// <summary>
- /// Called by non-LocalValue binding entries to re-evaluate the effective value when the
- /// binding produces an unset value.
- /// </summary>
- /// <param name="property">The bound property.</param>
- /// <param name="priority">The priority of binding which produced a new value.</param>
- public void OnBindingValueCleared(AvaloniaProperty property, BindingPriority priority)
- {
- Debug.Assert(priority != BindingPriority.LocalValue);
- if (TryGetEffectiveValue(property, out var existing))
- {
- if (priority <= existing.Priority)
- ReevaluateEffectiveValue(property, existing);
- }
- }
- /// <summary>
- /// Called by a <see cref="BindingEntryBase{TValue, TSource}"/> to re-evaluate the
- /// effective value when the binding completes or terminates on error.
- /// </summary>
- /// <param name="property">The previously bound property.</param>
- /// <param name="frame">The frame which contained the binding.</param>
- public void OnBindingCompleted(AvaloniaProperty property, ValueFrame frame)
- {
- var priority = frame.Priority;
- if (TryGetEffectiveValue(property, out var existing))
- {
- if (priority <= existing.Priority)
- ReevaluateEffectiveValue(property, existing);
- }
- }
- /// <summary>
- /// Called by a <see cref="ValueFrame"/> when its <see cref="ValueFrame.IsActive"/>
- /// state changes.
- /// </summary>
- /// <param name="frame">The frame which produced the change.</param>
- public void OnFrameActivationChanged(ValueFrame frame)
- {
- if (frame.EntryCount == 0)
- return;
- else if (frame.EntryCount == 1)
- {
- var property = frame.GetEntry(0).Property;
- _effectiveValues.TryGetValue(property, out var current);
- ReevaluateEffectiveValue(property, current);
- }
- else
- ReevaluateEffectiveValues();
- }
- /// <summary>
- /// Called by the parent value store when its inheritance ancestor changes.
- /// </summary>
- /// <param name="ancestor">The new inheritance ancestor.</param>
- public void OnInheritanceAncestorChanged(ValueStore? ancestor)
- {
- if (ancestor != this)
- {
- InheritanceAncestor = ancestor;
- if (_inheritedValueCount > 0)
- return;
- }
- var children = Owner.GetInheritanceChildren();
- if (children is null)
- return;
- var count = children.Count;
- for (var i = 0; i < count; ++i)
- {
- children[i].GetValueStore().OnInheritanceAncestorChanged(ancestor);
- }
- }
- /// <summary>
- /// Called by <see cref="EffectiveValue{T}"/> when an property with inheritance enabled
- /// changes its value on this value store.
- /// </summary>
- /// <param name="property">The property whose value changed.</param>
- /// <param name="oldValue">The old value of the property.</param>
- /// <param name="value">The effective value instance.</param>
- public void OnInheritedEffectiveValueChanged<T>(
- StyledPropertyBase<T> property,
- T oldValue,
- EffectiveValue<T> value)
- {
- Debug.Assert(property.Inherits);
- var children = Owner.GetInheritanceChildren();
- if (children is null)
- return;
- var count = children.Count;
- for (var i = 0; i < count; ++i)
- {
- children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, value.Value);
- }
- }
- /// <summary>
- /// Called by <see cref="EffectiveValue{T}"/> when an property with inheritance enabled
- /// is removed from the effective values.
- /// </summary>
- /// <param name="property">The property whose value changed.</param>
- /// <param name="oldValue">The old value of the property.</param>
- public void OnInheritedEffectiveValueDisposed<T>(StyledPropertyBase<T> property, T oldValue)
- {
- Debug.Assert(property.Inherits);
- var children = Owner.GetInheritanceChildren();
- if (children is not null)
- {
- var defaultValue = property.GetDefaultValue(Owner.GetType());
- var count = children.Count;
- for (var i = 0; i < count; ++i)
- {
- children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, defaultValue);
- }
- }
- }
- /// <summary>
- /// Called when a <see cref="LocalValueBindingObserver{T}"/> or
- /// <see cref="DirectBindingObserver{T}"/> completes.
- /// </summary>
- /// <param name="property">The previously bound property.</param>
- /// <param name="observer">The observer.</param>
- public void OnLocalValueBindingCompleted(AvaloniaProperty property, IDisposable observer)
- {
- if (_localValueBindings is not null &&
- _localValueBindings.TryGetValue(property.Id, out var existing))
- {
- if (existing == observer)
- {
- _localValueBindings?.Remove(property.Id);
- ClearLocalValue(property);
- }
- }
- }
- /// <summary>
- /// Called when an inherited property changes on the value store of the inheritance ancestor.
- /// </summary>
- /// <typeparam name="T">The property type.</typeparam>
- /// <param name="property">The property.</param>
- /// <param name="oldValue">The old value of the property.</param>
- /// <param name="newValue">The new value of the property.</param>
- public void OnAncestorInheritedValueChanged<T>(
- StyledPropertyBase<T> property,
- T oldValue,
- T newValue)
- {
- Debug.Assert(property.Inherits);
- // If the inherited value is set locally, propagation stops here.
- if (_effectiveValues.ContainsKey(property))
- return;
- using var notifying = PropertyNotifying.Start(Owner, property);
- Owner.RaisePropertyChanged(
- property,
- oldValue,
- newValue,
- BindingPriority.Inherited,
- true);
- var children = Owner.GetInheritanceChildren();
- if (children is null)
- return;
- var count = children.Count;
- for (var i = 0; i < count; ++i)
- {
- children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, newValue);
- }
- }
- /// <summary>
- /// Called by a <see cref="ValueFrame"/> to re-evaluate the effective value when a value
- /// is removed.
- /// </summary>
- /// <param name="frame">The frame on which the change occurred.</param>
- /// <param name="property">The property whose value was removed.</param>
- public void OnValueEntryRemoved(ValueFrame frame, AvaloniaProperty property)
- {
- Debug.Assert(frame.IsActive);
- if (TryGetEffectiveValue(property, out var existing))
- {
- if (frame.Priority <= existing.Priority)
- ReevaluateEffectiveValue(property, existing);
- }
- else
- {
- Logger.TryGet(LogEventLevel.Error, LogArea.Property)?.Log(
- Owner,
- "Internal error: ValueStore.OnEntryRemoved called for {Property} " +
- "but no effective value was found.",
- property);
- Debug.Assert(false);
- }
- }
- public bool RemoveFrame(ValueFrame frame)
- {
- if (_frames.Remove(frame))
- {
- frame.Dispose();
- ++_frameGeneration;
- ReevaluateEffectiveValues();
- }
- return false;
- }
- public AvaloniaPropertyValue GetDiagnostic(AvaloniaProperty property)
- {
- object? value;
- BindingPriority priority;
- if (_effectiveValues.TryGetValue(property, out var v))
- {
- value = v.Value;
- priority = v.Priority;
- }
- else if (property.Inherits && TryGetInheritedValue(property, out v))
- {
- value = v.Value;
- priority = BindingPriority.Inherited;
- }
- else
- {
- value = GetDefaultValue(property);
- priority = BindingPriority.Unset;
- }
- return new AvaloniaPropertyValue(
- property,
- value,
- priority,
- null);
- }
- private int InsertFrame(ValueFrame frame)
- {
- // Uncomment this line when #8549 is fixed.
- //Debug.Assert(!_frames.Contains(frame));
- var index = BinarySearchFrame(frame.Priority);
- _frames.Insert(index, frame);
- ++_frameGeneration;
- frame.SetOwner(this);
- return index;
- }
- private ImmediateValueFrame GetOrCreateImmediateValueFrame(
- AvaloniaProperty property,
- BindingPriority priority,
- out int frameIndex)
- {
- Debug.Assert(priority != BindingPriority.LocalValue);
- var index = BinarySearchFrame(priority);
- if (index > 0 && _frames[index - 1] is ImmediateValueFrame f &&
- f.Priority == priority &&
- !f.Contains(property))
- {
- frameIndex = index - 1;
- return f;
- }
- var result = new ImmediateValueFrame(priority);
- frameIndex = InsertFrame(result);
- return result;
- }
- private void AddEffectiveValue(AvaloniaProperty property, EffectiveValue effectiveValue)
- {
- _effectiveValues.Add(property, effectiveValue);
- if (property.Inherits && _inheritedValueCount++ == 0)
- OnInheritanceAncestorChanged(this);
- }
- /// <summary>
- /// Adds a new effective value, raises the initial <see cref="AvaloniaObject.PropertyChanged"/>
- /// event and notifies inheritance children if necessary .
- /// </summary>
- /// <param name="property">The property.</param>
- /// <param name="entry">The value entry.</param>
- /// <param name="priority">The value priority.</param>
- private void AddEffectiveValueAndRaise(AvaloniaProperty property, IValueEntry entry, BindingPriority priority)
- {
- Debug.Assert(priority < BindingPriority.Inherited);
- var effectiveValue = property.CreateEffectiveValue(Owner);
- AddEffectiveValue(property, effectiveValue);
- effectiveValue.SetAndRaise(this, entry, priority);
- }
- private void RemoveEffectiveValue(AvaloniaProperty property, int index)
- {
- _effectiveValues.RemoveAt(index);
- if (property.Inherits && --_inheritedValueCount == 0)
- OnInheritanceAncestorChanged(InheritanceAncestor);
- }
- private bool RemoveEffectiveValue(AvaloniaProperty property)
- {
- if (_effectiveValues.Remove(property))
- {
- if (property.Inherits && --_inheritedValueCount == 0)
- OnInheritanceAncestorChanged(InheritanceAncestor);
- return true;
- }
- return false;
- }
- private bool RemoveEffectiveValue(AvaloniaProperty property, [NotNullWhen(true)] out EffectiveValue? result)
- {
- if (_effectiveValues.Remove(property, out result))
- {
- if (property.Inherits && --_inheritedValueCount == 0)
- OnInheritanceAncestorChanged(InheritanceAncestor);
- return true;
- }
- result = null;
- return false;
- }
- private void InheritedValueChanged(
- AvaloniaProperty property,
- EffectiveValue? oldValue,
- EffectiveValue? newValue)
- {
- Debug.Assert(oldValue != newValue);
- Debug.Assert(oldValue is not null || newValue is not null);
- // If the value is set locally, propagaton ends here.
- if (_effectiveValues.ContainsKey(property) == true)
- return;
- using var notifying = PropertyNotifying.Start(Owner, property);
- // Raise PropertyChanged on this object if necessary.
- (oldValue ?? newValue!).RaiseInheritedValueChanged(Owner, property, oldValue, newValue);
- var children = Owner.GetInheritanceChildren();
- if (children is null)
- return;
- var count = children.Count;
- for (var i = 0; i < count; ++i)
- {
- children[i].GetValueStore().InheritedValueChanged(property, oldValue, newValue);
- }
- }
- private void ReevaluateEffectiveValue(
- AvaloniaProperty property,
- EffectiveValue? current,
- bool ignoreLocalValue = false)
- {
- IsEvaluating = true;
- try
- {
- restart:
- // Don't reevaluate if a styling pass is in effect, reevaluation will be done when
- // it has finished.
- if (_styling > 0)
- return;
- var generation = _frameGeneration;
- // Notify the existing effective value that reevaluation is starting.
- current?.BeginReevaluation(ignoreLocalValue);
- // Iterate the frames to get the effective value.
- for (var i = _frames.Count - 1; i >= 0; --i)
- {
- var frame = _frames[i];
- var priority = frame.Priority;
- var foundEntry = frame.TryGetEntryIfActive(property, out var entry, out var activeChanged);
-
- // If the active state of the frame has changed since the last read, and
- // the frame holds multiple values then we need to re-evaluate the
- // effective values of all properties.
- if (activeChanged && frame.EntryCount > 1)
- {
- ReevaluateEffectiveValues();
- return;
- }
- // We're interested in the value if:
- // - There is no current effective value, or
- // - The value's priority is higher than the current effective value's priority, or
- // - The value is a non-animation value and its priority is higher than the current
- // effective value's base priority
- var isRelevantPriority = current is null ||
- priority < current.Priority ||
- (priority > BindingPriority.Animation && priority < current.BasePriority);
- if (foundEntry && isRelevantPriority && entry!.HasValue)
- {
- if (current is not null)
- {
- current.SetAndRaise(this, entry, priority);
- }
- else
- {
- current = property.CreateEffectiveValue(Owner);
- AddEffectiveValue(property, current);
- current.SetAndRaise(this, entry, priority);
- }
- }
- if (generation != _frameGeneration)
- goto restart;
- if (current?.Priority < BindingPriority.Unset &&
- current?.BasePriority < BindingPriority.Unset)
- break;
- }
- current?.EndReevaluation();
- if (current?.Priority == BindingPriority.Unset)
- {
- if (current.BasePriority == BindingPriority.Unset)
- {
- RemoveEffectiveValue(property);
- current.DisposeAndRaiseUnset(this, property);
- }
- else
- {
- current.RemoveAnimationAndRaise(this, property);
- }
- }
- }
- finally
- {
- IsEvaluating = false;
- }
- }
- private void ReevaluateEffectiveValues()
- {
- IsEvaluating = true;
- try
- {
- restart:
- // Don't reevaluate if a styling pass is in effect, reevaluation will be done when
- // it has finished.
- if (_styling > 0)
- return;
- var generation = _frameGeneration;
- var count = _effectiveValues.Count;
- // Notify the existing effective values that reevaluation is starting.
- for (var i = 0; i < count; ++i)
- _effectiveValues[i].BeginReevaluation();
- // Iterate the frames, setting and creating effective values.
- for (var i = _frames.Count - 1; i >= 0; --i)
- {
- var frame = _frames[i];
- if (!frame.IsActive)
- continue;
- var priority = frame.Priority;
- count = frame.EntryCount;
- for (var j = 0; j < count; ++j)
- {
- var entry = frame.GetEntry(j);
- var property = entry.Property;
- // Skip if we already have a value/base value for this property.
- if (_effectiveValues.TryGetValue(property, out var effectiveValue) &&
- effectiveValue.BasePriority < BindingPriority.Unset)
- continue;
- if (!entry.HasValue)
- continue;
- if (effectiveValue is not null)
- {
- effectiveValue.SetAndRaise(this, entry, priority);
- }
- else
- {
- var v = property.CreateEffectiveValue(Owner);
- AddEffectiveValue(property, v);
- v.SetAndRaise(this, entry, priority);
- }
- if (generation != _frameGeneration)
- goto restart;
- }
- }
- // Remove all effective values that are still unset.
- for (var i = _effectiveValues.Count - 1; i >= 0; --i)
- {
- _effectiveValues.GetKeyValue(i, out var key, out var e);
- e.EndReevaluation();
- if (e.Priority == BindingPriority.Unset)
- {
- RemoveEffectiveValue(key, i);
- e.DisposeAndRaiseUnset(this, key);
- }
- }
- }
- finally
- {
- IsEvaluating = false;
- }
- }
- private bool TryGetEffectiveValue(
- AvaloniaProperty property,
- [NotNullWhen(true)] out EffectiveValue? value)
- {
- if (_effectiveValues.TryGetValue(property, out value))
- return true;
- value = null;
- return false;
- }
- private EffectiveValue? GetEffectiveValue(AvaloniaProperty property)
- {
- if (_effectiveValues.TryGetValue(property, out var value))
- return value;
- return null;
- }
- private object? GetDefaultValue(AvaloniaProperty property)
- {
- return ((IStyledPropertyAccessor)property).GetDefaultValue(Owner.GetType());
- }
- private void DisposeExistingLocalValueBinding(AvaloniaProperty property)
- {
- if (_localValueBindings is not null &&
- _localValueBindings.TryGetValue(property.Id, out var existing))
- {
- existing.Dispose();
- }
- }
- private int BinarySearchFrame(BindingPriority priority)
- {
- var lo = 0;
- var hi = _frames.Count - 1;
- // Binary search insertion point.
- while (lo <= hi)
- {
- var i = lo + ((hi - lo) >> 1);
- var order = priority - _frames[i].Priority;
- if (order <= 0)
- {
- lo = i + 1;
- }
- else
- {
- hi = i - 1;
- }
- }
- return lo;
- }
- private readonly struct OldNewValue
- {
- public OldNewValue(EffectiveValue? oldValue)
- {
- OldValue = oldValue;
- NewValue = null;
- }
- public OldNewValue(EffectiveValue? oldValue, EffectiveValue? newValue)
- {
- OldValue = oldValue;
- NewValue = newValue;
- }
- public readonly EffectiveValue? OldValue;
- public readonly EffectiveValue? NewValue;
- public OldNewValue WithNewValue(EffectiveValue newValue) => new(OldValue, newValue);
- }
- }
- }
|