diff --git a/osu.Android.props b/osu.Android.props
index ff76e17184..c73c643d4b 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -11,7 +11,7 @@
manifestmerger.jar
-
+
diff --git a/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs
new file mode 100644
index 0000000000..58eff9ade7
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs
@@ -0,0 +1,83 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Screens;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Input.Bindings;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Edit.Compose.Components;
+using osu.Game.Tests.Beatmaps;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ public partial class TestScenePlacementBlueprint : EditorTestScene
+ {
+ protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
+
+ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
+
+ private GlobalActionContainer globalActionContainer => this.ChildrenOfType().Single();
+
+ [Test]
+ public void TestCommitPlacementViaGlobalAction()
+ {
+ Playfield playfield = null!;
+
+ AddStep("select slider placement tool", () => InputManager.Key(Key.Number3));
+ AddStep("move mouse to top left of playfield", () =>
+ {
+ playfield = this.ChildrenOfType().Single();
+ var location = (3 * playfield.ScreenSpaceDrawQuad.TopLeft + playfield.ScreenSpaceDrawQuad.BottomRight) / 4;
+ InputManager.MoveMouseTo(location);
+ });
+ AddStep("begin placement", () => InputManager.Click(MouseButton.Left));
+ AddStep("move mouse to bottom right of playfield", () =>
+ {
+ var location = (playfield.ScreenSpaceDrawQuad.TopLeft + 3 * playfield.ScreenSpaceDrawQuad.BottomRight) / 4;
+ InputManager.MoveMouseTo(location);
+ });
+ AddStep("confirm via global action", () =>
+ {
+ globalActionContainer.TriggerPressed(GlobalAction.Select);
+ globalActionContainer.TriggerReleased(GlobalAction.Select);
+ });
+ AddAssert("slider placed", () => EditorBeatmap.HitObjects.Count, () => Is.EqualTo(1));
+ }
+
+ [Test]
+ public void TestAbortPlacementViaGlobalAction()
+ {
+ Playfield playfield = null!;
+
+ AddStep("select slider placement tool", () => InputManager.Key(Key.Number3));
+ AddStep("move mouse to top left of playfield", () =>
+ {
+ playfield = this.ChildrenOfType().Single();
+ var location = (3 * playfield.ScreenSpaceDrawQuad.TopLeft + playfield.ScreenSpaceDrawQuad.BottomRight) / 4;
+ InputManager.MoveMouseTo(location);
+ });
+ AddStep("begin placement", () => InputManager.Click(MouseButton.Left));
+ AddStep("move mouse to bottom right of playfield", () =>
+ {
+ var location = (playfield.ScreenSpaceDrawQuad.TopLeft + 3 * playfield.ScreenSpaceDrawQuad.BottomRight) / 4;
+ InputManager.MoveMouseTo(location);
+ });
+ AddStep("abort via global action", () =>
+ {
+ globalActionContainer.TriggerPressed(GlobalAction.Back);
+ globalActionContainer.TriggerReleased(GlobalAction.Back);
+ });
+ AddAssert("editor is still current", () => Editor.IsCurrentScreen());
+ AddAssert("slider not placed", () => EditorBeatmap.HitObjects.Count, () => Is.EqualTo(0));
+ AddAssert("no active placement", () => this.ChildrenOfType().Single().CurrentPlacement.PlacementActive,
+ () => Is.EqualTo(PlacementBlueprint.PlacementState.Waiting));
+ }
+ }
+}
diff --git a/osu.Game/Input/OsuUserInputManager.cs b/osu.Game/Input/OsuUserInputManager.cs
index ab43497156..c205636ab9 100644
--- a/osu.Game/Input/OsuUserInputManager.cs
+++ b/osu.Game/Input/OsuUserInputManager.cs
@@ -3,6 +3,7 @@
#nullable disable
+using osu.Framework.Bindables;
using osu.Framework.Input;
using osuTK.Input;
@@ -10,6 +11,10 @@ namespace osu.Game.Input
{
public partial class OsuUserInputManager : UserInputManager
{
+ protected override bool AllowRightClickFromLongTouch => !LocalUserPlaying.Value;
+
+ public readonly BindableBool LocalUserPlaying = new BindableBool();
+
internal OsuUserInputManager()
{
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 7c9b03bd5b..fe6e479d19 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -269,6 +269,13 @@ namespace osu.Game
if (hideToolbar) Toolbar.Hide();
}
+ protected override UserInputManager CreateUserInputManager()
+ {
+ var userInputManager = base.CreateUserInputManager();
+ (userInputManager as OsuUserInputManager)?.LocalUserPlaying.BindTo(LocalUserPlaying);
+ return userInputManager;
+ }
+
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
index 951cf3802f..4c9320c2a6 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
@@ -3,6 +3,8 @@
#nullable disable
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -23,6 +25,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
public partial class TabletSettings : SettingsSubsection
{
+ public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "area" });
+
public TabletAreaSelection AreaSelection { get; private set; }
private readonly ITabletHandler tabletHandler;
diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
index 253d59751d..12c0ea1807 100644
--- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
@@ -9,10 +9,12 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose;
@@ -24,7 +26,7 @@ namespace osu.Game.Rulesets.Edit
///
/// A blueprint which governs the creation of a new to actualisation.
///
- public abstract partial class PlacementBlueprint : CompositeDrawable
+ public abstract partial class PlacementBlueprint : CompositeDrawable, IKeyBindingHandler
{
///
/// Whether the is currently mid-placement, but has not necessarily finished being placed.
@@ -115,6 +117,30 @@ namespace osu.Game.Rulesets.Edit
PlacementActive = PlacementState.Finished;
}
+ public bool OnPressed(KeyBindingPressEvent e)
+ {
+ if (PlacementActive == PlacementState.Waiting)
+ return false;
+
+ switch (e.Action)
+ {
+ case GlobalAction.Select:
+ EndPlacement(true);
+ return true;
+
+ case GlobalAction.Back:
+ EndPlacement(false);
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public void OnReleased(KeyBindingReleaseEvent e)
+ {
+ }
+
///
/// Updates the time and position of this based on the provided snap information.
///
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 2ae54a3afe..a24e22f22b 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.UI
public abstract partial class RulesetInputManager : PassThroughInputManager, ICanAttachHUDPieces, IHasReplayHandler, IHasRecordingHandler
where T : struct
{
+ protected override bool AllowRightClickFromLongTouch => false;
+
public readonly KeyBindingContainer KeyBindingContainer;
[Resolved(CanBeNull = true)]
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 4315f44e07..3ea4a57c2c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -36,7 +36,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index c5477f765e..a240dec963 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -16,6 +16,6 @@
iossimulator-x64
-
+