Profile editor - Fix undoing profile editor commands

- Undoing a change to an existing keyframe's value restores the value instead of deleting the keyframe
- Undoing "remove effect" puts the effect back in its place instead of below the next one
- Undoing "toggle keyframes" restores what it was before
- Undoing a brush change on a layer that had no brush removes the brush again, using the new Layer.RemoveLayerBrush
- Undoing "add element" disables the element again
- Undoing an event path change back to no path goes back to an event start node, like a new condition
- MoveProfileElement says "Move folder" for folders
- UpdateLayerProperty without a time no longer calls itself "Add ... keyframe"
This commit is contained in:
RobertBeekman committed 2026-10-20 09:47:43 +02:00
1 parent 504845c7d2
commit 0920cf8917
14 files changed
+80 -71

No files matched your search

@@ -180,7 +180,7 @@ public class ConditionCommandTests : ProfileEditorTestBase
}
[Fact]
public void UpdateEventConditionPath_Undo_ToNoPath_KeepsTheNewStartNode()
public void UpdateEventConditionPath_Undo_ToNoPath_GoesBackToAnEventStartNode()
{
TestModule module = AddTestModule();
EventCondition condition = new(_layer);
@@ -191,11 +191,11 @@ public class ConditionCommandTests : ProfileEditorTestBase
command.Execute();
command.Undo();
// Pinned as-is: UpdateEventNode does nothing without a path, so undoing back to "no path" keeps the start node
// made for the path that was undone
// Like a new condition, rather than keeping the start node made for the path that was undone
condition.EventPath.ShouldBeNull();
condition.GetStartNode().ShouldNotBe(originalStartNode);
condition.GetStartNode().GetType().Name.ShouldBe("EventConditionValueChangedStartNode");
condition.GetStartNode().GetType().ShouldBe(originalStartNode.GetType());
condition.Script.Nodes.ShouldContain(condition.GetStartNode());
condition.Script.Nodes.ShouldNotContain(n => n.GetType().Name == "EventConditionValueChangedStartNode");
}
[Fact]
@@ -219,15 +219,15 @@ public class KeyframeCommandTests : ProfileEditorTestBase
}
[Fact]
public void ToggleLayerPropertyKeyframes_Undo_DoesNotRememberWhatItWasBefore()
public void ToggleLayerPropertyKeyframes_Undo_RestoresWhatItWasBefore()
{
ToggleLayerPropertyKeyframes<float> enable = new(_opacity, true, TimeSpan.Zero);
enable.Execute();
enable.Undo();
// Pinned as-is: undo sets the opposite of what the command did rather than restoring the earlier state, so
// "enabling" keyframes that were already enabled turns them off when undone
_opacity.KeyframesEnabled.ShouldBeFalse();
// "Enabling" keyframes that were already enabled leaves them enabled when undone
_opacity.KeyframesEnabled.ShouldBeTrue();
_opacity.Keyframes.ShouldBe([_first, _second]);
}
}
@@ -67,7 +67,7 @@ public class LayerBrushAndLedCommandTests : ProfileEditorTestBase
}
[Fact]
public void ChangeLayerBrush_Undo_OnALayerWithoutBrush_KeepsTheNewBrush()
public void ChangeLayerBrush_Undo_OnALayerWithoutBrush_RemovesTheNewBrush()
{
Layer layer = new(Root, "Layer without brush");
Root.AddChild(layer);
@@ -76,8 +76,8 @@ public class LayerBrushAndLedCommandTests : ProfileEditorTestBase
command.Execute();
command.Undo();
// Pinned as-is: with no brush to go back to, undo leaves the new brush (and the brush reference) in place
layer.LayerBrush.ShouldBeOfType<GradientTestBrush>();
layer.LayerBrush.ShouldBeNull();
layer.General.BrushReference.CurrentValue.ShouldBeNull();
}
[Fact]
@@ -111,7 +111,7 @@ public class LayerEffectCommandTests : ProfileEditorTestBase
[Theory]
[InlineData(0)]
[InlineData(1)]
public void RemoveLayerEffect_Undo_OfAnEffectAboveAnother_MovesItDown(int index)
public void RemoveLayerEffect_Undo_OfAnEffectAboveAnother_PutsItBackInItsPlace(int index)
{
Harness.AddEffect<TintTestEffect>(_layer);
Harness.AddEffect<OpacityTestEffect>(_layer);
@@ -124,13 +124,10 @@ public class LayerEffectCommandTests : ProfileEditorTestBase
command.Execute();
command.Undo();
// Pinned as-is: removing renumbers the remaining effects, so the effect below takes over the removed effect's
// order. Adding it back sorts it after the effect that now has the same order, swapping the two. Since effects
// apply in order, undo can change what the layer looks like
effects.RemoveAt(index);
effects.Insert(index + 1, removed);
// Effects apply in order, so this keeps the layer looking the same
_layer.LayerEffects.ShouldBe(effects);
Snapshot().ShouldNotBe(before);
_layer.LayerEffects.Select(e => e.Order).ShouldBe([1, 2, 3]);
Snapshot().ShouldBe(before);
}
[Fact]
@@ -114,7 +114,7 @@ public class LayerPropertyCommandTests : ProfileEditorTestBase
}
[Fact]
public void UpdateLayerProperty_WithOriginalValue_OnAKeyframe_RemovesTheKeyframeOnUndo()
public void UpdateLayerProperty_WithOriginalValue_OnAKeyframe_RestoresTheKeyframeOnUndo()
{
AddKeyframe(0, 0);
LayerPropertyKeyframe<float> keyframe = AddKeyframe(60, 1);
@@ -124,12 +124,10 @@ public class LayerPropertyCommandTests : ProfileEditorTestBase
keyframe.Value.ShouldBe(40);
command.Undo();
// Pinned as-is: this constructor never checks whether there already is a keyframe at the time. When keyframes are
// enabled, Execute treats the existing keyframe that SetCurrentValue updated as a new one, and Undo removes it
// instead of restoring its value. Changing a keyframe through a property input and undoing deletes the keyframe
_opacity.Keyframes.ShouldNotContain(keyframe);
_opacity.Keyframes.Count.ShouldBe(1);
command.DisplayName.ShouldBe("Add Opacity keyframe");
_opacity.Keyframes.ShouldContain(keyframe);
_opacity.Keyframes.Count.ShouldBe(2);
keyframe.Value.ShouldBe(60);
command.DisplayName.ShouldBe("Update Opacity");
}
[Fact]
@@ -142,9 +140,7 @@ public class LayerPropertyCommandTests : ProfileEditorTestBase
_opacity.BaseValue.ShouldBe(40);
_opacity.Keyframes.Count.ShouldBe(1);
// Pinned as-is: with keyframes enabled the command expects to add a keyframe and renames itself, even though
// without a time no keyframe is added
command.DisplayName.ShouldBe("Add Opacity keyframe");
command.DisplayName.ShouldBe("Update Opacity");
command.Undo();
_opacity.BaseValue.ShouldBe(0, 0.01f);
@@ -53,7 +53,7 @@ public class ProfileElementCommandTests : ProfileEditorTestBase
}
[Fact]
public void AddProfileElement_Undo_LeavesTheElementEnabled()
public void AddProfileElement_Undo_DisablesTheElement()
{
Layer layer = new(_folder, "New layer");
AddProfileElement command = new(layer, _folder, 0);
@@ -61,9 +61,8 @@ public class ProfileElementCommandTests : ProfileEditorTestBase
command.Execute();
command.Undo();
// Pinned as-is: Execute enables the element but Undo does not disable it again. It is no longer in the profile
// so it doesn't render, but its brush and effects stay enabled (unlike RemoveProfileElement, which disables)
layer.Enabled.ShouldBeTrue();
// Like RemoveProfileElement, so its brush and effects don't stay enabled
layer.Enabled.ShouldBeFalse();
}
[Fact]
@@ -212,12 +211,11 @@ public class ProfileElementCommandTests : ProfileEditorTestBase
}
[Fact]
public void MoveProfileElement_DisplayName_AlwaysSaysLayer()
public void MoveProfileElement_DisplayName_SaysWhatIsMoved()
{
Folder other = Harness.AddFolder(Root, "Other folder");
// Pinned as-is: the constructor sets "Move folder" for folders and then always overwrites it with "Move layer"
new MoveProfileElement(_folder, other, 0).DisplayName.ShouldBe("Move layer");
new MoveProfileElement(_folder, other, 0).DisplayName.ShouldBe("Move folder");
new MoveProfileElement(Root, _first, 0).DisplayName.ShouldBe("Move layer");
}