Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalidate mouse position when releasing final touch #6458

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ private void addTestInputManagerStep()
public void UseParentInputChange()
{
addTestInputManagerStep();
AddStep("Move mouse", () => InputManager.MoveMouseTo(Content));
AddStep("Press buttons", () =>
{
InputManager.PressButton(MouseButton.Left);
Expand Down Expand Up @@ -268,6 +269,7 @@ public void TestMouseTouchProductionOnPassThrough()

AddStep("end touch", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, testInputManager.ScreenSpaceDrawQuad.Centre)));

AddStep("bring back mouse", () => InputManager.MoveMouseTo(testInputManager));
AddStep("press mouse", () => InputManager.PressButton(MouseButton.Left));
AddAssert("pass-through handled mouse", () => testInputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left);
}
Expand Down
2 changes: 2 additions & 0 deletions osu.Framework.Tests/Visual/Input/TestSceneTouchInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ public void TestMouseInputAppliedFromLatestTouch()
{
AddStep($"deactivate {s}", () => InputManager.EndTouch(new Touch(s, getTouchUpPos(s))));
AddAssert("no mouse event received", () => receptors[(int)s].MouseEvents.Count == 0);
AddAssert("mouse is still valid", () => InputManager.CurrentState.Mouse.IsPositionValid);
}

AddStep("deactivate last", () =>
Expand All @@ -297,6 +298,7 @@ public void TestMouseInputAppliedFromLatestTouch()

return firstReceptor.MouseEvents.Count == 0;
});
AddAssert("mouse is invalidated", () => !InputManager.CurrentState.Mouse.IsPositionValid);

AddAssert("all events dequeued", () => receptors.All(r => r.MouseEvents.Count == 0));

Expand Down
33 changes: 23 additions & 10 deletions osu.Framework/Input/InputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public Drawable DraggedDrawable
/// <remarks>
/// This collection should not be retained as a reference. The contents is not stable outside of local usage.
/// </remarks>
public SlimReadOnlyListWrapper<Drawable> PositionalInputQueue => buildPositionalInputQueue(CurrentState.Mouse.Position);
public SlimReadOnlyListWrapper<Drawable> PositionalInputQueue => buildPositionalInputQueue(!CurrentState.Mouse.IsPositionValid ? null : CurrentState.Mouse.Position);

/// <summary>
/// Contains all <see cref="Drawable"/>s in top-down order which are considered
Expand Down Expand Up @@ -627,10 +627,13 @@ private SlimReadOnlyListWrapper<Drawable> buildNonPositionalInputQueue()

private readonly List<Drawable> positionalInputQueue = new List<Drawable>();

private SlimReadOnlyListWrapper<Drawable> buildPositionalInputQueue(Vector2 screenSpacePos)
private SlimReadOnlyListWrapper<Drawable> buildPositionalInputQueue(Vector2? screenSpacePos)
{
positionalInputQueue.Clear();

if (screenSpacePos == null)
return positionalInputQueue.AsSlimReadOnly();

if (this is UserInputManager)
FrameStatistics.Increment(StatisticsCounterType.PositionalIQ);

Expand All @@ -639,7 +642,7 @@ private SlimReadOnlyListWrapper<Drawable> buildPositionalInputQueue(Vector2 scre
for (int i = 0; i < children.Count; i++)
{
if (ShouldBeConsideredForInput(children[i]))
children[i].BuildPositionalInputQueue(screenSpacePos, positionalInputQueue);
children[i].BuildPositionalInputQueue(screenSpacePos.Value, positionalInputQueue);
}

positionalInputQueue.Reverse();
Expand Down Expand Up @@ -791,6 +794,7 @@ protected virtual bool HandleMouseTouchStateChange(TouchStateChangeEvent e)
if (!MapMouseToLatestTouch)
return false;

// Update mouse position state
if (e.IsActive == true || e.LastPosition != null)
{
new MousePositionAbsoluteInputFromTouch(e)
Expand All @@ -799,6 +803,7 @@ protected virtual bool HandleMouseTouchStateChange(TouchStateChangeEvent e)
}.Apply(CurrentState, this);
}

// Update mouse button state
if (e.IsActive != null)
{
if (e.IsActive == true)
Expand All @@ -809,6 +814,11 @@ protected virtual bool HandleMouseTouchStateChange(TouchStateChangeEvent e)
updateTouchMouseLeft(e);
}

// Invalidate mouse position if releasing last touch. This is done after updating button state
// for click events to be processed on the targeted input queue before the position is invalidated.
if (e.IsActive == false && !e.State.Touch.ActiveSources.HasAnyButtonPressed)
new MouseInvalidatePositionInputFromTouch(e).Apply(CurrentState, this);

updateTouchMouseRight(e);
return true;
}
Expand Down Expand Up @@ -959,16 +969,19 @@ protected virtual void HandleMousePositionChange(MousePositionChangeEvent e)
var state = e.State;
var mouse = state.Mouse;

foreach (var h in InputHandlers)
if (e.LastPosition != null)
{
if (h.Enabled.Value && h is INeedsMousePositionFeedback handler)
handler.FeedbackMousePositionChange(mouse.Position, h == mouseSource);
}
foreach (var h in InputHandlers)
{
if (h.Enabled.Value && h is INeedsMousePositionFeedback handler)
handler.FeedbackMousePositionChange(mouse.Position, h == mouseSource);
}

handleMouseMove(state, e.LastPosition);
handleMouseMove(state, e.LastPosition.Value);

foreach (var manager in mouseButtonEventManagers.Values)
manager.HandlePositionChange(state, e.LastPosition);
foreach (var manager in mouseButtonEventManagers.Values)
manager.HandlePositionChange(state, e.LastPosition.Value);
}

updateHoverEvents(state);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ namespace osu.Framework.Input.StateChanges.Events
public class MousePositionChangeEvent : InputStateChangeEvent
{
/// <summary>
/// The last mouse position.
/// The last mouse position, or null if the event is invalidation of the mouse position state.
/// </summary>
public readonly Vector2 LastPosition;
public readonly Vector2? LastPosition;

public MousePositionChangeEvent(InputState state, IInput input, Vector2 lastPosition)
: base(state, input)
Expand Down
28 changes: 28 additions & 0 deletions osu.Framework/Input/StateChanges/MouseInvalidatePositionInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Input.States;

namespace osu.Framework.Input.StateChanges
{
/// <summary>
/// Denotes an invalidation of the current mouse position,
/// mainly used when a single remaining touch source is released,
/// or a hovering pen (e.g. Apple Pencil) leaves the screen area.
/// </summary>
public class MouseInvalidatePositionInput : IInput
{
public void Apply(InputState state, IInputStateChangeHandler handler)
{
var mouse = state.Mouse;

if (mouse.IsPositionValid)
{
mouse.IsPositionValid = false;
mouse.LastSource = this;
handler.HandleInputStateChange(new MousePositionChangeEvent(state, this, mouse.Position));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Framework.Input.StateChanges.Events;

namespace osu.Framework.Input.StateChanges
{
public class MouseInvalidatePositionInputFromTouch : MouseInvalidatePositionInput, ISourcedFromTouch
{
public MouseInvalidatePositionInputFromTouch(TouchStateChangeEvent touchEvent)
{
TouchEvent = touchEvent;
}

public TouchStateChangeEvent TouchEvent { get; }
}
}
Loading