diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index e6126f7973..909283cbe5 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -644,37 +644,6 @@ namespace Avalonia.PropertyStore return result; } - private void ReevaluateEffectiveValue( - AvaloniaProperty property, - EffectiveValue? current, - bool ignoreLocalValue = false) - { - if (EvaluateEffectiveValue( - property, - !ignoreLocalValue ? current : null, - out var value, - out var priority, - out var baseValue, - out var basePriority)) - { - if (current is null) - { - current = property.CreateEffectiveValue(Owner); - AddEffectiveValue(property, current); - } - - if (basePriority != BindingPriority.Unset) - current.SetAndRaise(this, property, value, priority, baseValue, basePriority); - else - current.SetAndRaise(this, property, value, priority); - } - else if (current is not null) - { - RemoveEffectiveValue(property); - current.DisposeAndRaiseUnset(this, property); - } - } - private void AddEffectiveValue(AvaloniaProperty property, EffectiveValue effectiveValue) { _effectiveValues.Add(property, effectiveValue); @@ -740,102 +709,6 @@ namespace Avalonia.PropertyStore return false; } - /// - /// Evaluates the current value and base value for a property based on the current frames and optionally - /// local values. Does not evaluate inherited values. - /// - /// The property to evaluation - /// The current effective value if the local value is to be considered. - /// When the method exits will contain the current value if it exists. - /// When the method exits will contain the current value priority. - /// >When the method exits will contain the current base value if it exists. - /// When the method exits will contain the current base value priority. - /// - /// True if a value was found, otherwise false. - /// - private bool EvaluateEffectiveValue( - AvaloniaProperty property, - EffectiveValue? current, - out object? value, - out BindingPriority priority, - out object? baseValue, - out BindingPriority basePriority) - { - var i = _frames.Count - 1; - - value = baseValue = AvaloniaProperty.UnsetValue; - priority = basePriority = BindingPriority.Unset; - - // First try to find an animation value. - for (; i >= 0; --i) - { - var frame = _frames[i]; - - if (frame.Priority > BindingPriority.Animation) - break; - - if (frame.IsActive && - frame.TryGetEntry(property, out var entry) && - entry.TryGetValue(out value)) - { - priority = frame.Priority; - --i; - break; - } - } - - // Local values come from the current EffectiveValue. - if (current?.Priority == BindingPriority.LocalValue) - { - // If there's a current effective local value and no animated value then we use the - // effective local value. - if (priority == BindingPriority.Unset) - { - value = current.Value; - priority = BindingPriority.LocalValue; - } - - // The local value is always the base value. - baseValue = current.Value; - basePriority = BindingPriority.LocalValue; - return true; - } - - // Or the current effective base value if there's no longer an animated value. - if (priority == BindingPriority.Unset && current?.BasePriority == BindingPriority.LocalValue) - { - value = baseValue = current.BaseValue; - priority = basePriority = BindingPriority.LocalValue; - return true; - } - - // Now try the rest of the frames. - for (; i >= 0; --i) - { - var frame = _frames[i]; - - if (frame.Priority <= BindingPriority.LocalValue) - continue; - - if (frame.IsActive && - frame.TryGetEntry(property, out var entry) && - entry.TryGetValue(out var v)) - { - if (priority == BindingPriority.Unset) - { - value = v; - priority = frame.Priority; - } - - baseValue = v; - basePriority = frame.Priority; - return true; - } - } - - return priority != BindingPriority.Unset; - } - private void InheritedValueChanged( AvaloniaProperty property, EffectiveValue? oldValue, @@ -866,6 +739,74 @@ namespace Avalonia.PropertyStore } } + private void ReevaluateEffectiveValue( + AvaloniaProperty property, + EffectiveValue? current, + bool ignoreLocalValue = false) + { + 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; + + // Reset all non-LocalValue effective value to Unset priority. + if (current is not null) + { + if (ignoreLocalValue || current.Priority != BindingPriority.LocalValue) + current.SetPriority(BindingPriority.Unset); + if (ignoreLocalValue || current.BasePriority != BindingPriority.LocalValue) + current.SetBasePriority(BindingPriority.Unset); + } + + // Iterate the frames to get the effective value. + for (var i = _frames.Count - 1; i >= 0; --i) + { + var frame = _frames[i]; + + if (!frame.IsActive) + continue; + + var priority = frame.Priority; + + if (frame.TryGetEntry(property, out var entry) && 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 (current is not null && + current.Priority < BindingPriority.Unset && + current.BasePriority < BindingPriority.Unset) + return; + if (generation != _frameGeneration) + goto restart; + } + + if (current?.Priority == BindingPriority.Unset) + { + if (current.BasePriority == BindingPriority.Unset) + { + RemoveEffectiveValue(property); + current.DisposeAndRaiseUnset(this, property); + } + else + { + current.SetAndRaise(this, property, current.BaseValue, current.BasePriority); + } + } + } + private void ReevaluateEffectiveValues() { restart: