Add key modifier to draw straight lines from the last point (fix #562)

- Removed ToolLoop::screenToSprite(), now ToolLoopManager::Pointer has
  sprite coordinates (instead of screen coordinates)
This commit is contained in:
David Capello 2015-09-16 12:19:10 -03:00
parent c674c474f6
commit 8aefa24a5b
12 changed files with 98 additions and 38 deletions

View File

@ -437,6 +437,9 @@
<!-- Modifiers for move tool --> <!-- Modifiers for move tool -->
<key action="AutoSelectLayer" shortcut="Ctrl" mac="Cmd" /> <key action="AutoSelectLayer" shortcut="Ctrl" mac="Cmd" />
<!-- Modifiers for freehand tool controller -->
<key action="StraightLineFromLastPoint" shortcut="Shift" />
</actions> </actions>
</keyboard> </keyboard>

View File

@ -342,6 +342,9 @@ private:
case KeyContext::MoveTool: case KeyContext::MoveTool:
text = "Move Tool: " + text; text = "Move Tool: " + text;
break; break;
case KeyContext::FreehandTool:
text = "Freehand Tools: " + text;
break;
} }
KeyItem* keyItem = new KeyItem(text, key, NULL, 0); KeyItem* keyItem = new KeyItem(text, key, NULL, 0);

View File

@ -212,9 +212,6 @@ namespace app {
// Returns true if the loop was canceled by the user // Returns true if the loop was canceled by the user
virtual bool isCanceled() = 0; virtual bool isCanceled() = 0;
// Converts a coordinate in the screen to the sprite.
virtual gfx::Point screenToSprite(const gfx::Point& screenPoint) = 0;
// This region is modified by the ToolLoopManager so then you know // This region is modified by the ToolLoopManager so then you know
// what must be updated in updateDirtyArea(). // what must be updated in updateDirtyArea().
virtual gfx::Region& getDirtyArea() = 0; virtual gfx::Region& getDirtyArea() = 0;
@ -223,7 +220,6 @@ namespace app {
virtual void updateDirtyArea() = 0; virtual void updateDirtyArea() = 0;
virtual void updateStatusBar(const char* text) = 0; virtual void updateStatusBar(const char* text) = 0;
}; };
} // namespace tools } // namespace tools

View File

@ -101,7 +101,7 @@ void ToolLoopManager::pressButton(const Pointer& pointer)
} }
// Convert the screen point to a sprite point // Convert the screen point to a sprite point
Point spritePoint = m_toolLoop->screenToSprite(Point(pointer.x(), pointer.y())); Point spritePoint = pointer.point();
m_toolLoop->setSpeed(Point(0, 0)); m_toolLoop->setSpeed(Point(0, 0));
m_oldPoint = spritePoint; m_oldPoint = spritePoint;
snapToGrid(spritePoint); snapToGrid(spritePoint);
@ -122,7 +122,7 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
if (isCanceled()) if (isCanceled())
return false; return false;
Point spritePoint = m_toolLoop->screenToSprite(Point(pointer.x(), pointer.y())); Point spritePoint = pointer.point();
snapToGrid(spritePoint); snapToGrid(spritePoint);
bool res = m_toolLoop->getController()->releaseButton(m_points, spritePoint); bool res = m_toolLoop->getController()->releaseButton(m_points, spritePoint);
@ -144,7 +144,7 @@ void ToolLoopManager::movement(const Pointer& pointer)
return; return;
// Convert the screen point to a sprite point // Convert the screen point to a sprite point
Point spritePoint = m_toolLoop->screenToSprite(Point(pointer.x(), pointer.y())); Point spritePoint = pointer.point();
// Calculate the speed (new sprite point - old sprite point) // Calculate the speed (new sprite point - old sprite point)
m_toolLoop->setSpeed(spritePoint - m_oldPoint); m_toolLoop->setSpeed(spritePoint - m_oldPoint);
m_oldPoint = spritePoint; m_oldPoint = spritePoint;

View File

