diff --git a/src/app/ui/editor/drawing_state.cpp b/src/app/ui/editor/drawing_state.cpp index 2cb376b81..c666ebf04 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 { @@ -113,6 +116,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); @@ -202,7 +208,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. @@ -251,12 +257,21 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg) // Update velocity sensor. m_velocity.updateWithDisplayPoint(msg->position()); + // Update pointer with new mouse position m_lastPointer = tools::Pointer(gfx::Point(m_delayedMouseMove.spritePos()), m_velocity.velocity(), button_from_msg(msg), msg->pointerType(), msg->pressure()); + // Indicate that we've received a real mouse movement event here + // (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 // movement. @@ -367,13 +382,23 @@ bool DrawingState::getGridBounds(Editor* editor, gfx::Rect& gridBounds) void DrawingState::handleMouseMovement() { - m_mouseMoveReceived = true; - // Notify mouse movement to the tool ASSERT(m_toolLoopManager); 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 ce92dfd43..18a3bed5d 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 @@ -60,6 +61,7 @@ namespace app { private: void handleMouseMovement(); + bool canInterpretMouseMovementAsJustOneClick(); bool canExecuteCommands(); void onBeforeCommandExecution(CommandExecutionEvent& ev); void destroyLoopIfCanceled(Editor* editor); @@ -79,10 +81,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.