From 30f4e995a34665141498f7f38b61bc2fe66f3f64 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 19 Mar 2016 11:33:05 -0300 Subject: [PATCH 1/3] Select a tile by double clicking over it (fix #939) --- data/gui.xml | 1 + src/app/CMakeLists.txt | 1 + src/app/commands/cmd_select_tile.cpp | 87 +++++++++++++++++++++++++++ src/app/commands/commands_list.h | 1 + src/app/snap_to_grid.cpp | 27 +++++++-- src/app/snap_to_grid.h | 11 +++- src/app/tools/tool_loop_manager.cpp | 5 +- src/app/ui/editor/editor.cpp | 10 +++ src/app/ui/editor/editor_state.h | 3 + src/app/ui/editor/pixels_movement.cpp | 5 +- src/app/ui/editor/standby_state.cpp | 18 ++++++ src/app/ui/editor/standby_state.h | 3 +- 12 files changed, 159 insertions(+), 13 deletions(-) create mode 100644 src/app/commands/cmd_select_tile.cpp diff --git a/data/gui.xml b/data/gui.xml index 9b71f1329..8355df820 100644 --- a/data/gui.xml +++ b/data/gui.xml @@ -398,6 +398,7 @@ + diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 694df0276..149d59eca 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -247,6 +247,7 @@ add_library(app-lib commands/cmd_save_palette.cpp commands/cmd_scroll.cpp commands/cmd_scroll_center.cpp + commands/cmd_select_tile.cpp commands/cmd_set_color_selector.cpp commands/cmd_set_ink_type.cpp commands/cmd_set_loop_section.cpp diff --git a/src/app/commands/cmd_select_tile.cpp b/src/app/commands/cmd_select_tile.cpp new file mode 100644 index 000000000..1c4a4190b --- /dev/null +++ b/src/app/commands/cmd_select_tile.cpp @@ -0,0 +1,87 @@ +// Aseprite +// Copyright (C) 2015, 2016 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/cmd/set_mask.h" +#include "app/commands/command.h" +#include "app/context_access.h" +#include "app/document.h" +#include "app/modules/editors.h" +#include "app/modules/gui.h" +#include "app/pref/preferences.h" +#include "app/snap_to_grid.h" +#include "app/transaction.h" +#include "app/ui/editor/editor.h" +#include "doc/mask.h" +#include "ui/system.h" + +namespace app { + +using namespace doc; + +class SelectTileCommand : public Command { +public: + SelectTileCommand(); + Command* clone() const override { return new SelectTileCommand(*this); } + +protected: + bool onEnabled(Context* context) override; + void onExecute(Context* context) override; +}; + +SelectTileCommand::SelectTileCommand() + : Command("SelectTile", + "Select Tile", + CmdRecordableFlag) +{ +} + +bool SelectTileCommand::onEnabled(Context* context) +{ + return context->checkFlags(ContextFlags::ActiveDocumentIsWritable); +} + +void SelectTileCommand::onExecute(Context* ctx) +{ + if (!current_editor || + !current_editor->hasMouse()) + return; + + // Lock sprite + ContextWriter writer(ctx); + Document* doc(writer.document()); + auto& docPref = Preferences::instance().document(doc); + + base::UniquePtr mask(new Mask()); + { + const gfx::Rect gridBounds = docPref.grid.bounds(); + gfx::Point pos = current_editor->screenToEditor(ui::get_mouse_position()); + pos = snap_to_grid(gridBounds, pos, PreferSnapTo::BoxOrigin); + + mask->add(gfx::Rect(pos, gridBounds.size())); + } + + // Set the new mask + Transaction transaction(writer.context(), + "Select Tile", + DoesntModifyDocument); + transaction.execute(new cmd::SetMask(doc, mask)); + transaction.commit(); + + doc->generateMaskBoundaries(); + update_screen_for_document(doc); +} + +Command* CommandFactory::createSelectTileCommand() +{ + return new SelectTileCommand; +} + +} // namespace app diff --git a/src/app/commands/commands_list.h b/src/app/commands/commands_list.h index 684802e16..4fc651687 100644 --- a/src/app/commands/commands_list.h +++ b/src/app/commands/commands_list.h @@ -108,6 +108,7 @@ FOR_EACH_COMMAND(SaveMask) FOR_EACH_COMMAND(SavePalette) FOR_EACH_COMMAND(Scroll) FOR_EACH_COMMAND(ScrollCenter) +FOR_EACH_COMMAND(SelectTile) FOR_EACH_COMMAND(SelectionAsGrid) FOR_EACH_COMMAND(SetColorSelector) FOR_EACH_COMMAND(SetInkType) diff --git a/src/app/snap_to_grid.cpp b/src/app/snap_to_grid.cpp index f9fd31e1b..9a3989c8b 100644 --- a/src/app/snap_to_grid.cpp +++ b/src/app/snap_to_grid.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -18,7 +18,9 @@ namespace app { -gfx::Point snap_to_grid(const gfx::Rect& grid, const gfx::Point& point) +gfx::Point snap_to_grid(const gfx::Rect& grid, + const gfx::Point& point, + const PreferSnapTo prefer) { gfx::Point newPoint; div_t d, dx, dy; @@ -26,11 +28,24 @@ gfx::Point snap_to_grid(const gfx::Rect& grid, const gfx::Point& point) dx = std::div(grid.x, grid.w); dy = std::div(grid.y, grid.h); - d = std::div(point.x-dx.rem, grid.w); - newPoint.x = dx.rem + d.quot*grid.w + ((d.rem > grid.w/2)? grid.w: 0); + switch (prefer) { - d = std::div(point.y-dy.rem, grid.h); - newPoint.y = dy.rem + d.quot*grid.h + ((d.rem > grid.h/2)? grid.h: 0); + case PreferSnapTo::ClosestGridVertex: + d = std::div(point.x-dx.rem, grid.w); + newPoint.x = dx.rem + d.quot*grid.w + ((d.rem > grid.w/2)? grid.w: 0); + + d = std::div(point.y-dy.rem, grid.h); + newPoint.y = dy.rem + d.quot*grid.h + ((d.rem > grid.h/2)? grid.h: 0); + break; + + case PreferSnapTo::BoxOrigin: + d = std::div(point.x-dx.rem, grid.w); + newPoint.x = dx.rem + d.quot*grid.w; + + d = std::div(point.y-dy.rem, grid.h); + newPoint.y = dy.rem + d.quot*grid.h; + break; + } return newPoint; } diff --git a/src/app/snap_to_grid.h b/src/app/snap_to_grid.h index fb05da36f..9e9fc0c24 100644 --- a/src/app/snap_to_grid.h +++ b/src/app/snap_to_grid.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -13,7 +13,14 @@ namespace app { - gfx::Point snap_to_grid(const gfx::Rect& grid, const gfx::Point& point); + enum class PreferSnapTo { + ClosestGridVertex, + BoxOrigin + }; + + gfx::Point snap_to_grid(const gfx::Rect& grid, + const gfx::Point& point, + const PreferSnapTo prefer); } // namespace app diff --git a/src/app/tools/tool_loop_manager.cpp b/src/app/tools/tool_loop_manager.cpp index f58b96a5a..eda557ded 100644 --- a/src/app/tools/tool_loop_manager.cpp +++ b/src/app/tools/tool_loop_manager.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -234,7 +234,8 @@ void ToolLoopManager::snapToGrid(Point& point) !m_toolLoop->getSnapToGrid()) return; - point = snap_to_grid(m_toolLoop->getGridBounds(), point); + point = snap_to_grid(m_toolLoop->getGridBounds(), point, + PreferSnapTo::ClosestGridVertex); } // Strokes are relative to sprite origin. diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index 6bfcaeb69..f847633da 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -1294,6 +1294,16 @@ bool Editor::onProcessMessage(Message* msg) } break; + case kDoubleClickMessage: + if (m_sprite) { + MouseMessage* mouseMsg = static_cast(msg); + EditorStatePtr holdState(m_state); + bool used = m_state->onDoubleClick(this, mouseMsg); + if (used) + return true; + } + break; + case kTouchMagnifyMessage: if (m_sprite) { EditorStatePtr holdState(m_state); diff --git a/src/app/ui/editor/editor_state.h b/src/app/ui/editor/editor_state.h index a3d99ef44..a08b3e5be 100644 --- a/src/app/ui/editor/editor_state.h +++ b/src/app/ui/editor/editor_state.h @@ -89,6 +89,9 @@ namespace app { // Called when the user wants to zoom in/out using a pinch gesture in the trackpad. virtual bool onTouchMagnify(Editor* editor, ui::TouchMessage* msg) { return false; } + // Called when the user moves the mouse wheel over the editor. + virtual bool onDoubleClick(Editor* editor, ui::MouseMessage* msg) { return false; } + // Called each time the mouse changes its position so we can set an // appropiated cursor depending on the new coordinates of the mouse // pointer. diff --git a/src/app/ui/editor/pixels_movement.cpp b/src/app/ui/editor/pixels_movement.cpp index 711d724b7..81afc8257 100644 --- a/src/app/ui/editor/pixels_movement.cpp +++ b/src/app/ui/editor/pixels_movement.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -268,7 +268,8 @@ void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier) gfx::Rect gridBounds = App::instance() ->preferences().document(m_document).grid.bounds(); gfx::Point gridOffset(x1, y1); - gridOffset = snap_to_grid(gridBounds, gridOffset); + gridOffset = snap_to_grid(gridBounds, gridOffset, + PreferSnapTo::ClosestGridVertex); // Now we calculate the difference from x1,y1 point and we can // use it to adjust all coordinates (x1, y1, x2, y2). diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index 692729086..377a9d1b3 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -337,6 +337,24 @@ bool StandbyState::onMouseMove(Editor* editor, MouseMessage* msg) return true; } +bool StandbyState::onDoubleClick(Editor* editor, MouseMessage* msg) +{ + if (editor->hasCapture()) + return false; + + tools::Ink* ink = editor->getCurrentEditorInk(); + + // Select a tile with double-click + if (ink->isSelection()) { + Command* selectTileCmd = + CommandsModule::instance()->getCommandByName(CommandId::SelectTile); + + UIContext::instance()->executeCommand(selectTileCmd); + } + + return false; +} + bool StandbyState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { tools::Ink* ink = editor->getCurrentEditorInk(); diff --git a/src/app/ui/editor/standby_state.h b/src/app/ui/editor/standby_state.h index edbfc9065..c5954c71d 100644 --- a/src/app/ui/editor/standby_state.h +++ b/src/app/ui/editor/standby_state.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -32,6 +32,7 @@ namespace app { virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; + virtual bool onDoubleClick(Editor* editor, ui::MouseMessage* msg) override; virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override; virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override; virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override; From e62f80842c45f187cfb1a217f776b38ba87cd122 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 19 Mar 2016 12:04:39 -0300 Subject: [PATCH 2/3] Avoid enter to tool loop after we select the tile --- src/app/ui/editor/standby_state.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index 377a9d1b3..1e477a2a3 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -350,6 +350,7 @@ bool StandbyState::onDoubleClick(Editor* editor, MouseMessage* msg) CommandsModule::instance()->getCommandByName(CommandId::SelectTile); UIContext::instance()->executeCommand(selectTileCmd); + return true; } return false; From 417e431a328358a1c75c4a818cad2f3eda2204ee Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 19 Mar 2016 12:09:03 -0300 Subject: [PATCH 3/3] Add different selection modes to SelectTile command Now we can add a tile using Shift+double click or substract one with Shift+Alt+double click. --- data/gui.xml | 8 ++++- src/app/commands/cmd_select_tile.cpp | 49 ++++++++++++++++++++++++---- src/app/ui/editor/standby_state.cpp | 12 ++++++- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/data/gui.xml b/data/gui.xml index 8355df820..2ebc57bc4 100644 --- a/data/gui.xml +++ b/data/gui.xml @@ -393,12 +393,18 @@ - + + + + + + + diff --git a/src/app/commands/cmd_select_tile.cpp b/src/app/commands/cmd_select_tile.cpp index 1c4a4190b..0de6d17a5 100644 --- a/src/app/commands/cmd_select_tile.cpp +++ b/src/app/commands/cmd_select_tile.cpp @@ -32,20 +32,37 @@ public: Command* clone() const override { return new SelectTileCommand(*this); } protected: - bool onEnabled(Context* context) override; - void onExecute(Context* context) override; + void onLoadParams(const Params& params) override; + bool onEnabled(Context* ctx) override; + void onExecute(Context* ctx) override; + std::string onGetFriendlyName() const override; + +private: + tools::SelectionMode m_mode; }; SelectTileCommand::SelectTileCommand() : Command("SelectTile", "Select Tile", CmdRecordableFlag) + , m_mode(tools::SelectionMode::DEFAULT) { } -bool SelectTileCommand::onEnabled(Context* context) +void SelectTileCommand::onLoadParams(const Params& params) { - return context->checkFlags(ContextFlags::ActiveDocumentIsWritable); + std::string mode = params.get("mode"); + if (mode == "add") + m_mode = tools::SelectionMode::ADD; + else if (mode == "subtract") + m_mode = tools::SelectionMode::SUBTRACT; + else + m_mode = tools::SelectionMode::DEFAULT; +} + +bool SelectTileCommand::onEnabled(Context* ctx) +{ + return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable); } void SelectTileCommand::onExecute(Context* ctx) @@ -60,12 +77,20 @@ void SelectTileCommand::onExecute(Context* ctx) auto& docPref = Preferences::instance().document(doc); base::UniquePtr mask(new Mask()); + + if (m_mode != tools::SelectionMode::DEFAULT) + mask->copyFrom(doc->mask()); + { - const gfx::Rect gridBounds = docPref.grid.bounds(); + gfx::Rect gridBounds = docPref.grid.bounds(); gfx::Point pos = current_editor->screenToEditor(ui::get_mouse_position()); pos = snap_to_grid(gridBounds, pos, PreferSnapTo::BoxOrigin); + gridBounds.setOrigin(pos); - mask->add(gfx::Rect(pos, gridBounds.size())); + if (m_mode != tools::SelectionMode::SUBTRACT) + mask->add(gridBounds); + else + mask->subtract(gridBounds); } // Set the new mask @@ -79,6 +104,18 @@ void SelectTileCommand::onExecute(Context* ctx) update_screen_for_document(doc); } +std::string SelectTileCommand::onGetFriendlyName() const +{ + std::string text = "Select Tile"; + + switch (m_mode) { + case tools::SelectionMode::ADD: text += " (Add)"; break; + case tools::SelectionMode::SUBTRACT: text += " (Subtract)"; break; + } + + return text; +} + Command* CommandFactory::createSelectTileCommand() { return new SelectTileCommand; diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index 1e477a2a3..7aebb9704 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -349,7 +349,17 @@ bool StandbyState::onDoubleClick(Editor* editor, MouseMessage* msg) Command* selectTileCmd = CommandsModule::instance()->getCommandByName(CommandId::SelectTile); - UIContext::instance()->executeCommand(selectTileCmd); + Params params; + switch (editor->getSelectionMode()) { + case tools::SelectionMode::ADD: + params.set("mode", "add"); + break; + case tools::SelectionMode::SUBTRACT: + params.set("mode", "subtract"); + break; + } + + UIContext::instance()->executeCommand(selectTileCmd, params); return true; }