mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Merge branch 'main' into beta
This commit is contained in:
commit
ab3bf31c6a
@ -1,5 +1,5 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2018-2021 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2018-2022 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2018 David Capello -->
|
||||
<gui i18nwarnings="false">
|
||||
<window id="about" text="About Aseprite">
|
||||
@ -25,7 +25,7 @@
|
||||
</hbox>
|
||||
<separator horizontal="true" />
|
||||
<hbox>
|
||||
<label text="Copyright (C) 2001-2021" />
|
||||
<label text="Copyright (C) 2001-2022" />
|
||||
<link text="Igara Studio S.A." url="https://www.igarastudio.com/" />
|
||||
</hbox>
|
||||
<link text="https://www.aseprite.org/" url="https://www.aseprite.org/" />
|
||||
|
@ -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
|
||||
@ -342,6 +342,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
|
||||
|
95
src/app/ui/editor/delayed_mouse_move.cpp
Normal file
95
src/app/ui/editor/delayed_mouse_move.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
// 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<double>::min(),
|
||||
std::numeric_limits<double>::min())
|
||||
{
|
||||
ASSERT(m_delegate);
|
||||
m_timer.Tick.connect([this] { commitMouseMove(); });
|
||||
}
|
||||
|
||||
void DelayedMouseMove::initSpritePos(const gfx::PointF& pos)
|
||||
{
|
||||
m_spritePos = pos;
|
||||
}
|
||||
|
||||
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::PointF& DelayedMouseMove::spritePos() const
|
||||
{
|
||||
ASSERT(m_spritePos.x != std::numeric_limits<double>::min() &&
|
||||
m_spritePos.y != std::numeric_limits<double>::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::PointF spritePos = m_editor->screenToEditorF(mousePos);
|
||||
|
||||
// Avoid redrawing everything if the position in the canvas didn't
|
||||
// change.
|
||||
if (m_spritePos != spritePos) {
|
||||
m_spritePos = spritePos;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace app
|
71
src/app/ui/editor/delayed_mouse_move.h
Normal file
71
src/app/ui/editor/delayed_mouse_move.h
Normal file
@ -0,0 +1,71 @@
|
||||
// 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::PointF& 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);
|
||||
|
||||
// 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).
|
||||
void initSpritePos(const gfx::PointF& pos);
|
||||
|
||||
void onMouseDown(const ui::MouseMessage* msg);
|
||||
bool onMouseMove(const ui::MouseMessage* msg);
|
||||
void onMouseUp(const ui::MouseMessage* msg);
|
||||
|
||||
const gfx::PointF& 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::PointF 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,14 @@ DrawingState::~DrawingState()
|
||||
}
|
||||
|
||||
void DrawingState::initToolLoop(Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const tools::Pointer& pointer)
|
||||
{
|
||||
if (msg)
|
||||
m_delayedMouseMove.onMouseDown(msg);
|
||||
else
|
||||
m_delayedMouseMove.initSpritePos(gfx::PointF(pointer.point()));
|
||||
|
||||
Tileset* tileset = m_toolLoop->getDstTileset();
|
||||
|
||||
// For selection inks we don't use a "the selected layer" for
|
||||
@ -130,6 +152,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.
|
||||
@ -159,7 +183,7 @@ bool DrawingState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
// checkStartDrawingStraightLine() with the right-button.
|
||||
if (recreateLoop && isCanceled) {
|
||||
ASSERT(!m_toolLoopManager);
|
||||
checkStartDrawingStraightLine(editor, &pointer);
|
||||
checkStartDrawingStraightLine(editor, msg, &pointer);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -169,7 +193,8 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
ASSERT(m_toolLoopManager != NULL);
|
||||
|
||||
tools::Pointer pointer = pointer_from_msg(editor, msg, m_velocity.velocity());
|
||||
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
|
||||
@ -184,8 +209,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;
|
||||
|
||||
// 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".
|
||||
@ -207,7 +230,7 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
// button, if the Shift key is pressed, the whole ToolLoop starts
|
||||
// again.
|
||||
if (Preferences::instance().editor.straightLinePreview())
|
||||
checkStartDrawingStraightLine(editor, &pointer);
|
||||
checkStartDrawingStraightLine(editor, msg, &m_lastPointer);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -228,19 +251,29 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
// Update velocity sensor.
|
||||
m_velocity.updateWithDisplayPoint(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(gfx::Point(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::PointF& spritePos)
|
||||
{
|
||||
if (m_toolLoop &&
|
||||
m_toolLoopManager &&
|
||||
!m_toolLoopManager->isCanceled()) {
|
||||
handleMouseMovement();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawingState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
|
||||
{
|
||||
if (m_toolLoop->getInk()->isEyedropper()) {
|
||||
@ -299,12 +332,12 @@ bool DrawingState::onScrollChange(Editor* editor)
|
||||
// Update velocity sensor.
|
||||
m_velocity.updateWithDisplayPoint(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;
|
||||
}
|
||||
@ -332,14 +365,13 @@ bool DrawingState::getGridBounds(Editor* editor, gfx::Rect& gridBounds)
|
||||
return false;
|
||||
}
|
||||
|
||||
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,
|
||||
@ -47,6 +49,7 @@ namespace app {
|
||||
virtual bool getGridBounds(Editor* editor, gfx::Rect& gridBounds) override;
|
||||
|
||||
void initToolLoop(Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const tools::Pointer& pointer);
|
||||
|
||||
// Used to send a movement() to the ToolLoopManager when
|
||||
@ -56,14 +59,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::PointF& 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
|
||||
@ -1382,7 +1382,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-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
|
||||
@ -214,7 +214,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
|
||||
@ -53,14 +53,17 @@
|
||||
#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)
|
||||
@ -277,6 +280,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
|
||||
@ -364,6 +369,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();
|
||||
|
||||
@ -381,81 +388,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::PointF spritePos = editor->screenToEditorF(mousePos);
|
||||
|
||||
// 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 RotateNEHandle:
|
||||
case RotateSWHandle:
|
||||
case RotateSEHandle:
|
||||
keyContext = KeyContext::RotatingSelection;
|
||||
break;
|
||||
case SkewNHandle:
|
||||
case SkewWHandle:
|
||||
case SkewEHandle:
|
||||
case SkewSHandle:
|
||||
keyContext = KeyContext::ScalingSelection;
|
||||
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;
|
||||
|
||||
if (int(action & KeyAction::FineControl))
|
||||
moveModifier |= PixelsMovement::FineControl;
|
||||
|
||||
m_renderTimer.start();
|
||||
m_pixelsMovement->setFastMode(true);
|
||||
|
||||
// Invalidate handles
|
||||
Decorator* decorator = static_cast<Decorator*>(editor->decorator());
|
||||
TransformHandles* transfHandles = decorator->getTransformHandles(editor);
|
||||
const Transformation& transformation = m_pixelsMovement->getTransformation();
|
||||
transfHandles->invalidateHandles(editor, transformation);
|
||||
|
||||
// Drag the image to that position
|
||||
m_pixelsMovement->moveImage(spritePos, moveModifier);
|
||||
|
||||
// Update context bar and status bar
|
||||
ContextBar* contextBar = App::instance()->contextBar();
|
||||
contextBar->updateForMovingPixels(transformation);
|
||||
|
||||
editor->updateStatusBar();
|
||||
if (m_delayedMouseMove.onMouseMove(msg))
|
||||
m_renderTimer.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -463,6 +397,79 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
return StandbyState::onMouseMove(editor, msg);
|
||||
}
|
||||
|
||||
void MovingPixelsState::onCommitMouseMove(Editor* editor,
|
||||
const gfx::PointF& 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 RotateNEHandle:
|
||||
case RotateSWHandle:
|
||||
case RotateSEHandle:
|
||||
keyContext = KeyContext::RotatingSelection;
|
||||
break;
|
||||
case SkewNHandle:
|
||||
case SkewWHandle:
|
||||
case SkewEHandle:
|
||||
case SkewSHandle:
|
||||
keyContext = KeyContext::ScalingSelection;
|
||||
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;
|
||||
|
||||
if (int(action & KeyAction::FineControl))
|
||||
moveModifier |= PixelsMovement::FineControl;
|
||||
|
||||
// Invalidate handles
|
||||
Decorator* decorator = static_cast<Decorator*>(m_editor->decorator());
|
||||
TransformHandles* transfHandles = decorator->getTransformHandles(m_editor);
|
||||
const Transformation& transformation = m_pixelsMovement->getTransformation();
|
||||
transfHandles->invalidateHandles(m_editor, transformation);
|
||||
|
||||
// Drag the image to that position
|
||||
m_pixelsMovement->moveImage(spritePos, moveModifier);
|
||||
|
||||
// Update context bar and status bar
|
||||
ContextBar* contextBar = App::instance()->contextBar();
|
||||
contextBar->updateForMovingPixels(transformation);
|
||||
|
||||
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"
|
||||
@ -32,9 +33,13 @@ namespace app {
|
||||
, EditorObserver
|
||||
, TimelineObserver
|
||||
, ContextBarObserver
|
||||
, PixelsMovementDelegate {
|
||||
, PixelsMovementDelegate
|
||||
, 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 {
|
||||
@ -80,6 +85,10 @@ namespace app {
|
||||
virtual Transformation getTransformation(Editor* editor) override;
|
||||
|
||||
private:
|
||||
// DelayedMouseMoveDelegate impl
|
||||
void onCommitMouseMove(Editor* editor,
|
||||
const gfx::PointF& spritePos) override;
|
||||
|
||||
void onTransparentColorChange();
|
||||
void onRenderTimer();
|
||||
|
||||
@ -97,6 +106,7 @@ namespace app {
|
||||
|
||||
// Helper member to move/translate selection and pixels.
|
||||
PixelsMovementPtr m_pixelsMovement;
|
||||
DelayedMouseMove m_delayedMouseMove;
|
||||
Editor* m_editor;
|
||||
bool m_observingEditor;
|
||||
|
||||
@ -106,11 +116,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::PointF m_oldSpritePos;
|
||||
|
||||
obs::connection m_ctxConn;
|
||||
obs::connection m_opaqueConn;
|
||||
obs::connection m_transparentConn;
|
||||
|
@ -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
|
||||
@ -319,7 +319,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
// Shift+click on Pencil tool starts a line onMouseDown() when the
|
||||
// preview (onKeyDown) is disabled.
|
||||
if (!Preferences::instance().editor.straightLinePreview() &&
|
||||
checkStartDrawingStraightLine(editor, &pointer)) {
|
||||
checkStartDrawingStraightLine(editor, msg, &pointer)) {
|
||||
// Send first mouse down to draw the straight line and start the
|
||||
// freehand mode.
|
||||
editor->getState()->onMouseDown(editor, msg);
|
||||
@ -334,7 +334,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
if (layerEdges)
|
||||
layerEdgesOption(false);
|
||||
|
||||
startDrawingState(editor,
|
||||
startDrawingState(editor, msg,
|
||||
DrawingType::Regular,
|
||||
pointer);
|
||||
|
||||
@ -383,7 +383,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;
|
||||
@ -511,7 +511,7 @@ bool StandbyState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
|
||||
bool StandbyState::onKeyDown(Editor* editor, KeyMessage* msg)
|
||||
{
|
||||
if (Preferences::instance().editor.straightLinePreview() &&
|
||||
checkStartDrawingStraightLine(editor, nullptr))
|
||||
checkStartDrawingStraightLine(editor, nullptr, nullptr))
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
@ -627,6 +627,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
|
||||
|
||||
DrawingState* StandbyState::startDrawingState(
|
||||
Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const DrawingType drawingType,
|
||||
const tools::Pointer& pointer)
|
||||
{
|
||||
@ -652,13 +653,13 @@ DrawingState* StandbyState::startDrawingState(
|
||||
editor->setState(newState);
|
||||
|
||||
static_cast<DrawingState*>(newState.get())
|
||||
->initToolLoop(editor,
|
||||
pointer);
|
||||
->initToolLoop(editor, msg, pointer);
|
||||
|
||||
return static_cast<DrawingState*>(newState.get());
|
||||
}
|
||||
|
||||
bool StandbyState::checkStartDrawingStraightLine(Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const tools::Pointer* pointer)
|
||||
{
|
||||
// Start line preview with shift key
|
||||
@ -668,7 +669,7 @@ bool StandbyState::checkStartDrawingStraightLine(Editor* editor,
|
||||
(pointer ? pointer->button(): 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-2020 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
|
||||
@ -55,6 +55,7 @@ namespace app {
|
||||
protected:
|
||||
void callEyedropper(Editor* editor, const ui::MouseMessage* msg);
|
||||
bool checkStartDrawingStraightLine(Editor* editor,
|
||||
const ui::MouseMessage* msg,
|
||||
const tools::Pointer* pointer);
|
||||
virtual bool canCheckStartDrawingStraightLine() { return true; }
|
||||
|
||||
@ -87,6 +88,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);
|
||||
|
@ -24,7 +24,7 @@ BEGIN
|
||||
VALUE "FileDescription", "Aseprite - Animated sprites editor & pixel art tool"
|
||||
VALUE "FileVersion", "1,3,0,0"
|
||||
VALUE "InternalName", "aseprite"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2001-2021 Igara Studio S.A."
|
||||
VALUE "LegalCopyright", "Copyright (C) 2001-2022 Igara Studio S.A."
|
||||
VALUE "OriginalFilename", "aseprite.exe"
|
||||
VALUE "ProductName", "ASEPRITE"
|
||||
VALUE "ProductVersion", "1,3,0,0"
|
||||
|
@ -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.
|
||||
@ -1154,15 +1154,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1769,8 +1774,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-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.
|
||||
@ -192,6 +192,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;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Aseprite
|
||||
Copyright (C) 2020-2021 Igara Studio S.A.
|
||||
Copyright (C) 2020-2022 Igara Studio S.A.
|
||||
|
||||
This program is distributed under the terms of
|
||||
the End-User License Agreement for Aseprite. */
|
||||
@ -8,7 +8,7 @@
|
||||
#include "generated_version.h" /* It defines the VERSION macro */
|
||||
|
||||
#define PACKAGE "Aseprite"
|
||||
#define COPYRIGHT "Copyright (C) 2001-2021 Igara Studio S.A."
|
||||
#define COPYRIGHT "Copyright (C) 2001-2022 Igara Studio S.A."
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
#define HTTP "https"
|
||||
|
Loading…
x
Reference in New Issue
Block a user