Browse Source

Ensure a changing value overrides current value.

When a binding produces a new value, ensure that it overwrites any value in place from `SetCurrentValue` if the priority is the same.
Steven Kirk 2 years ago
parent
commit
9c936255be
1 changed files with 39 additions and 18 deletions
  1. 39 18
      src/Avalonia.Base/PropertyStore/ValueStore.cs

+ 39 - 18
src/Avalonia.Base/PropertyStore/ValueStore.cs

@@ -395,7 +395,7 @@ namespace Avalonia.PropertyStore
             if (TryGetEffectiveValue(property, out var existing))
             {
                 if (priority <= existing.BasePriority)
-                    ReevaluateEffectiveValue(property, existing);
+                    ReevaluateEffectiveValue(property, existing, changedValueEntry: entry);
             }
             else
             {
@@ -774,6 +774,7 @@ namespace Avalonia.PropertyStore
         private void ReevaluateEffectiveValue(
             AvaloniaProperty property,
             EffectiveValue? current,
+            IValueEntry? changedValueEntry = null,
             bool ignoreLocalValue = false)
         {
             ++_isEvaluating;
@@ -803,21 +804,11 @@ namespace Avalonia.PropertyStore
                     // effective values of all properties.
                     if (activeChanged && frame.EntryCount > 1)
                     {
-                        ReevaluateEffectiveValues();
+                        ReevaluateEffectiveValues(changedValueEntry);
                         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's priority is equal to the current effective value's priority, but the effective
-                    //   value was set via SetCurrentValue, 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 < current.BasePriority) ||
-                        (priority == current.Priority && current.IsOverridenCurrentValue) ||
-                        (priority > BindingPriority.Animation && priority < current.BasePriority);
+                    var isRelevantPriority = HasHigherPriority(entry!, priority, current, changedValueEntry);
 
                     if (foundEntry && isRelevantPriority && entry!.HasValue)
                     {
@@ -862,7 +853,7 @@ namespace Avalonia.PropertyStore
             }
         }
 
-        private void ReevaluateEffectiveValues()
+        private void ReevaluateEffectiveValues(IValueEntry? changedValueEntry = null)
         {
             ++_isEvaluating;
 
@@ -897,10 +888,9 @@ namespace Avalonia.PropertyStore
                     {
                         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)
+                        _effectiveValues.TryGetValue(property, out var effectiveValue);
+                        
+                        if (!HasHigherPriority(entry, priority, effectiveValue, changedValueEntry))
                             continue;
 
                         if (!entry.HasValue)
@@ -945,6 +935,37 @@ namespace Avalonia.PropertyStore
             }
         }
 
+        private static bool HasHigherPriority(
+            IValueEntry entry,
+            BindingPriority entryPriority,
+            EffectiveValue? current,
+            IValueEntry? changedValueEntry)
+        {
+            // Set the value if: there is no current effective value; or
+            if (current is null)
+                return true; 
+
+            // The value's priority is higher than the current effective value's priority; or
+            if (entryPriority < current.Priority && entryPriority < current.BasePriority)
+                return true;
+
+            // - The value's priority is equal to the current effective value's priority
+            // - But the effective value was set via SetCurrentValue
+            // - As long as the SetCurrentValue wasn't overriding the value from the value entry under consideration
+            // - Or if it was, the value entry under consideration has changed; or
+            if (entryPriority == current.Priority &&
+                current.IsOverridenCurrentValue &&
+                (current.ValueEntry != entry || entry == changedValueEntry))
+                return true;
+
+            // The value is a non-animation value and its priority is higher than the current effective value's base
+            // priority.
+            if (entryPriority > BindingPriority.Animation && entryPriority < current.BasePriority)
+                return true;
+
+            return false;
+        }
+
         private bool TryGetEffectiveValue(
             AvaloniaProperty property, 
             [NotNullWhen(true)] out EffectiveValue? value)