mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 04:19:12 +00:00
Group several mouse movement in one on the Editor
Mainly for Line-like tools (which require the last mouse position only). Related to #3119, possible fix for several performance issues on Linux mainly.
This commit is contained in:
parent
bc8e1b36eb
commit
26c1a94b83
@ -1,5 +1,5 @@
|
||||
# Aseprite
|
||||
# Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
# Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
# Copyright (C) 2001-2018 David Capello
|
||||
|
||||
# Generate a ui::Widget for each widget in a XML file
|
||||
@ -329,6 +329,7 @@ if(ENABLE_UI)
|
||||
ui/drop_down_button.cpp
|
||||
ui/dynamics_popup.cpp
|
||||
ui/editor/brush_preview.cpp
|
||||
ui/editor/delayed_mouse_move.cpp
|
||||
ui/editor/drawing_state.cpp
|
||||
ui/editor/editor.cpp
|
||||
ui/editor/editor_observers.cpp
|
||||
|
93
src/app/ui/editor/delayed_mouse_move.cpp
Normal file
93
src/app/ui/editor/delayed_mouse_move.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/ui/editor/delayed_mouse_move.h"
|
||||
|
||||
#include "app/ui/editor/editor.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
DelayedMouseMove::DelayedMouseMove(DelayedMouseMoveDelegate* delegate,
|
||||
Editor* editor,
|
||||
const int interval)
|
||||
: m_delegate(delegate)
|
||||
, m_editor(editor)
|
||||
, m_timer(interval)
|
||||
, m_spritePos(std::numeric_limits<int>::min(),
|
||||
std::numeric_limits<int>::min())
|
||||
{
|
||||
ASSERT(m_delegate);
|
||||
m_timer.Tick.connect([this] { commitMouseMove(); });
|
||||
}
|
||||
|
||||
void DelayedMouseMove::onMouseDown(const ui::MouseMessage* msg)
|
||||
{
|
||||
updateSpritePos(msg);
|
||||
}
|
||||
|
||||
bool DelayedMouseMove::onMouseMove(const ui::MouseMessage* msg)
|
||||
{
|
||||
if (!updateSpritePos(msg))
|
||||
return false;
|
||||
|
||||
if (!m_timer.isRunning()) {
|
||||
if (m_timer.interval() > 0) {
|
||||
m_timer.start();
|
||||
}
|
||||
else {
|
||||
// Commit immediately
|
||||
commitMouseMove();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DelayedMouseMove::onMouseUp(const ui::MouseMessage* msg)
|
||||
{
|
||||
updateSpritePos(msg);
|
||||
commitMouseMove();
|
||||
}
|
||||
|
||||
void DelayedMouseMove::commitMouseMove()
|
||||
{
|
||||
if (m_timer.isRunning())
|
||||
m_timer.stop();
|
||||
|
||||
m_delegate->onCommitMouseMove(m_editor, spritePos());
|
||||
}
|
||||
|
||||
const gfx::Point& DelayedMouseMove::spritePos() const
|
||||
{
|
||||
ASSERT(m_spritePos.x != std::numeric_limits<int>::min() &&
|
||||
m_spritePos.y != std::numeric_limits<int>::min());
|
||||
return m_spritePos;
|
||||
}
|
||||
|
||||
bool DelayedMouseMove::updateSpritePos(const ui::MouseMessage* msg)
|
||||
{
|
||||
// The autoScroll() function controls the "infinite scroll" when we
|
||||
// touch the viewport borders.
|
||||
const gfx::Point mousePos = m_editor->autoScroll(msg, AutoScroll::MouseDir);
|
||||
const gfx::Point spritePos = m_editor->screenToEditor(mousePos);
|
||||
|
||||
// Avoid redrawing everything if the position in the canvas didn't
|
||||
// change.
|
||||
//
|
||||
// TODO Remove this if we add support for anti-aliasing in the
|
||||
// transformations.
|
||||
if (m_spritePos != spritePos) {
|
||||
m_spritePos = spritePos;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace app
|
66
src/app/ui/editor/delayed_mouse_move.h
Normal file
66
src/app/ui/editor/delayed_mouse_move.h
Normal file
@ -0,0 +1,66 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_EDITOR_DELAYED_MOUSE_MOVE_STATE_H_INCLUDED
|
||||
#define APP_UI_EDITOR_DELAYED_MOUSE_MOVE_STATE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/editor/standby_state.h"
|
||||
#include "ui/timer.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace ui {
|
||||
class MouseMessage;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
class Editor;
|
||||
|
||||
class DelayedMouseMoveDelegate {
|
||||
public:
|
||||
virtual ~DelayedMouseMoveDelegate() { }
|
||||
virtual void onCommitMouseMove(Editor* editor,
|
||||
const gfx::Point& spritePos) = 0;
|
||||
};
|
||||
|
||||
// Helper class to group several onMouseMove() calls into one
|
||||
// onCommitMouseMove(). Useful in Linux as mouse movement messages
|
||||
// are queued a high speed (so we can group several mouse messages
|
||||
// for tools that use only the last position and have a heavy
|
||||
// calculation per mouse position, e.g. pixel transformations,
|
||||
// drawing with Line, Rectangle, etc.).
|
||||
class DelayedMouseMove {
|
||||
public:
|
||||
// The "interval" is given in milliseconds, and can be zero if we
|
||||
// want to disable the delay between onMouseMove() -> onCommitMouseMove()
|
||||
DelayedMouseMove(DelayedMouseMoveDelegate* delegate,
|
||||
Editor* editor,
|
||||
const int interval);
|
||||
|
||||
void onMouseDown(const ui::MouseMessage* msg);
|
||||
bool onMouseMove(const ui::MouseMessage* msg);
|
||||
void onMouseUp(const ui::MouseMessage* msg);
|
||||
|
||||
const gfx::Point& spritePos() const;
|
||||
|
||||
private:
|
||||
void commitMouseMove();
|
||||
bool updateSpritePos(const ui::MouseMessage* msg);
|
||||
|
||||
DelayedMouseMoveDelegate* m_delegate;
|
||||
Editor* m_editor;
|
||||
ui::Timer m_timer;
|
||||
|
||||
// Position of the mouse in the canvas to avoid redrawing when the
|
||||
// mouse position changes (only we redraw when the canvas position
|
||||
// changes).
|
||||
gfx::Point m_spritePos;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif // APP_UI_EDITOR_DELAYED_MOUSE_MOVE_STATE_H_INCLUDED
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -42,11 +42,27 @@ namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
static int get_delay_interval_for_tool_loop(tools::ToolLoop* toolLoop)
|
||||
{
|
||||
if (toolLoop->getTracePolicy() == tools::TracePolicy::Last) {
|
||||
// We use the delayed mouse movement for tools like Line,
|
||||
// Rectangle, etc. (tools that use the last mouse position for its
|
||||
// shape, so we can discard intermediate positions).
|
||||
return 5;
|
||||
}
|
||||
else {
|
||||
// Without delay for freehand-like tools
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DrawingState::DrawingState(Editor* editor,
|
||||
tools::ToolLoop* toolLoop,
|
||||
const DrawingType type)
|
||||
: m_editor(editor)
|
||||
, m_type(type)
|
||||
, m_delayedMouseMove(this, editor,
|
||||
get_delay_interval_for_tool_loop(toolLoop))
|
||||
, m_toolLoop(toolLoop)
|
||||
, m_toolLoopManager(new tools::ToolLoopManager(toolLoop))
|
||||
, m_mouseMoveReceived(false)
|
||||
@ -64,8 +80,11 @@ DrawingState::~DrawingState()
|
||||
}
|
||||
|
||||
void DrawingState::initToolLoop(Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const tools::Pointer& pointer)
|
||||
{
|
||||
m_delayedMouseMove.onMouseDown(msg);
|
||||
|
||||
// Prepare preview image (the destination image will be our preview
|
||||
// in the tool-loop time, so we can see what we are drawing)
|
||||
editor->renderEngine().setPreviewImage(
|
||||
@ -121,6 +140,8 @@ bool DrawingState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
m_velocity.velocity());
|
||||
m_lastPointer = pointer;
|
||||
|
||||
m_delayedMouseMove.onMouseDown(msg);
|
||||
|
||||
// Check if this drawing state was started with a Shift+Pencil tool
|
||||
// and now the user pressed the right button to draw the straight
|
||||
// line with the background color.
|
||||
@ -160,6 +181,9 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
ASSERT(m_toolLoopManager != NULL);
|
||||
|
||||
m_lastPointer = pointer_from_msg(editor, msg, m_velocity.velocity());
|
||||
m_delayedMouseMove.onMouseUp(msg);
|
||||
|
||||
// Selection tools with Replace mode are cancelled with a simple click.
|
||||
// ("one point" controller selection tool i.e. the magic wand, and
|
||||
// selection tools with Add or Subtract mode aren't cancelled with
|
||||
@ -173,9 +197,6 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
m_type == DrawingType::SelectTiles ||
|
||||
(editor->getToolLoopModifiers() != tools::ToolLoopModifiers::kReplaceSelection &&
|
||||
editor->getToolLoopModifiers() != tools::ToolLoopModifiers::kIntersectSelection)) {
|
||||
m_lastPointer = pointer_from_msg(editor, msg,
|
||||
m_velocity.velocity());
|
||||
|
||||
// Notify the release of the mouse button to the tool loop
|
||||
// manager. This is the correct way to say "the user finishes the
|
||||
// drawing trace correctly".
|
||||
@ -218,19 +239,28 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
// Update velocity sensor.
|
||||
m_velocity.updateWithScreenPoint(msg->position());
|
||||
|
||||
// The autoScroll() function controls the "infinite scroll" when we
|
||||
// touch the viewport borders.
|
||||
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir);
|
||||
handleMouseMovement(
|
||||
tools::Pointer(editor->screenToEditor(mousePos),
|
||||
m_velocity.velocity(),
|
||||
button_from_msg(msg),
|
||||
msg->pointerType(),
|
||||
msg->pressure()));
|
||||
m_lastPointer = tools::Pointer(m_delayedMouseMove.spritePos(),
|
||||
m_velocity.velocity(),
|
||||
button_from_msg(msg),
|
||||
msg->pointerType(),
|
||||
msg->pressure());
|
||||
|
||||
// Use DelayedMouseMove for tools like line, rectangle, etc. (that
|
||||
// use the only the last mouse position) to filter out rapid mouse
|
||||
// movement.
|
||||
m_delayedMouseMove.onMouseMove(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawingState::onCommitMouseMove(Editor* editor,
|
||||
const gfx::Point& spritePos)
|
||||
{
|
||||
if (m_toolLoop &&
|
||||
!m_toolLoop->isCanceled()) {
|
||||
handleMouseMovement();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawingState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
|
||||
{
|
||||
if (m_toolLoop->getInk()->isEyedropper()) {
|
||||
@ -289,12 +319,12 @@ bool DrawingState::onScrollChange(Editor* editor)
|
||||
// Update velocity sensor.
|
||||
m_velocity.updateWithScreenPoint(mousePos); // TODO add scroll as velocity?
|
||||
|
||||
handleMouseMovement(
|
||||
tools::Pointer(editor->screenToEditor(mousePos),
|
||||
m_velocity.velocity(),
|
||||
m_lastPointer.button(),
|
||||
tools::Pointer::Type::Unknown,
|
||||
0.0f));
|
||||
m_lastPointer = tools::Pointer(editor->screenToEditor(mousePos),
|
||||
m_velocity.velocity(),
|
||||
m_lastPointer.button(),
|
||||
tools::Pointer::Type::Unknown,
|
||||
0.0f);
|
||||
handleMouseMovement();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -312,14 +342,13 @@ void DrawingState::onExposeSpritePixels(const gfx::Region& rgn)
|
||||
m_toolLoop->validateDstImage(rgn);
|
||||
}
|
||||
|
||||
void DrawingState::handleMouseMovement(const tools::Pointer& pointer)
|
||||
void DrawingState::handleMouseMovement()
|
||||
{
|
||||
m_mouseMoveReceived = true;
|
||||
m_lastPointer = pointer;
|
||||
|
||||
// Notify mouse movement to the tool
|
||||
ASSERT(m_toolLoopManager);
|
||||
m_toolLoopManager->movement(pointer);
|
||||
m_toolLoopManager->movement(m_lastPointer);
|
||||
}
|
||||
|
||||
bool DrawingState::canExecuteCommands()
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "app/tools/pointer.h"
|
||||
#include "app/tools/velocity.h"
|
||||
#include "app/ui/editor/delayed_mouse_move.h"
|
||||
#include "app/ui/editor/standby_state.h"
|
||||
#include "obs/connection.h"
|
||||
#include <memory>
|
||||
@ -23,7 +24,8 @@ namespace app {
|
||||
|
||||
class CommandExecutionEvent;
|
||||
|
||||
class DrawingState : public StandbyState {
|
||||
class DrawingState : public StandbyState
|
||||
, DelayedMouseMoveDelegate {
|
||||
public:
|
||||
DrawingState(Editor* editor,
|
||||
tools::ToolLoop* loop,
|
||||
@ -45,6 +47,7 @@ namespace app {
|
||||
virtual bool requireBrushPreview() override { return false; }
|
||||
|
||||
void initToolLoop(Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const tools::Pointer& pointer);
|
||||
|
||||
// Used to send a movement() to the ToolLoopManager when
|
||||
@ -54,14 +57,19 @@ namespace app {
|
||||
void notifyToolLoopModifiersChange(Editor* editor);
|
||||
|
||||
private:
|
||||
void handleMouseMovement(const tools::Pointer& pointer);
|
||||
void handleMouseMovement();
|
||||
bool canExecuteCommands();
|
||||
void onBeforeCommandExecution(CommandExecutionEvent& ev);
|
||||
void destroyLoopIfCanceled(Editor* editor);
|
||||
void destroyLoop(Editor* editor);
|
||||
|
||||
// DelayedMouseMoveDelegate impl
|
||||
void onCommitMouseMove(Editor* editor,
|
||||
const gfx::Point& spritePos) override;
|
||||
|
||||
Editor* m_editor;
|
||||
DrawingType m_type;
|
||||
DelayedMouseMove m_delayedMouseMove;
|
||||
|
||||
// The tool-loop.
|
||||
std::unique_ptr<tools::ToolLoop> m_toolLoop;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -1300,7 +1300,8 @@ void Editor::flashCurrentLayer()
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir)
|
||||
gfx::Point Editor::autoScroll(const ui::MouseMessage* msg,
|
||||
const AutoScroll dir)
|
||||
{
|
||||
gfx::Point mousePos = msg->position();
|
||||
if (!Preferences::instance().editor.autoScroll())
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -204,7 +204,8 @@ namespace app {
|
||||
void updateStatusBar();
|
||||
|
||||
// Control scroll when cursor goes out of the editor viewport.
|
||||
gfx::Point autoScroll(ui::MouseMessage* msg, AutoScroll dir);
|
||||
gfx::Point autoScroll(const ui::MouseMessage* msg,
|
||||
const AutoScroll dir);
|
||||
|
||||
tools::Tool* getCurrentEditorTool() const;
|
||||
tools::Ink* getCurrentEditorInk() const;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -52,20 +52,21 @@
|
||||
#include "ui/view.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
MovingPixelsState::MovingPixelsState(Editor* editor, MouseMessage* msg, PixelsMovementPtr pixelsMovement, HandleType handle)
|
||||
MovingPixelsState::MovingPixelsState(Editor* editor,
|
||||
MouseMessage* msg,
|
||||
PixelsMovementPtr pixelsMovement,
|
||||
HandleType handle)
|
||||
: m_pixelsMovement(pixelsMovement)
|
||||
, m_delayedMouseMove(this, editor, 5)
|
||||
, m_editor(editor)
|
||||
, m_observingEditor(false)
|
||||
, m_discarded(false)
|
||||
, m_renderTimer(50)
|
||||
, m_oldSpritePos(std::numeric_limits<int>::min(),
|
||||
std::numeric_limits<int>::min())
|
||||
{
|
||||
// MovingPixelsState needs a selection tool to avoid problems
|
||||
// sharing the extra cel between the drawing cursor preview and the
|
||||
@ -270,6 +271,8 @@ bool MovingPixelsState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
ASSERT(m_pixelsMovement);
|
||||
ASSERT(editor == m_editor);
|
||||
|
||||
m_delayedMouseMove.onMouseDown(msg);
|
||||
|
||||
// Set this editor as the active one and setup the ContextBar for
|
||||
// moving pixels. This is needed in case that the user is working
|
||||
// with a couple of Editors, in one is moving pixels and the other
|
||||
@ -357,6 +360,8 @@ bool MovingPixelsState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
ASSERT(m_pixelsMovement);
|
||||
ASSERT(editor == m_editor);
|
||||
|
||||
m_delayedMouseMove.onMouseUp(msg);
|
||||
|
||||
// Drop the image temporarily in this location (where the user releases the mouse)
|
||||
m_pixelsMovement->dropImageTemporarily();
|
||||
|
||||
@ -374,78 +379,8 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
|
||||
// If there is a button pressed
|
||||
if (m_pixelsMovement->isDragging()) {
|
||||
// Auto-scroll
|
||||
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir);
|
||||
|
||||
// Get the position of the mouse in the sprite
|
||||
gfx::Point spritePos = editor->screenToEditor(mousePos);
|
||||
if (spritePos == m_oldSpritePos) {
|
||||
// Avoid redrawing everything if the position in the canvas didn't change.
|
||||
// TODO remove this if we add support for anti-aliasing in the
|
||||
// transformations
|
||||
return true;
|
||||
}
|
||||
m_oldSpritePos = spritePos;
|
||||
|
||||
m_renderTimer.start();
|
||||
m_pixelsMovement->setFastMode(true);
|
||||
|
||||
// Get the customization for the pixels movement (snap to grid, angle snap, etc.).
|
||||
KeyContext keyContext = KeyContext::Normal;
|
||||
switch (m_pixelsMovement->handle()) {
|
||||
case MovePixelsHandle:
|
||||
keyContext = KeyContext::TranslatingSelection;
|
||||
break;
|
||||
case ScaleNWHandle:
|
||||
case ScaleNHandle:
|
||||
case ScaleNEHandle:
|
||||
case ScaleWHandle:
|
||||
case ScaleEHandle:
|
||||
case ScaleSWHandle:
|
||||
case ScaleSHandle:
|
||||
case ScaleSEHandle:
|
||||
keyContext = KeyContext::ScalingSelection;
|
||||
break;
|
||||
case RotateNWHandle:
|
||||
case RotateNHandle:
|
||||
case RotateNEHandle:
|
||||
case RotateWHandle:
|
||||
case RotateEHandle:
|
||||
case RotateSWHandle:
|
||||
case RotateSHandle:
|
||||
case RotateSEHandle:
|
||||
keyContext = KeyContext::RotatingSelection;
|
||||
break;
|
||||
}
|
||||
|
||||
PixelsMovement::MoveModifier moveModifier = PixelsMovement::NormalMovement;
|
||||
KeyAction action = editor->getCustomizationDelegate()
|
||||
->getPressedKeyAction(keyContext);
|
||||
|
||||
if (int(action & KeyAction::SnapToGrid))
|
||||
moveModifier |= PixelsMovement::SnapToGridMovement;
|
||||
|
||||
if (int(action & KeyAction::AngleSnap))
|
||||
moveModifier |= PixelsMovement::AngleSnapMovement;
|
||||
|
||||
if (int(action & KeyAction::MaintainAspectRatio))
|
||||
moveModifier |= PixelsMovement::MaintainAspectRatioMovement;
|
||||
|
||||
if (int(action & KeyAction::ScaleFromCenter))
|
||||
moveModifier |= PixelsMovement::ScaleFromPivot;
|
||||
|
||||
if (int(action & KeyAction::LockAxis))
|
||||
moveModifier |= PixelsMovement::LockAxisMovement;
|
||||
|
||||
// Invalidate handles
|
||||
Decorator* decorator = static_cast<Decorator*>(editor->decorator());
|
||||
TransformHandles* transfHandles = decorator->getTransformHandles(editor);
|
||||
transfHandles->invalidateHandles(editor, m_pixelsMovement->getTransformation());
|
||||
|
||||
// Drag the image to that position
|
||||
m_pixelsMovement->moveImage(spritePos, moveModifier);
|
||||
|
||||
editor->updateStatusBar();
|
||||
if (m_delayedMouseMove.onMouseMove(msg))
|
||||
m_renderTimer.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -453,6 +388,69 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
return StandbyState::onMouseMove(editor, msg);
|
||||
}
|
||||
|
||||
void MovingPixelsState::onCommitMouseMove(Editor* editor,
|
||||
const gfx::Point& spritePos)
|
||||
{
|
||||
m_pixelsMovement->setFastMode(true);
|
||||
|
||||
// Get the customization for the pixels movement (snap to grid, angle snap, etc.).
|
||||
KeyContext keyContext = KeyContext::Normal;
|
||||
switch (m_pixelsMovement->handle()) {
|
||||
case MovePixelsHandle:
|
||||
keyContext = KeyContext::TranslatingSelection;
|
||||
break;
|
||||
case ScaleNWHandle:
|
||||
case ScaleNHandle:
|
||||
case ScaleNEHandle:
|
||||
case ScaleWHandle:
|
||||
case ScaleEHandle:
|
||||
case ScaleSWHandle:
|
||||
case ScaleSHandle:
|
||||
case ScaleSEHandle:
|
||||
keyContext = KeyContext::ScalingSelection;
|
||||
break;
|
||||
case RotateNWHandle:
|
||||
case RotateNHandle:
|
||||
case RotateNEHandle:
|
||||
case RotateWHandle:
|
||||
case RotateEHandle:
|
||||
case RotateSWHandle:
|
||||
case RotateSHandle:
|
||||
case RotateSEHandle:
|
||||
keyContext = KeyContext::RotatingSelection;
|
||||
break;
|
||||
}
|
||||
|
||||
PixelsMovement::MoveModifier moveModifier = PixelsMovement::NormalMovement;
|
||||
KeyAction action = m_editor->getCustomizationDelegate()
|
||||
->getPressedKeyAction(keyContext);
|
||||
|
||||
if (int(action & KeyAction::SnapToGrid))
|
||||
moveModifier |= PixelsMovement::SnapToGridMovement;
|
||||
|
||||
if (int(action & KeyAction::AngleSnap))
|
||||
moveModifier |= PixelsMovement::AngleSnapMovement;
|
||||
|
||||
if (int(action & KeyAction::MaintainAspectRatio))
|
||||
moveModifier |= PixelsMovement::MaintainAspectRatioMovement;
|
||||
|
||||
if (int(action & KeyAction::ScaleFromCenter))
|
||||
moveModifier |= PixelsMovement::ScaleFromPivot;
|
||||
|
||||
if (int(action & KeyAction::LockAxis))
|
||||
moveModifier |= PixelsMovement::LockAxisMovement;
|
||||
|
||||
// Invalidate handles
|
||||
Decorator* decorator = static_cast<Decorator*>(m_editor->decorator());
|
||||
TransformHandles* transfHandles = decorator->getTransformHandles(m_editor);
|
||||
transfHandles->invalidateHandles(m_editor, m_pixelsMovement->getTransformation());
|
||||
|
||||
// Drag the image to that position
|
||||
m_pixelsMovement->moveImage(spritePos, moveModifier);
|
||||
|
||||
m_editor->updateStatusBar();
|
||||
}
|
||||
|
||||
bool MovingPixelsState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
|
||||
{
|
||||
ASSERT(m_pixelsMovement);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/context_bar_observer.h"
|
||||
#include "app/ui/editor/delayed_mouse_move.h"
|
||||
#include "app/ui/editor/editor_observer.h"
|
||||
#include "app/ui/editor/handle_type.h"
|
||||
#include "app/ui/editor/pixels_movement.h"
|
||||
@ -31,9 +32,13 @@ namespace app {
|
||||
: public StandbyState
|
||||
, EditorObserver
|
||||
, TimelineObserver
|
||||
, ContextBarObserver {
|
||||
, ContextBarObserver
|
||||
, DelayedMouseMoveDelegate {
|
||||
public:
|
||||
MovingPixelsState(Editor* editor, ui::MouseMessage* msg, PixelsMovementPtr pixelsMovement, HandleType handle);
|
||||
MovingPixelsState(Editor* editor,
|
||||
ui::MouseMessage* msg,
|
||||
PixelsMovementPtr pixelsMovement,
|
||||
HandleType handle);
|
||||
virtual ~MovingPixelsState();
|
||||
|
||||
bool canHandleFrameChange() const {
|
||||
@ -74,6 +79,10 @@ namespace app {
|
||||
virtual Transformation getTransformation(Editor* editor) override;
|
||||
|
||||
private:
|
||||
// DelayedMouseMoveDelegate impl
|
||||
void onCommitMouseMove(Editor* editor,
|
||||
const gfx::Point& spritePos) override;
|
||||
|
||||
void onTransparentColorChange();
|
||||
void onRenderTimer();
|
||||
|
||||
@ -91,6 +100,7 @@ namespace app {
|
||||
|
||||
// Helper member to move/translate selection and pixels.
|
||||
PixelsMovementPtr m_pixelsMovement;
|
||||
DelayedMouseMove m_delayedMouseMove;
|
||||
Editor* m_editor;
|
||||
bool m_observingEditor;
|
||||
|
||||
@ -100,11 +110,6 @@ namespace app {
|
||||
|
||||
ui::Timer m_renderTimer;
|
||||
|
||||
// Position of the mouse in the canvas to avoid redrawing when the
|
||||
// mouse position changes (only we redraw when the canvas position
|
||||
// changes).
|
||||
gfx::Point m_oldSpritePos;
|
||||
|
||||
obs::connection m_ctxConn;
|
||||
obs::connection m_opaqueConn;
|
||||
obs::connection m_transparentConn;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -341,7 +341,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
if (layerEdges)
|
||||
layerEdgesOption(false);
|
||||
|
||||
startDrawingState(editor,
|
||||
startDrawingState(editor, msg,
|
||||
DrawingType::Regular,
|
||||
pointer_from_msg(editor, msg));
|
||||
|
||||
@ -390,7 +390,7 @@ bool StandbyState::onDoubleClick(Editor* editor, MouseMessage* msg)
|
||||
editor->backToPreviousState();
|
||||
|
||||
// Start a tool-loop selecting tiles.
|
||||
startDrawingState(editor,
|
||||
startDrawingState(editor, msg,
|
||||
DrawingType::SelectTiles,
|
||||
pointer_from_msg(editor, msg));
|
||||
return true;
|
||||
@ -619,6 +619,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
|
||||
|
||||
DrawingState* StandbyState::startDrawingState(
|
||||
Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const DrawingType drawingType,
|
||||
const tools::Pointer& pointer)
|
||||
{
|
||||
@ -644,8 +645,7 @@ DrawingState* StandbyState::startDrawingState(
|
||||
editor->setState(newState);
|
||||
|
||||
static_cast<DrawingState*>(newState.get())
|
||||
->initToolLoop(editor,
|
||||
pointer);
|
||||
->initToolLoop(editor, msg, pointer);
|
||||
|
||||
return static_cast<DrawingState*>(newState.get());
|
||||
}
|
||||
@ -660,7 +660,7 @@ bool StandbyState::checkStartDrawingStraightLine(Editor* editor,
|
||||
(msg ? button_from_msg(msg): tools::Pointer::Left);
|
||||
|
||||
DrawingState* drawingState =
|
||||
startDrawingState(editor,
|
||||
startDrawingState(editor, msg,
|
||||
DrawingType::LineFreehand,
|
||||
tools::Pointer(
|
||||
editor->document()->lastDrawingPoint(),
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -87,6 +87,7 @@ namespace app {
|
||||
|
||||
private:
|
||||
DrawingState* startDrawingState(Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const DrawingType drawingType,
|
||||
const tools::Pointer& pointer);
|
||||
void transformSelection(Editor* editor, ui::MouseMessage* msg, HandleType handle);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -1003,15 +1003,20 @@ void Manager::removeMessagesForTimer(Timer* timer)
|
||||
ASSERT(manager_thread == base::this_thread::native_id());
|
||||
#endif
|
||||
|
||||
for (auto it=msg_queue.begin(); it != msg_queue.end(); ) {
|
||||
Message* msg = *it;
|
||||
for (Message* msg : msg_queue) {
|
||||
if (msg->type() == kTimerMessage &&
|
||||
static_cast<TimerMessage*>(msg)->timer() == timer) {
|
||||
delete msg;
|
||||
it = msg_queue.erase(it);
|
||||
msg->removeRecipient(msg->recipient());
|
||||
static_cast<TimerMessage*>(msg)->_resetTimer();
|
||||
}
|
||||
}
|
||||
|
||||
for (Message* msg : used_msg_queue) {
|
||||
if (msg->type() == kTimerMessage &&
|
||||
static_cast<TimerMessage*>(msg)->timer() == timer) {
|
||||
msg->removeRecipient(msg->recipient());
|
||||
static_cast<TimerMessage*>(msg)->_resetTimer();
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1411,8 +1416,9 @@ int Manager::pumpQueue()
|
||||
|
||||
// Call Timer::tick() if this is a tick message.
|
||||
if (msg->type() == kTimerMessage) {
|
||||
ASSERT(static_cast<TimerMessage*>(msg)->timer() != nullptr);
|
||||
static_cast<TimerMessage*>(msg)->timer()->tick();
|
||||
// The timer can be nullptr if it was removed with removeMessagesForTimer()
|
||||
if (auto timer = static_cast<TimerMessage*>(msg)->timer())
|
||||
timer->tick();
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -179,6 +179,13 @@ namespace ui {
|
||||
int count() const { return m_count; }
|
||||
Timer* timer() { return m_timer; }
|
||||
|
||||
// Used by Manager::removeMessagesForTimer() to invalidate the
|
||||
// message. It's like removing the message from the queue, but
|
||||
// without touching the queue in case that we're iterating it
|
||||
// (which can happen if we remove the timer from a kTimerMessage
|
||||
// handler).
|
||||
void _resetTimer() { m_timer = nullptr; }
|
||||
|
||||
private:
|
||||
int m_count; // Accumulated calls
|
||||
Timer* m_timer; // Timer handle
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -57,6 +57,10 @@ void Timer::start()
|
||||
{
|
||||
assert_ui_thread();
|
||||
|
||||
// Infinite timer? Do nothing.
|
||||
if (m_interval == 0)
|
||||
return;
|
||||
|
||||
m_lastTick = base::current_tick();
|
||||
if (!m_running) {
|
||||
m_running = true;
|
||||
@ -109,6 +113,8 @@ void Timer::pollTimers()
|
||||
|
||||
for (auto timer : timers) {
|
||||
if (timer && timer->isRunning()) {
|
||||
ASSERT(timer->interval() > 0);
|
||||
|
||||
int64_t count = ((t - timer->m_lastTick) / timer->m_interval);
|
||||
if (count > 0) {
|
||||
timer->m_lastTick += count * timer->m_interval;
|
||||
|
Loading…
x
Reference in New Issue
Block a user