diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 78ab7947a7..7d9c08d3d2 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -148,7 +148,11 @@ namespace osu.Game.Tests.Visual.Navigation private void switchToGameplayScene() { - AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay").TriggerClick()); + AddStep("Click gameplay scene button", () => + { + InputManager.MoveMouseTo(skinEditor.ChildrenOfType().First(b => b.Text == "Gameplay")); + InputManager.Click(MouseButton.Left); + }); AddUntilStep("wait for player", () => { diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index c7bf23865b..d38aac1173 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -108,13 +108,6 @@ namespace osu.Game.Screens.Edit.Compose.Components /// protected virtual bool AllowDeselectionDuringDrag => true; - /// - /// Positional input must be received outside the container's bounds, - /// in order to handle blueprints which are partially offscreen. - /// - /// - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - protected override bool OnMouseDown(MouseDownEvent e) { bool selectionPerformed = performMouseDownActions(e); diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index ba43704a5f..4c37d200bc 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -39,6 +39,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private PlacementBlueprint currentPlacement; private InputManager inputManager; + /// + /// Positional input must be received outside the container's bounds, + /// in order to handle composer blueprints which are partially offscreen. + /// + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + public ComposeBlueprintContainer(HitObjectComposer composer) : base(composer) { diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 96bd352cf9..91af3fde16 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -21,6 +21,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Edit; +using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; using osuTK.Input; @@ -103,7 +104,8 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Positional input must be received outside the container's bounds, /// in order to handle blueprints which are partially offscreen. /// - /// + /// + /// public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 138e28fa06..742e16d5a9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -35,6 +35,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable placement; private SelectionBlueprint placementBlueprint; + /// + /// Positional input must be received outside the container's bounds, + /// in order to handle timeline blueprints which are stacked offscreen. + /// + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ReceivePositionalInputAt(screenSpacePos); + public TimelineBlueprintContainer(HitObjectComposer composer) : base(composer) { diff --git a/osu.Game/Screens/Utility/LatencyArea.cs b/osu.Game/Screens/Utility/LatencyArea.cs index 61a97d92b0..b7d45ba642 100644 --- a/osu.Game/Screens/Utility/LatencyArea.cs +++ b/osu.Game/Screens/Utility/LatencyArea.cs @@ -6,8 +6,10 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Graphics.Cursor; using osu.Game.Overlays; using osu.Game.Screens.Utility.SampleComponents; using osuTK.Input; @@ -15,7 +17,7 @@ using osuTK.Input; namespace osu.Game.Screens.Utility { [Cached] - public class LatencyArea : CompositeDrawable + public class LatencyArea : CompositeDrawable, IProvideCursor { [Resolved] private OverlayColourProvider overlayColourProvider { get; set; } = null!; @@ -34,6 +36,10 @@ namespace osu.Game.Screens.Utility public readonly Bindable VisualMode = new Bindable(); + public CursorContainer? Cursor { get; private set; } + + public bool ProvidingUserCursor => IsActiveArea.Value; + public LatencyArea(Key key, int? targetFrameRate) { this.key = key; @@ -85,7 +91,7 @@ namespace osu.Game.Screens.Utility { RelativeSizeAxes = Axes.Both, }, - new LatencyCursorContainer + Cursor = new LatencyCursorContainer { RelativeSizeAxes = Axes.Both, }, @@ -99,7 +105,7 @@ namespace osu.Game.Screens.Utility { RelativeSizeAxes = Axes.Both, }, - new LatencyCursorContainer + Cursor = new LatencyCursorContainer { RelativeSizeAxes = Axes.Both, }, @@ -113,7 +119,7 @@ namespace osu.Game.Screens.Utility { RelativeSizeAxes = Axes.Both, }, - new LatencyCursorContainer + Cursor = new LatencyCursorContainer { RelativeSizeAxes = Axes.Both, }, diff --git a/osu.Game/Screens/Utility/LatencyCertifierScreen.cs b/osu.Game/Screens/Utility/LatencyCertifierScreen.cs index 8675078a5e..6754b65129 100644 --- a/osu.Game/Screens/Utility/LatencyCertifierScreen.cs +++ b/osu.Game/Screens/Utility/LatencyCertifierScreen.cs @@ -39,8 +39,6 @@ namespace osu.Game.Screens.Utility public override bool HideOverlaysOnEnter => true; - public override bool CursorVisible => mainArea.Count == 0; - public override float BackgroundParallaxAmount => 0; private readonly LinkFlowContainer explanatoryText; diff --git a/osu.Game/Screens/Utility/SampleComponents/LatencyCursorContainer.cs b/osu.Game/Screens/Utility/SampleComponents/LatencyCursorContainer.cs index 40911684f8..656aa6a8b3 100644 --- a/osu.Game/Screens/Utility/SampleComponents/LatencyCursorContainer.cs +++ b/osu.Game/Screens/Utility/SampleComponents/LatencyCursorContainer.cs @@ -1,55 +1,52 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; +#nullable enable + using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Input.States; -using osu.Game.Overlays; using osuTK; using osuTK.Input; namespace osu.Game.Screens.Utility.SampleComponents { - public class LatencyCursorContainer : LatencySampleComponent + public class LatencyCursorContainer : CursorContainer { - private Circle cursor = null!; + protected override Drawable CreateCursor() => new LatencyCursor(); - [Resolved] - private OverlayColourProvider overlayColourProvider { get; set; } = null!; + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; public LatencyCursorContainer() { - Masking = true; + State.Value = Visibility.Hidden; } - protected override void LoadComplete() + protected override bool OnMouseMove(MouseMoveEvent e) { - base.LoadComplete(); - - InternalChild = cursor = new Circle - { - Size = new Vector2(40), - Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour2, - }; + // Scheduling is required to ensure updating of cursor position happens in limited rate. + // We can alternatively solve this by a PassThroughInputManager layer inside LatencyArea, + // but that would mean including input lag to this test, which may not be desired. + Schedule(() => base.OnMouseMove(e)); + return false; } - protected override bool OnHover(HoverEvent e) => false; - - protected override void UpdateAtLimitedRate(InputState inputState) + private class LatencyCursor : LatencySampleComponent { - cursor.Colour = inputState.Mouse.IsPressed(MouseButton.Left) ? overlayColourProvider.Content1 : overlayColourProvider.Colour2; - - if (IsActive.Value) + public LatencyCursor() { - cursor.Position = ToLocalSpace(inputState.Mouse.Position); - cursor.Alpha = 1; + AutoSizeAxes = Axes.Both; + Origin = Anchor.Centre; + + InternalChild = new Circle { Size = new Vector2(40) }; } - else + + protected override void UpdateAtLimitedRate(InputState inputState) { - cursor.Alpha = 0; + Colour = inputState.Mouse.IsPressed(MouseButton.Left) ? OverlayColourProvider.Content1 : OverlayColourProvider.Colour2; } } }