diff --git a/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs b/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs index a9dfc3a84..becbc538e 100644 --- a/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs +++ b/src/Artemis.Core.Tests/Conditions/EventConditionTests.cs @@ -79,16 +79,24 @@ public class EventConditionTests : RenderTestBase } [Fact] - public void Play_EventTriggeredBeforeTheConditionExisted_StillPlaysTheLayer() + public void Play_EventTriggeredBeforeTheConditionExisted_DoesNotPlayTheLayer() { DataModel.Event.Trigger(); (Layer layer, _) = AddLayer(); using RenderFrame frame = Render(layer.Profile); - // Pinned as-is: the condition starts out having seen no triggers at all, so a trigger from before it was created - // (for example long before the profile was activated) still counts as new and plays the layer once. Looks like - // the condition should only react to triggers that happen after it started watching. + ShouldBeOff(frame); + } + + [Fact] + public void Play_EventTriggeredAfterTheConditionExisted_PlaysTheLayer_EvenBeforeTheFirstFrame() + { + (Layer layer, _) = AddLayer(); + DataModel.Event.Trigger(); + + using RenderFrame frame = Render(layer.Profile); + ShouldBeOn(frame); } @@ -416,15 +424,14 @@ public class EventConditionTests : RenderTestBase } [Fact] - public void PathToAValue_PlaysOnTheFirstFrame() + public void PathToAValue_DoesNotPlayOnTheFirstFrame() { (Layer layer, _) = AddLayer(eventPath: "Integer"); using RenderFrame frame = Render(layer.Profile); - // Pinned as-is: the last seen value starts out empty, so the first frame always sees the value as changed (0 is - // not empty) and plays the layer even though nothing changed. - ShouldBeOn(frame); + // The first frame only learns the starting value, nothing changed yet + ShouldBeOff(frame); } [Fact] diff --git a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs index bbe50bdc1..acf68a02d 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/EventCondition.cs @@ -17,8 +17,10 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition private NodeScript _script; private DefaultNode _startNode; private DataModelPath? _eventPath; - private DateTime _lastProcessedTrigger; + // Only what happens after the condition starts watching its path counts, see StartWatching + private DateTime _lastProcessedTrigger = DateTime.Now; private object? _lastProcessedValue; + private bool _hasProcessedValue; private EventOverlapMode _overlapMode; private EventToggleOffMode _toggleOffMode; private EventTriggerMode _triggerMode; @@ -64,7 +66,11 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition public DataModelPath? EventPath { get => _eventPath; - set => SetAndNotify(ref _eventPath, value); + set + { + if (SetAndNotify(ref _eventPath, value)) + StartWatching(); + } } /// @@ -243,6 +249,15 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition Script.AddNode(newStartNode); } + private void StartWatching() + { + // Triggers and values from before the condition watched the path don't count, otherwise for example an event that + // triggered long before the profile was activated would still play the element once + _lastProcessedTrigger = DateTime.Now; + _lastProcessedValue = null; + _hasProcessedValue = false; + } + private bool Evaluate() { if (EventPath == null) @@ -258,6 +273,14 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition } else if (_startNode is EventConditionValueChangedStartNode valueChangedNode) { + // The first value seen is where the value starts, not a change + if (!_hasProcessedValue) + { + _lastProcessedValue = value; + _hasProcessedValue = true; + return false; + } + if (Equals(value, _lastProcessedValue)) return false;