diff --git a/osu.Framework/Input/InputManager.cs b/osu.Framework/Input/InputManager.cs
index debc7cef14..9c42184ff0 100644
--- a/osu.Framework/Input/InputManager.cs
+++ b/osu.Framework/Input/InputManager.cs
@@ -128,7 +128,7 @@ public Drawable DraggedDrawable
///
/// This collection should not be retained as a reference. The contents is not stable outside of local usage.
///
- public SlimReadOnlyListWrapper PositionalInputQueue => buildPositionalInputQueue(CurrentState.Mouse.Position);
+ public SlimReadOnlyListWrapper PositionalInputQueue => buildPositionalInputQueue(!CurrentState.Mouse.IsPositionValid ? null : CurrentState.Mouse.Position);
///
/// Contains all s in top-down order which are considered
@@ -627,10 +627,13 @@ private SlimReadOnlyListWrapper buildNonPositionalInputQueue()
private readonly List positionalInputQueue = new List();
- private SlimReadOnlyListWrapper buildPositionalInputQueue(Vector2 screenSpacePos)
+ private SlimReadOnlyListWrapper buildPositionalInputQueue(Vector2? screenSpacePos)
{
positionalInputQueue.Clear();
+ if (screenSpacePos == null)
+ return positionalInputQueue.AsSlimReadOnly();
+
if (this is UserInputManager)
FrameStatistics.Increment(StatisticsCounterType.PositionalIQ);
@@ -639,7 +642,7 @@ private SlimReadOnlyListWrapper 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();
@@ -959,16 +962,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);
}
diff --git a/osu.Framework/Input/StateChanges/Events/MousePositionChangeEvent.cs b/osu.Framework/Input/StateChanges/Events/MousePositionChangeEvent.cs
index 8b0958ab75..48b78ff2ee 100644
--- a/osu.Framework/Input/StateChanges/Events/MousePositionChangeEvent.cs
+++ b/osu.Framework/Input/StateChanges/Events/MousePositionChangeEvent.cs
@@ -9,9 +9,9 @@ namespace osu.Framework.Input.StateChanges.Events
public class MousePositionChangeEvent : InputStateChangeEvent
{
///
- /// The last mouse position.
+ /// The last mouse position, or null if the event is invalidation of the mouse position state.
///
- public readonly Vector2 LastPosition;
+ public readonly Vector2? LastPosition;
public MousePositionChangeEvent(InputState state, IInput input, Vector2 lastPosition)
: base(state, input)
diff --git a/osu.Framework/Input/StateChanges/MouseInvalidatePositionInput.cs b/osu.Framework/Input/StateChanges/MouseInvalidatePositionInput.cs
new file mode 100644
index 0000000000..206d066fa0
--- /dev/null
+++ b/osu.Framework/Input/StateChanges/MouseInvalidatePositionInput.cs
@@ -0,0 +1,28 @@
+// 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.Input.StateChanges.Events;
+using osu.Framework.Input.States;
+
+namespace osu.Framework.Input.StateChanges
+{
+ ///
+ /// 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.
+ ///
+ 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));
+ }
+ }
+ }
+}
diff --git a/osu.Framework/Input/StateChanges/MouseInvalidatePositionInputFromTouch.cs b/osu.Framework/Input/StateChanges/MouseInvalidatePositionInputFromTouch.cs
new file mode 100644
index 0000000000..d4966013d7
--- /dev/null
+++ b/osu.Framework/Input/StateChanges/MouseInvalidatePositionInputFromTouch.cs
@@ -0,0 +1,17 @@
+// 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.Input.StateChanges.Events;
+
+namespace osu.Framework.Input.StateChanges
+{
+ public class MouseInvalidatePositionInputFromTouch : MouseInvalidatePositionInput, ISourcedFromTouch
+ {
+ public MouseInvalidatePositionInputFromTouch(TouchStateChangeEvent touchEvent)
+ {
+ TouchEvent = touchEvent;
+ }
+
+ public TouchStateChangeEvent TouchEvent { get; }
+ }
+}