diff --git a/Assets/Impossible Odds/MouseEvents/Documentation.pdf b/Assets/Impossible Odds/MouseEvents/Documentation.pdf index f209919..47263d2 100644 Binary files a/Assets/Impossible Odds/MouseEvents/Documentation.pdf and b/Assets/Impossible Odds/MouseEvents/Documentation.pdf differ diff --git a/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEvent.cs b/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEvent.cs index b4c0484..b82a0d1 100644 --- a/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEvent.cs +++ b/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEvent.cs @@ -160,7 +160,7 @@ public bool IsDrag public bool IsSingleClick => buttonState == MouseButtonEventType.SingleClick; /// - /// Is this event a double-click event? + /// Is this event a double click event? /// public bool IsDoubleClick => buttonState == MouseButtonEventType.DoubleClick; diff --git a/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEventType.cs b/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEventType.cs index 649b424..f7e68d9 100644 --- a/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEventType.cs +++ b/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonEventType.cs @@ -3,36 +3,40 @@ /// /// States a mouse button can reside in. /// - public enum MouseButtonEventType : int + public enum MouseButtonEventType { /// /// No current event of interest has happened. /// - None = 0, + None, /// /// When the mouse button performed a single click, but is waiting for a follow-up action. /// - SingleClickPending = 1, + SingleClickPending, /// /// When the mouse button performed a single click. /// - SingleClick = 2, + SingleClick, /// /// When the mouse button performed a double click. /// - DoubleClick = 3, + DoubleClick, + /// + /// When the mouse button is being dragged, but the threshold for a drag action has not been met yet. + /// + DragPending, /// /// When the mouse button is being used to initiate a drag action. /// - DragStart = 4, + DragStart, /// /// When the mouse button is being used for dragging. /// - Dragging = 5, + Dragging, /// /// When the mouse button completed a drag action. /// - DragComplete = 6, + DragComplete, Idle = None } diff --git a/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonStateTracker.cs b/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonStateTracker.cs index 9c973c9..d58791b 100644 --- a/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonStateTracker.cs +++ b/Assets/Impossible Odds/MouseEvents/Runtime/MouseButtonStateTracker.cs @@ -8,7 +8,8 @@ public class MouseButtonStateTracker public event Action onStateUpdated; private readonly MouseButton button; - private readonly Func multiClickTimeThreshold = null; + private readonly Func multiClickTimeThreshold; + private readonly Func dragDistanceThreshold; private MouseButtonEventType state = MouseButtonEventType.None; private EventModifiers lastModifiers = EventModifiers.None; @@ -42,10 +43,11 @@ public class MouseButtonStateTracker /// public Vector2 MousePosition => mousePosition; - public MouseButtonStateTracker(MouseButton button, Func multiClickTimeThreshold) + public MouseButtonStateTracker(MouseButton button, Func multiClickTimeThreshold, Func dragDistanceThreshold) { this.button = button; this.multiClickTimeThreshold = multiClickTimeThreshold; + this.dragDistanceThreshold = dragDistanceThreshold; ClearState(); } @@ -95,7 +97,7 @@ internal void ProcessEvent(Event mouseEvent) internal void Suspend(Event mouseEvent) { // If the button doesn't match, don't bother. - if ((MouseButton)mouseEvent.button != (MouseButton)button) + if (mouseEvent.button != button) { return; } @@ -128,6 +130,7 @@ private void ProcessMouseUpEvent(Event mouseEvent) switch (state) { case MouseButtonEventType.None: + case MouseButtonEventType.DragPending: state = MouseButtonEventType.SingleClickPending; lastModifiers = mouseEvent.modifiers; mousePosition = mouseEvent.mousePosition; @@ -163,19 +166,29 @@ private void ProcessMouseDragEvent(Event mouseEvent) return; } - // If a single click was still pending, - // then perform the click before getting to the drag operation. + // If a single click was still pending, then perform the click before getting to the drag operation. if (state == MouseButtonEventType.SingleClickPending) { state = MouseButtonEventType.SingleClick; lastClickAction = 0f; } - else if ((state != MouseButtonEventType.DragStart) && (state != MouseButtonEventType.Dragging)) + else if ((state != MouseButtonEventType.DragPending) && + (state != MouseButtonEventType.DragStart) && + (state != MouseButtonEventType.Dragging)) { - state = MouseButtonEventType.DragStart; // The first of such events initiates the drag start. + state = MouseButtonEventType.DragPending; // The first of such events declares that drag is pending, until the drag threshold is exceeded. dragStartPosition = mouseEvent.mousePosition; mousePosition = mouseEvent.mousePosition; } + else if (state == MouseButtonEventType.DragPending) + { + if (Vector2.Distance(mouseEvent.mousePosition, dragStartPosition) > dragDistanceThreshold()) + { + state = MouseButtonEventType.DragStart; // The first of such events initiates the drag start. + } + + mousePosition = mouseEvent.mousePosition; + } else { state = MouseButtonEventType.Dragging; diff --git a/Assets/Impossible Odds/MouseEvents/Runtime/MouseEventMonitor.cs b/Assets/Impossible Odds/MouseEvents/Runtime/MouseEventMonitor.cs index 00a6d39..69fed98 100644 --- a/Assets/Impossible Odds/MouseEvents/Runtime/MouseEventMonitor.cs +++ b/Assets/Impossible Odds/MouseEvents/Runtime/MouseEventMonitor.cs @@ -43,6 +43,8 @@ public class MouseEventMonitor : MonoBehaviour private List monitoredButtons = new List(); [SerializeField, Tooltip("How long (in seconds) should it wait for registering a multi-click event?"), Range(0.1f, 1f)] private float multiClickTimeThreshold = 0.3f; + [SerializeField, Tooltip("How far can the mouse move (in pixels) before it is considered a drag movement?"), Range(0, 50)] + private float dragDistanceThreshold = 0f; [SerializeField, Tooltip("When the cursor is over UI, should the mouse event monitor suspend operations?")] private bool suspendWhenOverUI; [SerializeField, Tooltip("Should the cursor coordinates be reported in pixel coordinates (as reported by Input.mousePosition), or in GUI coordinates (as reported by OnGUI events)?")] @@ -74,6 +76,23 @@ public float MultiClickTimeThreshold } } + /// + /// How far can the mouse cursor be dragged (in pixels) before it registers the start of a drag operation? + /// + public float DragDistanceThreshold + { + get => dragDistanceThreshold; + set + { + if (value < 0f) + { + throw new ArgumentOutOfRangeException(nameof(value), "The drag distance threshold should be greater than or equal to 0."); + } + + dragDistanceThreshold = value; + } + } + /// /// When the cursor is over UI, should the mouse event monitor suspend operations? /// @@ -160,12 +179,17 @@ public void StartMonitoring(MouseButton mouseButton) monitoredButtons.Add(mouseButton); - if (enabled) + if (!enabled) { - MouseButtonStateTracker stateTracker = new MouseButtonStateTracker(mouseButton, () => multiClickTimeThreshold); - stateTrackers[mouseButton] = stateTracker; - stateTracker.onStateUpdated += OnMouseKeyStateUpdate; + return; } + + MouseButtonStateTracker stateTracker = new MouseButtonStateTracker( + mouseButton, + () => multiClickTimeThreshold, + () => dragDistanceThreshold); + stateTrackers[mouseButton] = stateTracker; + stateTracker.onStateUpdated += OnMouseKeyStateUpdate; } /// @@ -185,7 +209,10 @@ private void OnEnable() { foreach (MouseButton key in monitoredButtons) { - MouseButtonStateTracker state = new MouseButtonStateTracker(key, () => multiClickTimeThreshold); + MouseButtonStateTracker state = new MouseButtonStateTracker( + key, + () => multiClickTimeThreshold, + () => dragDistanceThreshold); stateTrackers[key] = state; state.onStateUpdated += OnMouseKeyStateUpdate; } diff --git a/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.cs b/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.cs index 52e41e6..22071ab 100644 --- a/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.cs +++ b/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.cs @@ -87,6 +87,11 @@ private void OnMouseEvent(MouseButtonEvent mouseEvent) break; } + if (display != null) + { + Debug.Log(display.text); + } + // The dragging event is not send out each frame if there's not mouse movement. // So don't clear it when this state is detected. if (!mouseEvent.IsDragging && (display != null)) diff --git a/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.unity b/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.unity index 5ce585f..037e26e 100644 --- a/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.unity +++ b/Assets/Impossible Odds/MouseEvents/Samples/MouseEventsDemo.unity @@ -1747,6 +1747,7 @@ MonoBehaviour: m_EditorClassIdentifier: monitoredButtons: 000000000200000001000000 multiClickTimeThreshold: 0.3 + dragDistanceThreshold: 0 suspendWhenOverUI: 1 cursorPositionInPixelCoordinates: 1 --- !u!1 &1158115896 @@ -1973,7 +1974,7 @@ Camera: m_Enabled: 1 serializedVersion: 2 m_ClearFlags: 2 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_BackGroundColor: {r: 0.35, g: 0.35, b: 0.35, a: 1} m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 diff --git a/Assets/Impossible Odds/MouseEvents/package.json b/Assets/Impossible Odds/MouseEvents/package.json index c1b3424..138d962 100644 --- a/Assets/Impossible Odds/MouseEvents/package.json +++ b/Assets/Impossible Odds/MouseEvents/package.json @@ -1,6 +1,6 @@ { "name": "com.impossibleodds.mousevents", - "version": "1.2.0", + "version": "1.3.0", "displayName": "Impossible Odds - Mouse Events", "unity": "2019.4", "documentationUrl": "https://github.com/juniordiscart/ImpossibleOdds-MouseEvents/blob/master/README.md", diff --git a/Docs/Images/Demo_MouseEventMonitor.png b/Docs/Images/Demo_MouseEventMonitor.png index 7d17dd3..f663c15 100644 Binary files a/Docs/Images/Demo_MouseEventMonitor.png and b/Docs/Images/Demo_MouseEventMonitor.png differ diff --git a/README.md b/README.md index e6333fe..e0a742e 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Additionally, you can also query the state of a specific mouse button on the mou ## Advanced -The main point of entry is the `MouseEventMonitor` script. It requires to be placed on a game object in your scene and will monitor the mouse inputs you set it up to be: left, right and/or middle mouse buttons. Apart from which mouse buttons it should monitor, you can also adjust the time threshold for registering multi-clicks (double click). Unity does not allow to transparently distinguish between single click and double click without always invoking the single click event. This multi-click time threshold is the time limit it will delay a single click event while listening in for a secondary click or other event. +The main point of entry is the `MouseEventMonitor` script. It requires to be placed on a game object in your scene and will monitor the mouse inputs you set it up to be: left, right and/or middle mouse buttons. Apart from which mouse buttons it should monitor, you can also adjust the time threshold for registering multi-clicks (double click). Unity does not allow to transparently distinguish between single click and double click without always invoking the single click event. This multi-click time threshold is the time limit it will delay a single click event while listening in for a secondary click or other event. Additionally, you can also set a drag distance threshold for when you want to let small or accidental drag operations to still count as a click event instead. You can listen for events of the registered mouse buttons as well as querying the current state of a particular button using the `CurrentEvent` method. When a new mouse button requires monitoring, you can add it using the `StartMonitoring` method. Conversely, you can also stop monitoring events for a specific mouse button by calling the `StopMonitoring` method. @@ -126,6 +126,11 @@ This package is provided under the [MIT][License] license. * Fixed a runtime issue where clicks would be invalidated due to using Unity's built-in `Event` struct. This structure would put the event type to `Ignore` while it was actually a mouse button up/down event. +### v1.3.0 + +* Added a `DragDistanceThreshold` to the the `MouseEventMonitor` to allow small/accidental drag operations to still register as a click. +* Updated the sample scene to also log the performed actions to the console. + [License]: ./LICENSE.md [Changelog]: ./CHANGELOG.md [Logo]: ./Docs/Images/ImpossibleOddsLogo.png