diff --git a/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs b/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs index 8bdcec2e53..8fe0bb2537 100644 --- a/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs +++ b/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs @@ -1,6 +1,4 @@ -#nullable enable -using System; -using Avalonia.LogicalTree; +using Avalonia.LogicalTree; namespace Avalonia.Styling.Activators { @@ -14,7 +12,7 @@ namespace Avalonia.Styling.Activators private readonly int _step; private readonly int _offset; private readonly bool _reversed; - private int _index = -1; + private int? _index; public NthChildActivator( ILogical control, @@ -30,7 +28,7 @@ namespace Avalonia.Styling.Activators protected override bool EvaluateIsActive() { - var index = _index >= 0 ? _index : _provider.GetChildIndex(_control); + var index = _index ?? _provider.GetChildIndex(_control); return NthChildSelector.Evaluate(index, _provider, _step, _offset, _reversed).IsMatch; } @@ -50,8 +48,7 @@ namespace Avalonia.Styling.Activators // 1. Subscribed child index was changed // 2. Child indexes were reset // 3. We're a reversed (nth-last-child) selector and total count has changed - if ((e.Child == _control || e.Action == ChildIndexChangedAction.ChildIndexesReset) || - (_reversed && e.Action == ChildIndexChangedAction.TotalCountChanged)) + switch (e.Action) { // We're using the _index field to pass the index of the child to EvaluateIsActive // *only* when the active state is re-evaluated via this event handler. The docs @@ -65,16 +62,17 @@ namespace Avalonia.Styling.Activators // IChildIndexProvider.GetChildIndex. This is because this event can be fired during // the process of realizing an element of a virtualized list; in this case calling // GetChildIndex may not return the correct index as the element isn't yet realized. - _index = e.Index; - ReevaluateIsActive(); - _index = -1; + case ChildIndexChangedAction.ChildIndexChanged when e.Child == _control: + _index = e.Index; + ReevaluateIsActive(); + _index = null; + break; + case ChildIndexChangedAction.ChildIndexesReset: + case ChildIndexChangedAction.TotalCountChanged when _reversed: + _index = null; + ReevaluateIsActive(); + break; } } - - private void TotalCountChanged(object? sender, EventArgs e) - { - if (_reversed) - ReevaluateIsActive(); - } } } diff --git a/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs b/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs index 06848a7524..3dbf927b86 100644 --- a/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs +++ b/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs @@ -6,9 +6,14 @@ namespace Avalonia.Styling.Activators internal abstract class StyleActivatorBase : IStyleActivator { private IStyleActivatorSink? _sink; - private bool _value; + private bool? _value; - public bool GetIsActive() => _value = EvaluateIsActive(); + public bool GetIsActive() + { + var value = EvaluateIsActive(); + _value ??= value; + return value; + } public bool IsSubscribed => _sink is not null; @@ -63,7 +68,7 @@ namespace Avalonia.Styling.Activators /// protected bool ReevaluateIsActive() { - var value = EvaluateIsActive(); + var value = GetIsActive(); if (value != _value) { diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs index ca6d1e5326..52bee42d9d 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs @@ -5,7 +5,7 @@ using Avalonia.Base.UnitTests.Animation; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; -using Avalonia.PropertyStore; +using Avalonia.Media; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -963,6 +963,72 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Equal(0.0, target.Double); } + [Fact] + public void Animations_With_Activator_Trigger_Should_Be_Activated_And_Deactivated() + { + var clock = new TestClock(); + var border = new Border(); + + var root = new TestRoot + { + Clock = clock, + Styles = + { + new Style(x => x.OfType().Not(default(Selector).Class("foo"))) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Yellow), + }, + Animations = + { + new Avalonia.Animation.Animation + { + Duration = TimeSpan.FromSeconds(1.0), + Children = + { + new KeyFrame + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Green) + }, + Cue = new Cue(0.0) + }, + new KeyFrame + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Green) + }, + Cue = new Cue(1.0) + } + } + } + } + }, + new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Blue), + } + } + }, + Child = border + }; + + root.Measure(Size.Infinity); + + Assert.Equal(Brushes.Yellow, border.Background); + + clock.Step(TimeSpan.FromSeconds(0.5)); + Assert.Equal(Brushes.Green, border.Background); + + border.Classes.Add("foo"); + Assert.Equal(Brushes.Blue, border.Background); + } + private class Class1 : Control { public static readonly StyledProperty FooProperty =