From 48b96a10a44b2a7eca1b7f9413a81792bf6a9bf0 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sun, 6 Feb 2022 22:07:33 +0100 Subject: [PATCH] Added InputHandler mouse and touch position querying that preserves the game's viewport and fixed the graphics device's viewport being ignored for mouse and touch queries Closes #1 --- CHANGELOG.md | 2 ++ MLEM.Ui/Elements/ScrollBar.cs | 14 +++++----- MLEM.Ui/Elements/Tooltip.cs | 2 +- MLEM.Ui/UiControls.cs | 10 +++---- MLEM/Input/InputHandler.cs | 50 ++++++++++++++++++++++++++++++++--- Sandbox/GameImpl.cs | 8 +++++- 6 files changed, 69 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56940ba..094f66a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Additions Improvements - Generify GenericFont's string drawing +- Added InputHandler mouse and touch position querying that preserves the game's viewport Fixes - Fixed StaticSpriteBatch handling rotated sprites incorrectly @@ -40,6 +41,7 @@ Improvements Fixes - Fixed paragraph links having incorrect hover locations when using special text alignments +- Fixed the graphics device's viewport being ignored for mouse and touch queries Removals - Marked StyleProp equality members as obsolete diff --git a/MLEM.Ui/Elements/ScrollBar.cs b/MLEM.Ui/Elements/ScrollBar.cs index 4b356e6..267601a 100644 --- a/MLEM.Ui/Elements/ScrollBar.cs +++ b/MLEM.Ui/Elements/ScrollBar.cs @@ -137,14 +137,14 @@ namespace MLEM.Ui.Elements { // MOUSE INPUT var moused = this.Controls.MousedElement; - if (moused == this && this.Controls.Input.IsMouseButtonPressed(MouseButton.Left)) { + if (moused == this && this.Input.IsMouseButtonPressed(MouseButton.Left)) { this.isMouseHeld = true; - this.scrollStartOffset = this.TransformInverseAll(this.Input.MousePosition.ToVector2()) - this.ScrollerPosition; - } else if (this.isMouseHeld && !this.Controls.Input.IsMouseButtonDown(MouseButton.Left)) { + this.scrollStartOffset = this.TransformInverseAll(this.Input.ViewportMousePosition.ToVector2()) - this.ScrollerPosition; + } else if (this.isMouseHeld && !this.Input.IsMouseButtonDown(MouseButton.Left)) { this.isMouseHeld = false; } if (this.isMouseHeld) - this.ScrollToPos(this.TransformInverseAll(this.Input.MousePosition.ToVector2())); + this.ScrollToPos(this.TransformInverseAll(this.Input.ViewportMousePosition.ToVector2())); if (!this.Horizontal && moused != null && (moused == this.Parent || moused.GetParentTree().Contains(this.Parent))) { var scroll = this.Input.LastScrollWheel - this.Input.ScrollWheel; if (scroll != 0) @@ -154,7 +154,7 @@ namespace MLEM.Ui.Elements { // TOUCH INPUT if (!this.Horizontal) { // are we dragging on top of the panel? - if (this.Input.GetGesture(GestureType.VerticalDrag, out var drag)) { + if (this.Input.GetViewportGesture(GestureType.VerticalDrag, out var drag)) { // if the element under the drag's start position is on top of the panel, start dragging var touched = this.Parent.GetElementUnderPos(this.TransformInverseAll(drag.Position)); if (touched != null && touched != this) @@ -167,11 +167,11 @@ namespace MLEM.Ui.Elements { this.isDragging = false; } } - if (this.Input.TouchState.Count <= 0) { + if (this.Input.ViewportTouchState.Count <= 0) { // if no touch has occured this tick, then reset the variable this.isTouchHeld = false; } else { - foreach (var loc in this.Input.TouchState) { + foreach (var loc in this.Input.ViewportTouchState) { var pos = this.TransformInverseAll(loc.Position); // if we just started touching and are on top of the scroller, then we should start scrolling if (this.DisplayArea.Contains(pos) && !loc.TryGetPreviousLocation(out _)) { diff --git a/MLEM.Ui/Elements/Tooltip.cs b/MLEM.Ui/Elements/Tooltip.cs index b2b276b..c2be313 100644 --- a/MLEM.Ui/Elements/Tooltip.cs +++ b/MLEM.Ui/Elements/Tooltip.cs @@ -91,7 +91,7 @@ namespace MLEM.Ui.Elements { /// public void SnapPositionToMouse() { var viewport = this.System.Viewport; - var offset = (this.Input.MousePosition.ToVector2() + this.MouseOffset.Value) / this.Scale; + var offset = (this.Input.ViewportMousePosition.ToVector2() + this.MouseOffset.Value) / this.Scale; if (offset.X < viewport.X) offset.X = viewport.X; if (offset.Y < viewport.Y) diff --git a/MLEM.Ui/UiControls.cs b/MLEM.Ui/UiControls.cs index e376503..c0d8430 100644 --- a/MLEM.Ui/UiControls.cs +++ b/MLEM.Ui/UiControls.cs @@ -143,7 +143,7 @@ namespace MLEM.Ui { // MOUSE INPUT if (this.HandleMouse) { - var mousedNow = this.GetElementUnderPos(this.Input.MousePosition.ToVector2()); + var mousedNow = this.GetElementUnderPos(this.Input.ViewportMousePosition.ToVector2()); this.SetMousedElement(mousedNow); if (this.Input.IsMouseButtonPressed(MouseButton.Left)) { @@ -184,22 +184,22 @@ namespace MLEM.Ui { // TOUCH INPUT if (this.HandleTouch) { - if (this.Input.GetGesture(GestureType.Tap, out var tap)) { + if (this.Input.GetViewportGesture(GestureType.Tap, out var tap)) { this.IsAutoNavMode = false; var tapped = this.GetElementUnderPos(tap.Position); this.SelectElement(this.ActiveRoot, tapped); if (tapped != null && tapped.CanBePressed) this.System.InvokeOnElementPressed(tapped); - } else if (this.Input.GetGesture(GestureType.Hold, out var hold)) { + } else if (this.Input.GetViewportGesture(GestureType.Hold, out var hold)) { this.IsAutoNavMode = false; var held = this.GetElementUnderPos(hold.Position); this.SelectElement(this.ActiveRoot, held); if (held != null && held.CanBePressed) this.System.InvokeOnElementSecondaryPressed(held); - } else if (this.Input.TouchState.Count <= 0) { + } else if (this.Input.ViewportTouchState.Count <= 0) { this.SetTouchedElement(null); } else { - foreach (var location in this.Input.TouchState) { + foreach (var location in this.Input.ViewportTouchState) { var element = this.GetElementUnderPos(location.Position); if (location.State == TouchLocationState.Pressed) { // start touching an element if we just touched down on it diff --git a/MLEM/Input/InputHandler.cs b/MLEM/Input/InputHandler.cs index 48ddddc..2761bcf 100644 --- a/MLEM/Input/InputHandler.cs +++ b/MLEM/Input/InputHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; using MLEM.Misc; @@ -16,7 +17,7 @@ namespace MLEM.Input { /// /// Contains all of the gestures that have finished during the last update call. - /// To easily query these gestures, use + /// To easily query these gestures, use or . /// public readonly ReadOnlyCollection Gestures; @@ -79,6 +80,14 @@ namespace MLEM.Input { /// public TouchCollection TouchState { get; private set; } /// + /// Contains the , but with the taken into account. + /// + public IList LastViewportTouchState { get; private set; } + /// + /// Contains the , but with the taken into account. + /// + public IList ViewportTouchState { get; private set; } + /// /// Contains the amount of gamepads that are currently connected. /// This field is automatically updated in /// @@ -92,13 +101,21 @@ namespace MLEM.Input { /// public MouseState MouseState { get; private set; } /// + /// Contains the position of the mouse from the last update call, extracted from + /// + public Point LastMousePosition => this.LastMouseState.Position; + /// + /// Contains the , but with the taken into account. + /// + public Point LastViewportMousePosition => this.LastMousePosition + this.ViewportOffset; + /// /// Contains the current position of the mouse, extracted from /// public Point MousePosition => this.MouseState.Position; /// - /// Contains the position of the mouse from the last update call, extracted from + /// Contains the , but with the taken into account. /// - public Point LastMousePosition => this.LastMouseState.Position; + public Point ViewportMousePosition => this.MousePosition + this.ViewportOffset; /// /// Contains the current scroll wheel value, in increments of 120 /// @@ -125,6 +142,7 @@ namespace MLEM.Input { private readonly List inputsDownAccum = new List(); private readonly List gestures = new List(); + private Point ViewportOffset => new Point(-this.Game.GraphicsDevice.Viewport.X, -this.Game.GraphicsDevice.Viewport.Y); private DateTime heldKeyStart; private DateTime lastKeyRepeat; private bool triggerKeyRepeat; @@ -267,7 +285,17 @@ namespace MLEM.Input { if (this.HandleTouch) { this.LastTouchState = this.TouchState; + this.LastViewportTouchState = this.ViewportTouchState; + this.TouchState = active ? TouchPanel.GetState() : default; + this.ViewportTouchState = this.TouchState; + if (this.ViewportTouchState.Count > 0 && this.ViewportOffset != Point.Zero) { + for (var i = 0; i < this.ViewportTouchState.Count; i++) { + var touch = this.ViewportTouchState[i]; + touch.TryGetPreviousLocation(out var previous); + this.ViewportTouchState[i] = new TouchLocation(touch.Id, touch.State, touch.Position + this.ViewportOffset.ToVector2(), previous.State, previous.Position + this.ViewportOffset.ToVector2()); + } + } this.gestures.Clear(); while (active && TouchPanel.IsGestureAvailable) @@ -512,6 +540,22 @@ namespace MLEM.Input { return false; } + /// + /// Queries for a gesture of the given type that finished during the current update call. + /// Unlike , the return value of this method takes the into account. + /// + /// The type of gesture to query for + /// The resulting gesture sample with the taken into account, or default if there isn't one + /// True if a gesture of the type was found, otherwise false + public bool GetViewportGesture(GestureType type, out GestureSample sample) { + if (this.GetGesture(type, out var original)) { + sample = new GestureSample(original.GestureType, original.Timestamp, original.Position + this.ViewportOffset.ToVector2(), original.Position2 + this.ViewportOffset.ToVector2(), original.Delta, original.Delta2); + return true; + } + sample = default; + return false; + } + /// /// Returns if a given control of any kind is down. /// This is a helper function that can be passed a , or . diff --git a/Sandbox/GameImpl.cs b/Sandbox/GameImpl.cs index 42e6835..bf73b71 100644 --- a/Sandbox/GameImpl.cs +++ b/Sandbox/GameImpl.cs @@ -22,6 +22,7 @@ using MLEM.Ui; using MLEM.Ui.Elements; using MLEM.Ui.Style; using MonoGame.Extended.Tiled; +using MonoGame.Extended.ViewportAdapters; namespace Sandbox { public class GameImpl : MlemGame { @@ -298,6 +299,11 @@ namespace Sandbox { } this.SpriteBatch.End(); }; + + var viewport = new BoxingViewportAdapter(this.Window, this.GraphicsDevice, 1280, 720); + var newPanel = new Panel(Anchor.TopLeft, new Vector2(200, 100), new Vector2(10, 10)); + newPanel.AddChild(new Button(Anchor.TopLeft, new Vector2(100, 20), "Text", "Tooltip text")); + this.UiSystem.Add("Panel", newPanel); } protected override void DoUpdate(GameTime gameTime) { @@ -307,7 +313,7 @@ namespace Sandbox { var delta = this.InputHandler.ScrollWheel - this.InputHandler.LastScrollWheel; if (delta != 0) { - this.camera.Zoom(0.1F * Math.Sign(delta), this.InputHandler.MousePosition.ToVector2()); + this.camera.Zoom(0.1F * Math.Sign(delta), this.InputHandler.ViewportMousePosition.ToVector2()); } /*if (Input.InputsDown.Length > 0)