@ -46,17 +46,16 @@ namespace app {
enum Button { None, Left, Middle, Right }; enum Button { None, Left, Middle, Right };
Pointer() Pointer()
: m_x(0), m_y(0), m_button(None) { } : m_point(0, 0), m_button(None) { }
Pointer(int x, int y, Button button) Pointer(const gfx::Point& point, Button button)
: m_x(x), m_y(y), m_button(button) { } : m_point(point), m_button(button) { }
int x() const { return m_x; } const gfx::Point& point() const { return m_point; }
int y() const { return m_y; }
Button getButton() const { return m_button; } Button getButton() const { return m_button; }
private: private:
int m_x, m_y; gfx::Point m_point;
Button m_button; Button m_button;
}; };

View File

@ -20,6 +20,7 @@
#include "app/tools/tool_loop.h" #include "app/tools/tool_loop.h"
#include "app/tools/tool_loop_manager.h" #include "app/tools/tool_loop_manager.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
#include "app/ui/editor/editor_customization_delegate.h"
#include "app/ui/keyboard_shortcuts.h" #include "app/ui/keyboard_shortcuts.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "ui/message.h" #include "ui/message.h"
@ -44,10 +45,11 @@ static tools::ToolLoopManager::Pointer::Button button_from_msg(MouseMessage* msg
tools::ToolLoopManager::Pointer::Left)); tools::ToolLoopManager::Pointer::Left));
} }
static tools::ToolLoopManager::Pointer pointer_from_msg(MouseMessage* msg) static tools::ToolLoopManager::Pointer pointer_from_msg(Editor* editor, MouseMessage* msg)
{ {
return return
tools::ToolLoopManager::Pointer(msg->position().x, msg->position().y, button_from_msg(msg)); tools::ToolLoopManager::Pointer(editor->screenToEditor(msg->position()),
button_from_msg(msg));
} }
DrawingState::DrawingState(tools::ToolLoop* toolLoop) DrawingState::DrawingState(tools::ToolLoop* toolLoop)
@ -75,9 +77,33 @@ void DrawingState::initToolLoop(Editor* editor, MouseMessage* msg)
m_toolLoop->getFrame(), m_toolLoop->getFrame(),
m_toolLoop->getDstImage()); m_toolLoop->getDstImage());
m_toolLoopManager->prepareLoop(pointer_from_msg(msg), msg->keyModifiers()); m_lastPoint = editor->lastDrawingPosition();
m_toolLoopManager->pressButton(pointer_from_msg(msg));
tools::ToolLoopManager::Pointer pointer;
bool movement = false;
if (m_toolLoop->getController()->isFreehand() &&
(editor->getCustomizationDelegate()
->getPressedKeyAction(KeyContext::FreehandTool) & KeyAction::StraightLineFromLastPoint) == KeyAction::StraightLineFromLastPoint &&
m_lastPoint.x >= 0) {
pointer = tools::ToolLoopManager::Pointer(m_lastPoint, button_from_msg(msg));
movement = true;
}
else {
pointer = pointer_from_msg(editor, msg);
}
m_toolLoopManager->prepareLoop(pointer, msg->keyModifiers());
m_toolLoopManager->pressButton(pointer);
// This first movement is done when the user pressed Shift+click in
// a freehand tool to draw a straight line.
if (movement) {
pointer = pointer_from_msg(editor, msg);
m_toolLoopManager->movement(pointer);
}
editor->setLastDrawingPosition(pointer.point());
editor->captureMouse(); editor->captureMouse();
} }
@ -87,7 +113,7 @@ bool DrawingState::onMouseDown(Editor* editor, MouseMessage* msg)
ASSERT(m_toolLoopManager != NULL); ASSERT(m_toolLoopManager != NULL);
// Notify the mouse button down to the tool loop manager. // Notify the mouse button down to the tool loop manager.
m_toolLoopManager->pressButton(pointer_from_msg(msg)); m_toolLoopManager->pressButton(pointer_from_msg(editor, msg));
// Cancel drawing loop // Cancel drawing loop
if (m_toolLoopManager->isCanceled()) { if (m_toolLoopManager->isCanceled()) {
@ -114,7 +140,7 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
// Notify the release of the mouse button to the tool loop // Notify the release of the mouse button to the tool loop
// manager. This is the correct way to say "the user finishes the // manager. This is the correct way to say "the user finishes the
// drawing trace correctly". // drawing trace correctly".
if (m_toolLoopManager->releaseButton(pointer_from_msg(msg))) if (m_toolLoopManager->releaseButton(pointer_from_msg(editor, msg)))
return true; return true;
} }
@ -143,12 +169,15 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg)
// Infinite scroll // Infinite scroll
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir, true); gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir, true);
tools::ToolLoopManager::Pointer pointer(editor->screenToEditor(mousePos),
button_from_msg(msg));
// Notify mouse movement to the tool // Notify mouse movement to the tool
ASSERT(m_toolLoopManager != NULL); ASSERT(m_toolLoopManager != NULL);
m_toolLoopManager m_toolLoopManager->movement(pointer);
->movement(tools::ToolLoopManager::Pointer(mousePos.x, mousePos.y,
button_from_msg(msg))); // Save the last point.
editor->setLastDrawingPosition(pointer.point());
return true; return true;
} }
@ -203,8 +232,14 @@ void DrawingState::onExposeSpritePixels(const gfx::Region& rgn)
void DrawingState::destroyLoop(Editor* editor) void DrawingState::destroyLoop(Editor* editor)
{ {
if (editor) if (editor) {
if (m_toolLoopManager &&
m_toolLoopManager->isCanceled()) {
editor->setLastDrawingPosition(m_lastPoint);
}
editor->renderEngine().removePreviewImage(); editor->renderEngine().removePreviewImage();
}
if (m_toolLoop) if (m_toolLoop)
m_toolLoop->dispose(); m_toolLoop->dispose();

View File

@ -49,6 +49,11 @@ namespace app {
// cancel selection tool (deselect) when the user click (press and // cancel selection tool (deselect) when the user click (press and
// release the mouse button in the same location). // release the mouse button in the same location).
bool m_mouseMoveReceived; bool m_mouseMoveReceived;
// Stores the last drawing position before we start this
// DrawingState. It's used to restore the last drawing position in
// case this stroke is canceled.
gfx::Point m_lastPoint;
}; };
} // namespace app } // namespace app

