From 7ddbf4eaa7bfb01667bf9f6bbf16048e0499546f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Jun 2023 17:01:06 +0900 Subject: [PATCH 1/7] Add a visual effect when keyboard shortcuts are used to trigger selection box buttons --- .../Edit/Compose/Components/SelectionBox.cs | 19 +++++++------------ .../Compose/Components/SelectionBoxButton.cs | 8 ++++---- .../Compose/Components/SelectionBoxControl.cs | 8 ++++---- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 1c5faed0e5..50eb3a186d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -32,6 +32,8 @@ namespace osu.Game.Screens.Edit.Compose.Components public Action OperationStarted; public Action OperationEnded; + private SelectionBoxButton reverseButton; + private bool canReverse; /// @@ -166,19 +168,10 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Repeat || !e.ControlPressed) return false; - bool runOperationFromHotkey(Func operation) - { - operationStarted(); - bool result = operation?.Invoke() ?? false; - operationEnded(); - - return result; - } - switch (e.Key) { case Key.G: - return CanReverse && runOperationFromHotkey(OnReverse); + return reverseButton?.TriggerClick() ?? false; } return base.OnKeyDown(e); @@ -256,7 +249,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (CanFlipX) addXFlipComponents(); if (CanFlipY) addYFlipComponents(); if (CanRotate) addRotationComponents(); - if (CanReverse) addButton(FontAwesome.Solid.Backward, "Reverse pattern (Ctrl-G)", () => OnReverse?.Invoke()); + if (CanReverse) reverseButton = addButton(FontAwesome.Solid.Backward, "Reverse pattern (Ctrl-G)", () => OnReverse?.Invoke()); } private void addRotationComponents() @@ -300,7 +293,7 @@ namespace osu.Game.Screens.Edit.Compose.Components addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically", () => OnFlip?.Invoke(Direction.Vertical, false)); } - private void addButton(IconUsage icon, string tooltip, Action action) + private SelectionBoxButton addButton(IconUsage icon, string tooltip, Action action) { var button = new SelectionBoxButton(icon, tooltip) { @@ -310,6 +303,8 @@ namespace osu.Game.Screens.Edit.Compose.Components button.OperationStarted += operationStarted; button.OperationEnded += operationEnded; buttons.Add(button); + + return button; } private void addScaleHandle(Anchor anchor) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs index 832d8b65e5..6108d44c81 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -17,11 +15,11 @@ namespace osu.Game.Screens.Edit.Compose.Components { public sealed partial class SelectionBoxButton : SelectionBoxControl, IHasTooltip { - private SpriteIcon icon; + private SpriteIcon icon = null!; private readonly IconUsage iconUsage; - public Action Action; + public Action? Action; public SelectionBoxButton(IconUsage iconUsage, string tooltip) { @@ -49,6 +47,8 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnClick(ClickEvent e) { + Circle.FlashColour(Colours.GrayF, 300); + TriggerOperationStarted(); Action?.Invoke(); TriggerOperationEnded(); diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 35c67a1c67..3746c9652e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit.Compose.Components public event Action OperationStarted; public event Action OperationEnded; - private Circle circle; + protected Circle Circle { get; private set; } /// /// Whether the user is currently holding the control with mouse. @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Edit.Compose.Components InternalChildren = new Drawable[] { - circle = new Circle + Circle = new Circle { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -85,9 +85,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected virtual void UpdateHoverState() { if (IsHeld) - circle.FadeColour(Colours.GrayF, TRANSFORM_DURATION, Easing.OutQuint); + Circle.FadeColour(Colours.GrayF, TRANSFORM_DURATION, Easing.OutQuint); else - circle.FadeColour(IsHovered ? Colours.Red : Colours.YellowDark, TRANSFORM_DURATION, Easing.OutQuint); + Circle.FadeColour(IsHovered ? Colours.Red : Colours.YellowDark, TRANSFORM_DURATION, Easing.OutQuint); this.ScaleTo(IsHeld || IsHovered ? 1.5f : 1, TRANSFORM_DURATION, Easing.OutQuint); } From c6d952abe36a1ae16224291cd60d3012ef709140 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Jun 2023 17:01:40 +0900 Subject: [PATCH 2/7] Add support for `Ctrl` + `<` / `>` to rotate selection in editor As discussed in https://github.com/ppy/osu/discussions/24048. --- .../Screens/Edit/Compose/Components/SelectionBox.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 50eb3a186d..d8fd18ff8f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.Edit.Compose.Components public Action OperationEnded; private SelectionBoxButton reverseButton; + private SelectionBoxButton rotateClockwiseButton; + private SelectionBoxButton rotateCounterClockwiseButton; private bool canReverse; @@ -172,6 +174,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { case Key.G: return reverseButton?.TriggerClick() ?? false; + + case Key.Comma: + return rotateCounterClockwiseButton?.TriggerClick() ?? false; + + case Key.Period: + return rotateClockwiseButton?.TriggerClick() ?? false; } return base.OnKeyDown(e); @@ -254,8 +262,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addRotationComponents() { - addButton(FontAwesome.Solid.Undo, "Rotate 90 degrees counter-clockwise", () => OnRotation?.Invoke(-90)); - addButton(FontAwesome.Solid.Redo, "Rotate 90 degrees clockwise", () => OnRotation?.Invoke(90)); + rotateCounterClockwiseButton = addButton(FontAwesome.Solid.Undo, "Rotate 90 degrees counter-clockwise", () => OnRotation?.Invoke(-90)); + rotateClockwiseButton = addButton(FontAwesome.Solid.Redo, "Rotate 90 degrees clockwise", () => OnRotation?.Invoke(90)); addRotateHandle(Anchor.TopLeft); addRotateHandle(Anchor.TopRight); From ad3a470eafad8a22b678a45d6ff7a7da574764c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jun 2023 22:01:44 +0200 Subject: [PATCH 3/7] Enable NRT in `SelectionBox` --- .../Edit/Compose/Components/SelectionBox.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index d8fd18ff8f..8ab2a821a9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -24,17 +22,17 @@ namespace osu.Game.Screens.Edit.Compose.Components private const float button_padding = 5; - public Func OnRotation; - public Func OnScale; - public Func OnFlip; - public Func OnReverse; + public Func? OnRotation; + public Func? OnScale; + public Func? OnFlip; + public Func? OnReverse; - public Action OperationStarted; - public Action OperationEnded; + public Action? OperationStarted; + public Action? OperationEnded; - private SelectionBoxButton reverseButton; - private SelectionBoxButton rotateClockwiseButton; - private SelectionBoxButton rotateCounterClockwiseButton; + private SelectionBoxButton? reverseButton; + private SelectionBoxButton? rotateClockwiseButton; + private SelectionBoxButton? rotateCounterClockwiseButton; private bool canReverse; @@ -138,7 +136,7 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - private string text; + private string text = string.Empty; public string Text { @@ -154,13 +152,13 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - private SelectionBoxDragHandleContainer dragHandles; - private FillFlowContainer buttons; + private SelectionBoxDragHandleContainer dragHandles = null!; + private FillFlowContainer buttons = null!; - private OsuSpriteText selectionDetailsText; + private OsuSpriteText? selectionDetailsText; [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; [BackgroundDependencyLoader] private void load() => recreate(); From 54280f06be5d8cb0dae7348061088278bbfd9a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jun 2023 22:02:15 +0200 Subject: [PATCH 4/7] Switch to `== true` --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 8ab2a821a9..81b501b39e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -171,13 +171,13 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (e.Key) { case Key.G: - return reverseButton?.TriggerClick() ?? false; + return reverseButton?.TriggerClick() == true; case Key.Comma: - return rotateCounterClockwiseButton?.TriggerClick() ?? false; + return rotateCounterClockwiseButton?.TriggerClick() == true; case Key.Period: - return rotateClockwiseButton?.TriggerClick() ?? false; + return rotateClockwiseButton?.TriggerClick() == true; } return base.OnKeyDown(e); From 17ed45d07c2e8b6fba9995fc65a4963a613341ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jun 2023 22:04:15 +0200 Subject: [PATCH 5/7] Mention hotkeys in button tooltips --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 81b501b39e..05fe137732 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -260,8 +260,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addRotationComponents() { - rotateCounterClockwiseButton = addButton(FontAwesome.Solid.Undo, "Rotate 90 degrees counter-clockwise", () => OnRotation?.Invoke(-90)); - rotateClockwiseButton = addButton(FontAwesome.Solid.Redo, "Rotate 90 degrees clockwise", () => OnRotation?.Invoke(90)); + rotateCounterClockwiseButton = addButton(FontAwesome.Solid.Undo, "Rotate 90 degrees counter-clockwise (Ctrl-<)", () => OnRotation?.Invoke(-90)); + rotateClockwiseButton = addButton(FontAwesome.Solid.Redo, "Rotate 90 degrees clockwise (Ctrl->)", () => OnRotation?.Invoke(90)); addRotateHandle(Anchor.TopLeft); addRotateHandle(Anchor.TopRight); From 444f71541ad8894077674b7a350faf3fe7993c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jun 2023 22:10:53 +0200 Subject: [PATCH 6/7] Add test coverage for rotate hotkeys --- .../Editing/TestSceneComposerSelection.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs index b14025c9d8..4d99c47f77 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs @@ -101,6 +101,38 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("objects reverted to original position", () => addedObjects[0].StartTime == 100); } + [Test] + public void TestRotateHotkeys() + { + HitCircle[] addedObjects = null; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, + })); + + AddStep("select objects", () => EditorBeatmap.SelectedHitObjects.AddRange(addedObjects)); + + AddStep("rotate clockwise", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.Period); + InputManager.ReleaseKey(Key.ControlLeft); + }); + AddAssert("objects rotated clockwise", () => addedObjects[0].Position == new Vector2(300, 0)); + + AddStep("rotate counterclockwise", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.Comma); + InputManager.ReleaseKey(Key.ControlLeft); + }); + AddAssert("objects reverted to original position", () => addedObjects[0].Position == new Vector2(0)); + } + [Test] public void TestBasicSelect() { From 9be2d9d62e131d22e98b650bdb82e48c97d8f18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jun 2023 22:25:01 +0200 Subject: [PATCH 7/7] Fix hotkey presses generating unnecessary undo history The buttons don't check whether the operation they correspond to is possible to perform in the current state of the selection box, so not checking `Can{Reverse,Rotate}` causes superfluous undo states to be added without any real changes if an attempt is made to reverse or rotate a selection that cannot be reversed or rotated. --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 05fe137732..e93b9f0691 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -171,13 +171,13 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (e.Key) { case Key.G: - return reverseButton?.TriggerClick() == true; + return CanReverse && reverseButton?.TriggerClick() == true; case Key.Comma: - return rotateCounterClockwiseButton?.TriggerClick() == true; + return CanRotate && rotateCounterClockwiseButton?.TriggerClick() == true; case Key.Period: - return rotateClockwiseButton?.TriggerClick() == true; + return CanRotate && rotateClockwiseButton?.TriggerClick() == true; } return base.OnKeyDown(e);