From f3fd0de3d006f496ce8965c2a9a9ed278940ca84 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 12 Jan 2022 23:19:51 -0300 Subject: [PATCH] Improve detection of one click when using a stylus We can interpret a quick mouse down/up events (e.g. less than 250 milliseconds) as a simple click if the mouse doesn't move too much in the middle of both events (e.g. a tiny rectangle of 7x7 pixels). Some discussion about this: https://community.aseprite.org/t/12491/10 --- src/app/ui/editor/drawing_state.cpp | 23 ++++++++++++++++++++++- src/app/ui/editor/drawing_state.h | 11 ++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/app/ui/editor/drawing_state.cpp b/src/app/ui/editor/drawing_state.cpp index 7b25c5c28..12a36a863 100644 --- a/src/app/ui/editor/drawing_state.cpp +++ b/src/app/ui/editor/drawing_state.cpp @@ -36,6 +36,9 @@ #include "app/ui/main_window.h" #include "app/ui/timeline/timeline.h" +#include +#include +#include #include namespace app { @@ -104,6 +107,9 @@ void DrawingState::initToolLoop(Editor* editor, m_velocity.reset(); m_lastPointer = pointer; + m_mouseDownPos = msg->position(); + m_mouseDownTime = base::current_tick(); + m_toolLoopManager->prepareLoop(pointer); m_toolLoopManager->pressButton(pointer); @@ -193,7 +199,7 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg) // one click). if (!m_toolLoop->getInk()->isSelection() || m_toolLoop->getController()->isOnePoint() || - m_mouseMoveReceived || + !canInterpretMouseMovementAsJustOneClick() || // In case of double-click (to select tiles) we don't want to // deselect if the mouse is not moved. In this case the tile // will be selected anyway even if the mouse is not moved. @@ -253,6 +259,9 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg) // (used in the Rectangular Marquee to deselect when we just do a // simple click without moving the mouse). m_mouseMoveReceived = true; + gfx::Point delta = (msg->position() - m_mouseDownPos); + m_mouseMaxDelta.x = std::max(m_mouseMaxDelta.x, std::abs(delta.x)); + m_mouseMaxDelta.y = std::max(m_mouseMaxDelta.y, std::abs(delta.y)); // Use DelayedMouseMove for tools like line, rectangle, etc. (that // use the only the last mouse position) to filter out rapid mouse @@ -358,6 +367,18 @@ void DrawingState::handleMouseMovement() m_toolLoopManager->movement(m_lastPointer); } +bool DrawingState::canInterpretMouseMovementAsJustOneClick() +{ + // If the user clicked (pressed and released the mouse button) in + // less than 250 milliseconds in "the same place" (inside a 7 pixels + // rectangle actually, to detect stylus shake). + return + !m_mouseMoveReceived || + (m_mouseMaxDelta.x < 4 && + m_mouseMaxDelta.y < 4 && + (base::current_tick() - m_mouseDownTime < 250)); +} + bool DrawingState::canExecuteCommands() { // Returning true here means that the user can trigger commands with diff --git a/src/app/ui/editor/drawing_state.h b/src/app/ui/editor/drawing_state.h index 2ba1cff4b..9db7c8ce9 100644 --- a/src/app/ui/editor/drawing_state.h +++ b/src/app/ui/editor/drawing_state.h @@ -13,6 +13,7 @@ #include "app/tools/velocity.h" #include "app/ui/editor/delayed_mouse_move.h" #include "app/ui/editor/standby_state.h" +#include "base/time.h" #include "obs/connection.h" #include @@ -58,6 +59,7 @@ namespace app { private: void handleMouseMovement(); + bool canInterpretMouseMovementAsJustOneClick(); bool canExecuteCommands(); void onBeforeCommandExecution(CommandExecutionEvent& ev); void destroyLoopIfCanceled(Editor* editor); @@ -77,10 +79,13 @@ namespace app { // Tool-loop manager std::unique_ptr m_toolLoopManager; - // True if at least we've received a onMouseMove(). It's used to - // cancel selection tool (deselect) when the user click (press and - // release the mouse button in the same location). + // These fields are used to detect a selection tool cancelation + // (deselect command) when the user just click (press and release + // the mouse button in the "same location" approximately). bool m_mouseMoveReceived; + gfx::Point m_mouseMaxDelta; + gfx::Point m_mouseDownPos; + base::tick_t m_mouseDownTime; // Stores the last mouse pointer, used to re-use the latest mouse // button when onScrollChange() event is received.