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
This commit is contained in:
David Capello 2022-01-12 23:19:51 -03:00
parent 87e8b45dc1
commit f3fd0de3d0
2 changed files with 30 additions and 4 deletions

View File

@ -36,6 +36,9 @@
#include "app/ui/main_window.h"
#include "app/ui/timeline/timeline.h"
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
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

View File

@ -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 <memory>
@ -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<tools::ToolLoopManager> 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.