From 866ae3472bb654af5bde50f79ecb0ae2cbd884e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Jan 2022 16:46:34 +0900 Subject: [PATCH] Add global flip hotkeys --- .../Edit/CatchSelectionHandler.cs | 12 ++++--- .../Edit/OsuSelectionHandler.cs | 7 +++-- .../Input/Bindings/GlobalActionContainer.cs | 10 +++++- .../GlobalActionKeyBindingStrings.cs | 10 ++++++ .../Edit/Compose/Components/SelectionBox.cs | 6 ++-- .../Compose/Components/SelectionHandler.cs | 31 +++++++++++++++++-- .../Timeline/TimelineSelectionHandler.cs | 12 +++---- .../Skinning/Editor/SkinSelectionHandler.cs | 4 +-- 8 files changed, 68 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 8cb0804ab7..41a2584acc 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Edit return true; } - public override bool HandleFlip(Direction direction) + public override bool HandleFlip(Direction direction, bool flipOverOrigin) { var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.Edit EditorBeatmap.PerformOnSelection(h => { if (h is CatchHitObject catchObject) - changed |= handleFlip(selectionRange, catchObject); + changed |= handleFlip(selectionRange, catchObject, flipOverOrigin); }); return changed; } @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Catch.Edit return Math.Clamp(deltaX, lowerBound, upperBound); } - private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject) + private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject, bool flipOverOrigin) { switch (hitObject) { @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.Edit return false; case JuiceStream juiceStream: - juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX); + juiceStream.OriginalX = getFlippedPosition(juiceStream.OriginalX); foreach (var point in juiceStream.Path.ControlPoints) point.Position *= new Vector2(-1, 1); @@ -133,9 +133,11 @@ namespace osu.Game.Rulesets.Catch.Edit return true; default: - hitObject.OriginalX = selectionRange.GetFlippedPosition(hitObject.OriginalX); + hitObject.OriginalX = getFlippedPosition(hitObject.OriginalX); return true; } + + float getFlippedPosition(float original) => flipOverOrigin ? CatchPlayfield.WIDTH - original : selectionRange.GetFlippedPosition(original); } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 4a57d36eb4..d172fa5398 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -10,6 +10,7 @@ using osu.Game.Extensions; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -84,15 +85,15 @@ namespace osu.Game.Rulesets.Osu.Edit return true; } - public override bool HandleFlip(Direction direction) + public override bool HandleFlip(Direction direction, bool flipOverOrigin) { var hitObjects = selectedMovableObjects; - var selectedObjectsQuad = getSurroundingQuad(hitObjects); + var flipQuad = flipOverOrigin ? new Quad(0, 0, OsuPlayfield.BASE_SIZE.X, OsuPlayfield.BASE_SIZE.Y) : getSurroundingQuad(hitObjects); foreach (var h in hitObjects) { - h.Position = GetFlippedPosition(direction, selectedObjectsQuad, h.Position); + h.Position = GetFlippedPosition(direction, flipQuad, h.Position); if (h is Slider slider) { diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index c71cb6a00a..47cb7be2cf 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -77,6 +77,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight), new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode), new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay), + new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally), + new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically), }; public IEnumerable InGameKeyBindings => new[] @@ -292,6 +294,12 @@ namespace osu.Game.Input.Bindings EditorCycleGridDisplayMode, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestGameplay))] - EditorTestGameplay + EditorTestGameplay, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipHorizontally))] + EditorFlipHorizontally, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipVertically))] + EditorFlipVertically, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 35a0c2ae74..777e97d1e3 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -229,6 +229,16 @@ namespace osu.Game.Localisation /// public static LocalisableString EditorNudgeRight => new TranslatableString(getKey(@"editor_nudge_right"), @"Nudge selection right"); + /// + /// "Flip selection horizontally" + /// + public static LocalisableString EditorFlipHorizontally => new TranslatableString(getKey(@"editor_flip_horizontally"), @"Flip selection horizontally"); + + /// + /// "Flip selection vertically" + /// + public static LocalisableString EditorFlipVertically => new TranslatableString(getKey(@"editor_flip_vertically"), @"Flip selection vertically"); + /// /// "Toggle skin editor" /// diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 732c6a5dd4..3a31f6ea8c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Compose.Components public Func OnRotation; public Func OnScale; - public Func OnFlip; + public Func OnFlip; public Func OnReverse; public Action OperationStarted; @@ -281,12 +281,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addXFlipComponents() { - addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally", () => OnFlip?.Invoke(Direction.Horizontal)); + addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally", () => OnFlip?.Invoke(Direction.Horizontal, false)); } private void addYFlipComponents() { - addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically", () => OnFlip?.Invoke(Direction.Vertical)); + addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically", () => OnFlip?.Invoke(Direction.Vertical, false)); } private void addButton(IconUsage icon, string tooltip, Action action) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index ee35b6a47c..d9d310c72c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -17,6 +17,7 @@ using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// A component which outlines items and handles movement of selections. /// - public abstract class SelectionHandler : CompositeDrawable, IKeyBindingHandler, IHasContextMenu + public abstract class SelectionHandler : CompositeDrawable, IKeyBindingHandler, IKeyBindingHandler, IHasContextMenu { /// /// The currently selected blueprints. @@ -127,9 +128,10 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Handles the selected items being flipped. /// - /// The direction to flip + /// The direction to flip. + /// Whether the flip operation should be global to the playfield's origin or local to the selected pattern. /// Whether any items could be flipped. - public virtual bool HandleFlip(Direction direction) => false; + public virtual bool HandleFlip(Direction direction, bool flipOverOrigin) => false; /// /// Handles the selected items being reversed pattern-wise. @@ -137,6 +139,29 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether any items could be reversed. public virtual bool HandleReverse() => false; + public virtual bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat) + return false; + + switch (e.Action) + { + case GlobalAction.EditorFlipHorizontally: + HandleFlip(Direction.Horizontal, true); + return true; + + case GlobalAction.EditorFlipVertically: + HandleFlip(Direction.Vertical, true); + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + public bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs index 845a671e2c..8b7ff45765 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; @@ -17,7 +16,7 @@ using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler + internal class TimelineSelectionHandler : EditorSelectionHandler { [Resolved] private Timeline timeline { get; set; } @@ -27,8 +26,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; - public bool OnPressed(KeyBindingPressEvent e) + public override bool OnPressed(KeyBindingPressEvent e) { + // Importantly, we block the base call here. + // Other key operations will be handled by the composer view's SelectionHandler instead. + switch (e.Action) { case GlobalAction.EditorNudgeLeft: @@ -43,10 +45,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return false; } - public void OnReleased(KeyBindingReleaseEvent e) - { - } - /// /// Nudge the current selection by the specified multiple of beat divisor lengths, /// based on the timing at the first object in the selection. diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 01bd5e8196..a54590a0ea 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -126,9 +126,9 @@ namespace osu.Game.Skinning.Editor return true; } - public override bool HandleFlip(Direction direction) + public override bool HandleFlip(Direction direction, bool flipOverOrigin) { - var selectionQuad = getSelectionQuad(); + var selectionQuad = flipOverOrigin ? ScreenSpaceDrawQuad : getSelectionQuad(); Vector2 scaleFactor = direction == Direction.Horizontal ? new Vector2(-1, 1) : new Vector2(1, -1); foreach (var b in SelectedBlueprints)