From 2e63c2ce20ef4c707150f2eaf3c5a640a9c963c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= <dach.bartlomiej@gmail.com>
Date: Wed, 17 Mar 2021 21:50:45 +0100
Subject: [PATCH] Fix selection box operation hotkeys not registering in change
 handler

Could lead to crashes after reversing a note cluster and playing it
back.

The root cause of the crash was that the hotkey operations were not ran
inside of an editor change handler operation. This, in turn, caused the
autoplay replay to not be regenerated after flipping an object cluster,
therefore finally manifesting as a hard crash due to negative time
offsets appearing in judgement results, which interfered with the
default implementation of note lock.

Note that this incidentally also fixes the fact that selection box
hotkey operations (reverse and flip) did not handle undo/redo.
---
 .../Edit/Compose/Components/SelectionBox.cs       | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs
index 2f4721f63e..9d6b44e207 100644
--- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs
@@ -113,16 +113,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
             if (e.Repeat || !e.ControlPressed)
                 return false;
 
+            bool runOperationFromHotkey(Func<bool> operation)
+            {
+                operationStarted();
+                bool result = operation?.Invoke() ?? false;
+                operationEnded();
+
+                return result;
+            }
+
             switch (e.Key)
             {
                 case Key.G:
-                    return CanReverse && OnReverse?.Invoke() == true;
+                    return CanReverse && runOperationFromHotkey(OnReverse);
 
                 case Key.H:
-                    return CanScaleX && OnFlip?.Invoke(Direction.Horizontal) == true;
+                    return CanScaleX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false);
 
                 case Key.J:
-                    return CanScaleY && OnFlip?.Invoke(Direction.Vertical) == true;
+                    return CanScaleY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false);
             }
 
             return base.OnKeyDown(e);