mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 13:21:34 +00:00
Improve click behavior to cancel Text box tool (#4692)
We've moved the logic to interpret "a single click" from DrawingState to DelayedMouseMove.
This commit is contained in:
parent
5b88ea8532
commit
8b7370a77a
@ -11,9 +11,14 @@
|
||||
#include "app/ui/editor/delayed_mouse_move.h"
|
||||
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "base/time.h"
|
||||
#include "ui/message.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
static const gfx::Point kNoPosReceived(std::numeric_limits<int>::min(),
|
||||
std::numeric_limits<int>::min());
|
||||
|
||||
DelayedMouseMove::DelayedMouseMove(DelayedMouseMoveDelegate* delegate,
|
||||
Editor* editor,
|
||||
const int interval)
|
||||
@ -22,11 +27,20 @@ DelayedMouseMove::DelayedMouseMove(DelayedMouseMoveDelegate* delegate,
|
||||
, m_timer(interval)
|
||||
, m_spritePos(std::numeric_limits<float>::min(),
|
||||
std::numeric_limits<float>::min())
|
||||
, m_mouseMoveReceived(false)
|
||||
, m_mouseDownPos(kNoPosReceived)
|
||||
, m_mouseDownTime(base::current_tick())
|
||||
{
|
||||
ASSERT(m_delegate);
|
||||
m_timer.Tick.connect([this] { commitMouseMove(); });
|
||||
}
|
||||
|
||||
void DelayedMouseMove::reset()
|
||||
{
|
||||
m_mouseMoveReceived = false;
|
||||
m_mouseDownPos = kNoPosReceived;
|
||||
}
|
||||
|
||||
void DelayedMouseMove::initSpritePos(const gfx::PointF& pos)
|
||||
{
|
||||
m_spritePos = pos;
|
||||
@ -34,11 +48,23 @@ void DelayedMouseMove::initSpritePos(const gfx::PointF& pos)
|
||||
|
||||
void DelayedMouseMove::onMouseDown(const ui::MouseMessage* msg)
|
||||
{
|
||||
if (m_mouseDownPos == kNoPosReceived) {
|
||||
m_mouseDownPos = msg->position();
|
||||
}
|
||||
|
||||
updateSpritePos(msg);
|
||||
}
|
||||
|
||||
bool DelayedMouseMove::onMouseMove(const ui::MouseMessage* msg)
|
||||
{
|
||||
// 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));
|
||||
|
||||
if (!updateSpritePos(msg))
|
||||
return false;
|
||||
|
||||
@ -66,6 +92,15 @@ void DelayedMouseMove::stopTimer()
|
||||
m_timer.stop();
|
||||
}
|
||||
|
||||
bool DelayedMouseMove::canInterpretMouseMovementAsJustOneClick() const
|
||||
{
|
||||
return
|
||||
!m_mouseMoveReceived ||
|
||||
(m_mouseMaxDelta.x < 4 &&
|
||||
m_mouseMaxDelta.y < 4 &&
|
||||
(base::current_tick() - m_mouseDownTime < 250));
|
||||
}
|
||||
|
||||
void DelayedMouseMove::commitMouseMove()
|
||||
{
|
||||
if (m_timer.isRunning())
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 Igara Studio S.A.
|
||||
// Copyright (C) 2022-2024 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -41,6 +41,10 @@ namespace app {
|
||||
Editor* editor,
|
||||
const int interval);
|
||||
|
||||
// Resets internals to receive an onMouseDown() again and
|
||||
// interpret a "one click" correctly again.
|
||||
void reset();
|
||||
|
||||
// In case the event wasn't started with onMouseDown() we can
|
||||
// initialize the sprite position directly (e.g. starting a line
|
||||
// from last painted point with Shift+click with Pencil tool).
|
||||
@ -57,6 +61,11 @@ namespace app {
|
||||
|
||||
const gfx::PointF& spritePos() const;
|
||||
|
||||
// If the user clicked (pressed and released the mouse button) in
|
||||
// less than 250 milliseconds in "the same place" (inside a 4
|
||||
// pixels rectangle actually, to detect stylus shake).
|
||||
bool canInterpretMouseMovementAsJustOneClick() const;
|
||||
|
||||
private:
|
||||
void commitMouseMove();
|
||||
bool updateSpritePos(const ui::MouseMessage* msg);
|
||||
@ -65,6 +74,14 @@ namespace app {
|
||||
Editor* m_editor;
|
||||
ui::Timer m_timer;
|
||||
|
||||
// These fields are used to detect a single click, e.g. in a
|
||||
// selection tool, a single click deselect (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;
|
||||
|
||||
// Position of the mouse in the canvas to avoid redrawing when the
|
||||
// mouse position changes (only we redraw when the canvas position
|
||||
// changes).
|
||||
|
@ -68,7 +68,6 @@ DrawingState::DrawingState(Editor* editor,
|
||||
get_delay_interval_for_tool_loop(toolLoop))
|
||||
, m_toolLoop(toolLoop)
|
||||
, m_toolLoopManager(new tools::ToolLoopManager(toolLoop))
|
||||
, m_mouseMoveReceived(false)
|
||||
, m_mousePressedReceived(false)
|
||||
, m_processScrollChange(true)
|
||||
{
|
||||
@ -117,9 +116,6 @@ void DrawingState::initToolLoop(Editor* editor,
|
||||
|
||||
m_velocity.reset();
|
||||
m_lastPointer = pointer;
|
||||
m_mouseDownPos = (msg ? msg->position():
|
||||
editor->editorToScreen(pointer.point()));
|
||||
m_mouseDownTime = base::current_tick();
|
||||
|
||||
m_toolLoopManager->prepareLoop(pointer);
|
||||
m_toolLoopManager->pressButton(pointer);
|
||||
@ -129,7 +125,7 @@ void DrawingState::initToolLoop(Editor* editor,
|
||||
editor->captureMouse();
|
||||
}
|
||||
|
||||
void DrawingState::disableMouseStabilizer()
|
||||
void DrawingState::disableMouseStabilizer()
|
||||
{
|
||||
ASSERT(m_toolLoopManager);
|
||||
m_toolLoopManager->disableMouseStabilizer();
|
||||
@ -216,7 +212,7 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
// one click).
|
||||
if (!m_toolLoop->getInk()->isSelection() ||
|
||||
m_toolLoop->getController()->isOnePoint() ||
|
||||
!canInterpretMouseMovementAsJustOneClick() ||
|
||||
!m_delayedMouseMove.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.
|
||||
@ -271,14 +267,6 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* 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.
|
||||
@ -395,18 +383,6 @@ 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
|
||||
|
@ -72,7 +72,6 @@ namespace app {
|
||||
|
||||
private:
|
||||
void handleMouseMovement();
|
||||
bool canInterpretMouseMovementAsJustOneClick();
|
||||
bool canExecuteCommands();
|
||||
void onBeforeCommandExecution(CommandExecutionEvent& ev);
|
||||
void destroyLoopIfCanceled(Editor* editor);
|
||||
@ -92,14 +91,6 @@ namespace app {
|
||||
// Tool-loop manager
|
||||
std::unique_ptr<tools::ToolLoopManager> m_toolLoopManager;
|
||||
|
||||
// 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.
|
||||
tools::Pointer m_lastPointer;
|
||||
|
@ -308,12 +308,14 @@ WritingTextState::~WritingTextState()
|
||||
|
||||
bool WritingTextState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
if (!editor->hasCapture())
|
||||
m_delayedMouseMove.reset();
|
||||
|
||||
m_delayedMouseMove.onMouseDown(msg);
|
||||
m_hit = calcHit(editor, msg->position());
|
||||
|
||||
if (msg->left()) {
|
||||
if (m_hit == Hit::Edges) {
|
||||
m_mouseMoveReceived = false;
|
||||
m_movingBounds = true;
|
||||
m_cursorStart = editor->screenToEditorF(msg->position());
|
||||
m_boundsOrigin = m_bounds.origin();
|
||||
@ -322,6 +324,8 @@ bool WritingTextState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
// On mouse down with the left button, we just drop the text
|
||||
// directly when we click outside the edges.
|
||||
drop();
|
||||
return true;
|
||||
}
|
||||
@ -338,12 +342,12 @@ bool WritingTextState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
m_delayedMouseMove.onMouseUp(msg);
|
||||
|
||||
const bool result = StandbyState::onMouseUp(editor, msg);
|
||||
if (m_movingBounds) {
|
||||
if (m_movingBounds)
|
||||
m_movingBounds = false;
|
||||
|
||||
// Drop if the user just clicked (so other text box is created)
|
||||
if (!m_mouseMoveReceived)
|
||||
drop();
|
||||
// Drop if the user just clicked (so other text box is created)
|
||||
if (m_delayedMouseMove.canInterpretMouseMovementAsJustOneClick()) {
|
||||
drop();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -367,8 +371,6 @@ void WritingTextState::onCommitMouseMove(Editor* editor,
|
||||
if (delta.x == 0 && delta.y == 0)
|
||||
return;
|
||||
|
||||
m_mouseMoveReceived = true;
|
||||
|
||||
m_bounds.setOrigin(gfx::Point(delta + m_boundsOrigin));
|
||||
m_entry->setExtraCelBounds(m_bounds);
|
||||
m_entry->setBounds(calcEntryBounds());
|
||||
|
Loading…
x
Reference in New Issue
Block a user