View File

@ -152,6 +152,7 @@ Editor::Editor(Document* document, EditorFlags flags)
, m_frame(frame_t(0)) , m_frame(frame_t(0))
, m_zoom(1, 1) , m_zoom(1, 1)
, m_brushPreview(this) , m_brushPreview(this)
, m_lastDrawingPosition(-1, -1)
, m_quicktool(NULL) , m_quicktool(NULL)
, m_selectionMode(tools::SelectionMode::DEFAULT) , m_selectionMode(tools::SelectionMode::DEFAULT)
, m_padding(0, 0) , m_padding(0, 0)
@ -1384,6 +1385,11 @@ void Editor::setCursor(const gfx::Point& mouseScreenPos)
showMouseCursor(kArrowCursor); showMouseCursor(kArrowCursor);
} }
void Editor::setLastDrawingPosition(const gfx::Point& pos)
{
m_lastDrawingPosition = pos;
}
bool Editor::canDraw() bool Editor::canDraw()
{ {
return (m_layer != NULL && return (m_layer != NULL &&

View File

@ -168,6 +168,9 @@ namespace app {
bool isSecondaryButton() const { return m_secondaryButton; } bool isSecondaryButton() const { return m_secondaryButton; }
gfx::Point lastDrawingPosition() const { return m_lastDrawingPosition; }
void setLastDrawingPosition(const gfx::Point& pos);
// Returns true if we are able to draw in the current doc/sprite/layer/cel. // Returns true if we are able to draw in the current doc/sprite/layer/cel.
bool canDraw(); bool canDraw();
@ -258,6 +261,10 @@ namespace app {
// Brush preview // Brush preview
BrushPreview m_brushPreview; BrushPreview m_brushPreview;
// Position used to draw straight lines using freehand tools + Shift key
// (EditorCustomizationDelegate::isStraightLineFromLastPoint() modifier)
gfx::Point m_lastDrawingPosition;
// Current selected quicktool (this genererally should be NULL if // Current selected quicktool (this genererally should be NULL if
// the user is not pressing any keyboard key). // the user is not pressing any keyboard key).
tools::Tool* m_quicktool; tools::Tool* m_quicktool;

View File

@ -195,10 +195,6 @@ public:
tools::TracePolicy getTracePolicy() override { return m_tracePolicy; } tools::TracePolicy getTracePolicy() override { return m_tracePolicy; }
doc::Remap* getShadingRemap() override { return m_shadingRemap; } doc::Remap* getShadingRemap() override { return m_shadingRemap; }
gfx::Point screenToSprite(const gfx::Point& screenPoint) override {
return m_editor->screenToEditor(screenPoint);
}
gfx::Region& getDirtyArea() override { gfx::Region& getDirtyArea() override {
return m_dirtyArea; return m_dirtyArea;
} }
@ -213,6 +209,7 @@ public:
void updateStatusBar(const char* text) override { void updateStatusBar(const char* text) override {
StatusBar::instance()->setStatusText(0, text); StatusBar::instance()->setStatusText(0, text);
} }
}; };
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -43,6 +43,7 @@ namespace {
{ "AddSelection" , "Add Selection" , app::KeyAction::AddSelection }, { "AddSelection" , "Add Selection" , app::KeyAction::AddSelection },
{ "SubtractSelection" , "Subtract Selection" , app::KeyAction::SubtractSelection }, { "SubtractSelection" , "Subtract Selection" , app::KeyAction::SubtractSelection },
{ "AutoSelectLayer" , "Auto Select Layer" , app::KeyAction::AutoSelectLayer }, { "AutoSelectLayer" , "Auto Select Layer" , app::KeyAction::AutoSelectLayer },
{ "StraightLineFromLastPoint", "Straight Line from Last Point", app::KeyAction::StraightLineFromLastPoint },
{ "LeftMouseButton" , "Trigger Left Mouse Button" , app::KeyAction::LeftMouseButton }, { "LeftMouseButton" , "Trigger Left Mouse Button" , app::KeyAction::LeftMouseButton },
{ "RightMouseButton" , "Trigger Right Mouse Button" , app::KeyAction::RightMouseButton }, { "RightMouseButton" , "Trigger Right Mouse Button" , app::KeyAction::RightMouseButton },
{ NULL , NULL , app::KeyAction::None } { NULL , NULL , app::KeyAction::None }
@ -158,6 +159,9 @@ Key::Key(KeyAction action)
case KeyAction::AutoSelectLayer: case KeyAction::AutoSelectLayer:
m_keycontext = KeyContext::MoveTool; m_keycontext = KeyContext::MoveTool;
break; break;
case KeyAction::StraightLineFromLastPoint:
m_keycontext = KeyContext::FreehandTool;
break;
case KeyAction::LeftMouseButton: case KeyAction::LeftMouseButton:
m_keycontext = KeyContext::Any; m_keycontext = KeyContext::Any;
break; break;
@ -533,6 +537,9 @@ void KeyboardShortcuts::exportAccel(TiXmlElement& parent, Key* key, const ui::Ac
case KeyContext::MoveTool: case KeyContext::MoveTool:
keycontextStr = "MoveTool"; keycontextStr = "MoveTool";
break; break;
case KeyContext::FreehandTool:
keycontextStr = "FreehandTool";
break;
} }
if (keycontextStr) if (keycontextStr)

View File

@ -38,6 +38,7 @@ namespace app {
ScalingSelection, ScalingSelection,
RotatingSelection, RotatingSelection,
MoveTool, MoveTool,
FreehandTool,
}; };
enum class KeySource { enum class KeySource {
@ -54,17 +55,18 @@ namespace app {
// TODO This should be called "KeyActionModifier" or something similar // TODO This should be called "KeyActionModifier" or something similar
enum class KeyAction { enum class KeyAction {
None = 0x00000000, None = 0x00000000,
CopySelection = 0x00000001, CopySelection = 0x00000001,
SnapToGrid = 0x00000002, SnapToGrid = 0x00000002,
AngleSnap = 0x00000004, AngleSnap = 0x00000004,
MaintainAspectRatio = 0x00000008, MaintainAspectRatio = 0x00000008,
LockAxis = 0x00000010, LockAxis = 0x00000010,
AddSelection = 0x00000020, AddSelection = 0x00000020,
SubtractSelection = 0x00000040, SubtractSelection = 0x00000040,
AutoSelectLayer = 0x00000080, AutoSelectLayer = 0x00000080,
LeftMouseButton = 0x00000100, LeftMouseButton = 0x00000100,
RightMouseButton = 0x00000200, RightMouseButton = 0x00000200,
StraightLineFromLastPoint = 0x00000400
}; };
inline KeyAction operator&(KeyAction a, KeyAction b) { inline KeyAction operator&(KeyAction a, KeyAction b) {