From a3deb2063e37c67f421c4c5f44c1580470a618b9 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 28 Oct 2015 20:00:18 -0300 Subject: [PATCH] Add MovingSymmetryState to modify symmetry axis position --- src/app/CMakeLists.txt | 1 + src/app/pref/preferences.cpp | 30 +-- src/app/ui/editor/editor.cpp | 2 +- src/app/ui/editor/editor.h | 9 +- src/app/ui/editor/moving_symmetry_state.cpp | 83 ++++++++ src/app/ui/editor/moving_symmetry_state.h | 41 ++++ src/app/ui/editor/standby_state.cpp | 207 ++++++++++++++------ src/app/ui/editor/standby_state.h | 8 +- src/app/ui/editor/tool_loop_impl.cpp | 28 ++- 9 files changed, 321 insertions(+), 88 deletions(-) create mode 100644 src/app/ui/editor/moving_symmetry_state.cpp create mode 100644 src/app/ui/editor/moving_symmetry_state.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index d4e30a04e..8f11db80d 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -344,6 +344,7 @@ add_library(app-lib ui/editor/editor_view.cpp ui/editor/moving_cel_state.cpp ui/editor/moving_pixels_state.cpp + ui/editor/moving_symmetry_state.cpp ui/editor/navigate_state.cpp ui/editor/pivot_helpers.cpp ui/editor/pixels_movement.cpp diff --git a/src/app/pref/preferences.cpp b/src/app/pref/preferences.cpp index 1d992a7df..288797309 100644 --- a/src/app/pref/preferences.cpp +++ b/src/app/pref/preferences.cpp @@ -15,6 +15,7 @@ #include "app/resource_finder.h" #include "app/tools/ink.h" #include "app/tools/tool.h" +#include "doc/sprite.h" namespace app { @@ -92,23 +93,27 @@ ToolPreferences& Preferences::tool(tools::Tool* tool) } } -DocumentPreferences& Preferences::document(const app::Document* document) +DocumentPreferences& Preferences::document(const app::Document* doc) { - auto it = m_docs.find(document); + auto it = m_docs.find(doc); if (it != m_docs.end()) { return *it->second; } else { DocumentPreferences* docPref; - if (document) { + if (doc) { docPref = new DocumentPreferences(""); *docPref = this->document(nullptr); + + // Default values for symmetry + docPref->symmetry.xAxis.setDefaultValue(doc->sprite()->width()/2); + docPref->symmetry.yAxis.setDefaultValue(doc->sprite()->height()/2); } else docPref = new DocumentPreferences(""); - m_docs[document] = docPref; - serializeDocPref(document, docPref, false); + m_docs[doc] = docPref; + serializeDocPref(doc, docPref, false); return *docPref; } } @@ -151,18 +156,21 @@ void Preferences::serializeDocPref(const app::Document* doc, app::DocumentPrefer bool specific_file = false; if (doc) { - if (!doc->isAssociatedToFile()) + if (doc->isAssociatedToFile()) { + push_config_state(); + set_config_file(docConfigFileName(doc).c_str()); + specific_file = true; + } + else if (save) return; - - push_config_state(); - set_config_file(docConfigFileName(doc).c_str()); - specific_file = true; } if (save) docPref->save(); - else + else { + // Load default preferences, or preferences from .ini file. docPref->load(); + } if (specific_file) { flush_config_file(); diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index d9e8b652c..0e4afb119 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -633,7 +633,7 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc) } // Symmetry mode - { + if (Preferences::instance().symmetryMode.enabled()) { switch (docPref.symmetry.mode()) { case app::gen::SymmetryMode::NONE: // Do nothing diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h index ee8caede9..862faf0dd 100644 --- a/src/app/ui/editor/editor.h +++ b/src/app/ui/editor/editor.h @@ -68,8 +68,13 @@ namespace app { kShowOnionskin = 4, kShowOutside = 8, kShowDecorators = 16, - kDefaultEditorFlags = (kShowGrid | kShowMask | - kShowOnionskin | kShowOutside | kShowDecorators), + kShowSymmetryLine = 2, + kDefaultEditorFlags = (kShowGrid | + kShowMask | + kShowOnionskin | + kShowOutside | + kShowDecorators | + kShowSymmetryLine) }; enum class ZoomBehavior { diff --git a/src/app/ui/editor/moving_symmetry_state.cpp b/src/app/ui/editor/moving_symmetry_state.cpp new file mode 100644 index 000000000..490b0ed84 --- /dev/null +++ b/src/app/ui/editor/moving_symmetry_state.cpp @@ -0,0 +1,83 @@ +// Aseprite +// Copyright (C) 2015 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/ui/editor/moving_symmetry_state.h" + +#include "app/ui/editor/editor.h" +#include "app/ui/status_bar.h" +#include "ui/message.h" + +namespace app { + +using namespace ui; + +MovingSymmetryState::MovingSymmetryState(Editor* editor, MouseMessage* msg, + app::gen::SymmetryMode mode, + Option& symmetryAxis) + : m_symmetryMode(mode) + , m_symmetryAxis(symmetryAxis) + , m_symmetryAxisStart(symmetryAxis()) +{ + m_mouseStart = editor->screenToEditor(msg->position()); + editor->captureMouse(); +} + +MovingSymmetryState::~MovingSymmetryState() +{ +} + +bool MovingSymmetryState::onMouseUp(Editor* editor, MouseMessage* msg) +{ + editor->backToPreviousState(); + editor->releaseMouse(); + return true; +} + +bool MovingSymmetryState::onMouseMove(Editor* editor, MouseMessage* msg) +{ + gfx::Point newCursorPos = editor->screenToEditor(msg->position()); + gfx::Point delta = newCursorPos - m_mouseStart; + int pos = 0; + + switch (m_symmetryMode) { + case app::gen::SymmetryMode::HORIZONTAL: + pos = m_symmetryAxisStart + delta.x; + pos = MID(1, pos, editor->sprite()->width()-1); + break; + case app::gen::SymmetryMode::VERTICAL: + pos = m_symmetryAxisStart + delta.y; + pos = MID(1, pos, editor->sprite()->height()-1); + break; + } + m_symmetryAxis(pos); + + // Redraw the editor. + editor->invalidate(); + + // Use StandbyState implementation + return StandbyState::onMouseMove(editor, msg); +} + +bool MovingSymmetryState::onUpdateStatusBar(Editor* editor) +{ + if (m_symmetryMode == app::gen::SymmetryMode::HORIZONTAL) + StatusBar::instance()->setStatusText + (0, "Left %3d Right %3d", m_symmetryAxis(), + editor->sprite()->width() - m_symmetryAxis()); + else + StatusBar::instance()->setStatusText + (0, "Top %3d Bottom %3d", m_symmetryAxis(), + editor->sprite()->height() - m_symmetryAxis()); + + return true; +} + +} // namespace app diff --git a/src/app/ui/editor/moving_symmetry_state.h b/src/app/ui/editor/moving_symmetry_state.h new file mode 100644 index 000000000..dcfe4be59 --- /dev/null +++ b/src/app/ui/editor/moving_symmetry_state.h @@ -0,0 +1,41 @@ +// Aseprite +// Copyright (C) 2015 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. + +#ifndef APP_UI_EDITOR_MOVING_SYMMETRY_STATE_H_INCLUDED +#define APP_UI_EDITOR_MOVING_SYMMETRY_STATE_H_INCLUDED +#pragma once + +#include "app/pref/preferences.h" +#include "app/ui/editor/standby_state.h" + +namespace app { + class Editor; + + class MovingSymmetryState : public StandbyState { + public: + MovingSymmetryState(Editor* editor, ui::MouseMessage* msg, + app::gen::SymmetryMode mode, + Option& symmetryAxis); + virtual ~MovingSymmetryState(); + + virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; + virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; + virtual bool onUpdateStatusBar(Editor* editor) override; + + virtual bool requireBrushPreview() override { return false; } + + private: + app::gen::SymmetryMode m_symmetryMode; + Option& m_symmetryAxis; + int m_symmetryAxisStart; + int m_symmetryAxisNew; + gfx::Point m_mouseStart; + }; + +} // namespace app + +#endif diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index 37c2a63e8..545cd51a7 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -27,6 +27,7 @@ #include "app/ui/editor/editor_customization_delegate.h" #include "app/ui/editor/handle_type.h" #include "app/ui/editor/moving_cel_state.h" +#include "app/ui/editor/moving_symmetry_state.h" #include "app/ui/editor/moving_pixels_state.h" #include "app/ui/editor/pivot_helpers.h" #include "app/ui/editor/pixels_movement.h" @@ -34,6 +35,7 @@ #include "app/ui/editor/tool_loop_impl.h" #include "app/ui/editor/transform_handles.h" #include "app/ui/editor/zooming_state.h" +#include "app/ui/skin/skin_theme.h" #include "app/ui/status_bar.h" #include "app/ui_context.h" #include "app/util/new_image_from_mask.h" @@ -44,6 +46,7 @@ #include "doc/sprite.h" #include "fixmath/fixmath.h" #include "gfx/rect.h" +#include "she/surface.h" #include "ui/alert.h" #include "ui/message.h" #include "ui/system.h" @@ -225,7 +228,9 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg) if (clickedInk->isSelection()) { // Transform selected pixels - if (editor->isActive() && document->isMaskVisible() && m_decorator->getTransformHandles(editor)) { + if (editor->isActive() && + document->isMaskVisible() && + m_decorator->getTransformHandles(editor)) { TransformHandles* transfHandles = m_decorator->getTransformHandles(editor); // Get the handle covered by the mouse. @@ -264,6 +269,22 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg) } } + // Move symmetry + gfx::Rect box1, box2; + if (m_decorator->getSymmetryHandles(editor, box1, box2) && + (box1.contains(msg->position()) || + box2.contains(msg->position()))) { + auto& symmetry = Preferences::instance().document(editor->document()).symmetry; + auto mode = symmetry.mode(); + bool horz = (mode == app::gen::SymmetryMode::HORIZONTAL); + auto& axis = (horz ? symmetry.xAxis: + symmetry.yAxis); + editor->setState( + EditorStatePtr(new MovingSymmetryState(editor, msg, + mode, axis))); + return true; + } + // Start the Tool-Loop if (layer) { tools::ToolLoop* toolLoop = create_tool_loop(editor, context); @@ -305,13 +326,14 @@ bool StandbyState::onMouseMove(Editor* editor, MouseMessage* msg) bool StandbyState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { tools::Ink* ink = editor->getCurrentEditorInk(); + + // See if the cursor is in some selection handle. + if (m_decorator->onSetCursor(ink, editor, mouseScreenPos)) + return true; + if (ink) { // If the current tool change selection (e.g. rectangular marquee, etc.) if (ink->isSelection()) { - // See if the cursor is in some selection handle. - if (m_decorator->onSetCursor(editor, mouseScreenPos)) - return true; - // Move pixels if (editor->isInsideSelection()) { EditorCustomizationDelegate* customization = editor->getCustomizationDelegate(); @@ -521,69 +543,87 @@ TransformHandles* StandbyState::Decorator::getTransformHandles(Editor* editor) return m_transfHandles; } -bool StandbyState::Decorator::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) +bool StandbyState::Decorator::onSetCursor(tools::Ink* ink, Editor* editor, const gfx::Point& mouseScreenPos) { - if (!editor->isActive() || - !editor->document()->isMaskVisible()) + if (!editor->isActive()) return false; - const gfx::Transformation transformation(m_standbyState->getTransformation(editor)); - TransformHandles* tr = getTransformHandles(editor); - HandleType handle = tr->getHandleAtPoint( - editor, mouseScreenPos, transformation); + if (ink && ink->isSelection() && editor->document()->isMaskVisible()) { + const gfx::Transformation transformation(m_standbyState->getTransformation(editor)); + TransformHandles* tr = getTransformHandles(editor); + HandleType handle = tr->getHandleAtPoint( + editor, mouseScreenPos, transformation); - CursorType newCursor = kArrowCursor; + CursorType newCursor = kArrowCursor; - switch (handle) { - case ScaleNWHandle: newCursor = kSizeNWCursor; break; - case ScaleNHandle: newCursor = kSizeNCursor; break; - case ScaleNEHandle: newCursor = kSizeNECursor; break; - case ScaleWHandle: newCursor = kSizeWCursor; break; - case ScaleEHandle: newCursor = kSizeECursor; break; - case ScaleSWHandle: newCursor = kSizeSWCursor; break; - case ScaleSHandle: newCursor = kSizeSCursor; break; - case ScaleSEHandle: newCursor = kSizeSECursor; break; - case RotateNWHandle: newCursor = kRotateNWCursor; break; - case RotateNHandle: newCursor = kRotateNCursor; break; - case RotateNEHandle: newCursor = kRotateNECursor; break; - case RotateWHandle: newCursor = kRotateWCursor; break; - case RotateEHandle: newCursor = kRotateECursor; break; - case RotateSWHandle: newCursor = kRotateSWCursor; break; - case RotateSHandle: newCursor = kRotateSCursor; break; - case RotateSEHandle: newCursor = kRotateSECursor; break; - case PivotHandle: newCursor = kHandCursor; break; - default: - return false; + switch (handle) { + case ScaleNWHandle: newCursor = kSizeNWCursor; break; + case ScaleNHandle: newCursor = kSizeNCursor; break; + case ScaleNEHandle: newCursor = kSizeNECursor; break; + case ScaleWHandle: newCursor = kSizeWCursor; break; + case ScaleEHandle: newCursor = kSizeECursor; break; + case ScaleSWHandle: newCursor = kSizeSWCursor; break; + case ScaleSHandle: newCursor = kSizeSCursor; break; + case ScaleSEHandle: newCursor = kSizeSECursor; break; + case RotateNWHandle: newCursor = kRotateNWCursor; break; + case RotateNHandle: newCursor = kRotateNCursor; break; + case RotateNEHandle: newCursor = kRotateNECursor; break; + case RotateWHandle: newCursor = kRotateWCursor; break; + case RotateEHandle: newCursor = kRotateECursor; break; + case RotateSWHandle: newCursor = kRotateSWCursor; break; + case RotateSHandle: newCursor = kRotateSCursor; break; + case RotateSEHandle: newCursor = kRotateSECursor; break; + case PivotHandle: newCursor = kHandCursor; break; + default: + return false; + } + + // Adjust the cursor depending the current transformation angle. + fixmath::fixed angle = fixmath::ftofix(128.0 * transformation.angle() / PI); + angle = fixmath::fixadd(angle, fixmath::itofix(16)); + angle &= (255<<16); + angle >>= 16; + angle /= 32; + + if (newCursor >= kSizeNCursor && newCursor <= kSizeNWCursor) { + size_t num = sizeof(rotated_size_cursors) / sizeof(rotated_size_cursors[0]); + size_t c; + for (c=num-1; c>0; --c) + if (rotated_size_cursors[c] == newCursor) + break; + + newCursor = rotated_size_cursors[(c+angle) % num]; + } + else if (newCursor >= kRotateNCursor && newCursor <= kRotateNWCursor) { + size_t num = sizeof(rotated_rotate_cursors) / sizeof(rotated_rotate_cursors[0]); + size_t c; + for (c=num-1; c>0; --c) + if (rotated_rotate_cursors[c] == newCursor) + break; + + newCursor = rotated_rotate_cursors[(c+angle) % num]; + } + + editor->showMouseCursor(newCursor); + return true; } - // Adjust the cursor depending the current transformation angle. - fixmath::fixed angle = fixmath::ftofix(128.0 * transformation.angle() / PI); - angle = fixmath::fixadd(angle, fixmath::itofix(16)); - angle &= (255<<16); - angle >>= 16; - angle /= 32; - - if (newCursor >= kSizeNCursor && newCursor <= kSizeNWCursor) { - size_t num = sizeof(rotated_size_cursors) / sizeof(rotated_size_cursors[0]); - size_t c; - for (c=num-1; c>0; --c) - if (rotated_size_cursors[c] == newCursor) + gfx::Rect box1, box2; + if (getSymmetryHandles(editor, box1, box2) && + (box1.contains(mouseScreenPos) || + box2.contains(mouseScreenPos))) { + switch (Preferences::instance().document(editor->document()).symmetry.mode()) { + case app::gen::SymmetryMode::HORIZONTAL: + editor->showMouseCursor(kSizeWECursor); break; - - newCursor = rotated_size_cursors[(c+angle) % num]; - } - else if (newCursor >= kRotateNCursor && newCursor <= kRotateNWCursor) { - size_t num = sizeof(rotated_rotate_cursors) / sizeof(rotated_rotate_cursors[0]); - size_t c; - for (c=num-1; c>0; --c) - if (rotated_rotate_cursors[c] == newCursor) + case app::gen::SymmetryMode::VERTICAL: + editor->showMouseCursor(kSizeNSCursor); break; - - newCursor = rotated_rotate_cursors[(c+angle) % num]; + } + return true; } - editor->showMouseCursor(newCursor); - return true; + return false; } void StandbyState::Decorator::preRenderDecorator(EditorPreRender* render) @@ -607,6 +647,59 @@ void StandbyState::Decorator::postRenderDecorator(EditorPostRender* render) getTransformHandles(editor)->drawHandles(editor, m_standbyState->getTransformation(editor)); } + + // Draw transformation handles (if the mask is visible and isn't frozen). + gfx::Rect box1, box2; + if (StandbyState::Decorator::getSymmetryHandles(editor, box1, box2)) { + skin::SkinTheme* theme = static_cast(CurrentTheme::get()); + she::Surface* part = theme->parts.transformationHandle()->getBitmap(0); + ScreenGraphics g; + g.drawRgbaSurface(part, box1.x, box1.y); + g.drawRgbaSurface(part, box2.x, box2.y); + } +} + +bool StandbyState::Decorator::getSymmetryHandles(Editor* editor, gfx::Rect& box1, gfx::Rect& box2) +{ + // Draw transformation handles (if the mask is visible and isn't frozen). + if (editor->isActive() && + editor->editorFlags() & Editor::kShowSymmetryLine && + Preferences::instance().symmetryMode.enabled()) { + const auto& symmetry = Preferences::instance().document(editor->document()).symmetry; + auto mode = symmetry.mode(); + if (mode != app::gen::SymmetryMode::NONE) { + bool horz = (mode == app::gen::SymmetryMode::HORIZONTAL); + int pos = (horz ? symmetry.xAxis(): + symmetry.yAxis()); + gfx::Rect spriteBounds = editor->sprite()->bounds(); + skin::SkinTheme* theme = static_cast(CurrentTheme::get()); + she::Surface* part = theme->parts.transformationHandle()->getBitmap(0); + gfx::Point pt1, pt2; + if (horz) { + pt1 = gfx::Point(spriteBounds.x+pos, spriteBounds.y); + pt1 = editor->editorToScreen(pt1); + pt2 = gfx::Point(spriteBounds.x+pos, spriteBounds.y+spriteBounds.h); + pt2 = editor->editorToScreen(pt2); + pt1.y -= part->height(); + pt1.x -= part->width()/2; + pt2.x -= part->width()/2; + } + else { + pt1 = gfx::Point(spriteBounds.x, spriteBounds.y+pos); + pt1 = editor->editorToScreen(pt1); + pt2 = gfx::Point(spriteBounds.x+spriteBounds.w, spriteBounds.y+pos); + pt2 = editor->editorToScreen(pt2); + pt1.x -= part->width(); + pt1.y -= part->height()/2; + pt2.y -= part->height()/2; + } + + box1 = gfx::Rect(pt1.x, pt1.y, part->width(), part->height()); + box2 = gfx::Rect(pt2.x, pt2.y, part->width(), part->height()); + return true; + } + } + return false; } } // namespace app diff --git a/src/app/ui/editor/standby_state.h b/src/app/ui/editor/standby_state.h index 390ec3e4b..bace98c46 100644 --- a/src/app/ui/editor/standby_state.h +++ b/src/app/ui/editor/standby_state.h @@ -16,6 +16,10 @@ #include "gfx/transformation.h" namespace app { + namespace tools { + class Ink; + } + class TransformHandles; class StandbyState : public StateWithWheelBehavior { @@ -54,12 +58,14 @@ namespace app { virtual ~Decorator(); TransformHandles* getTransformHandles(Editor* editor); + bool getSymmetryHandles(Editor* editor, gfx::Rect& box1, gfx::Rect& box2); - bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos); + bool onSetCursor(tools::Ink* ink, Editor* editor, const gfx::Point& mouseScreenPos); // EditorDecorator overrides void preRenderDecorator(EditorPreRender* render) override; void postRenderDecorator(EditorPostRender* render) override; + private: TransformHandles* m_transfHandles; StandbyState* m_standbyState; diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp index 638714a32..218965caa 100644 --- a/src/app/ui/editor/tool_loop_impl.cpp +++ b/src/app/ui/editor/tool_loop_impl.cpp @@ -141,25 +141,21 @@ public: } // Symmetry mode - switch (m_docPref.symmetry.mode()) { + if (Preferences::instance().symmetryMode.enabled()) { + switch (m_docPref.symmetry.mode()) { - case app::gen::SymmetryMode::NONE: - ASSERT(m_symmetry == nullptr); - break; + case app::gen::SymmetryMode::NONE: + ASSERT(m_symmetry == nullptr); + break; - case app::gen::SymmetryMode::HORIZONTAL: - if (m_docPref.symmetry.xAxis() == 0) - m_docPref.symmetry.xAxis(m_sprite->width()/2); + case app::gen::SymmetryMode::HORIZONTAL: + m_symmetry.reset(new app::tools::HorizontalSymmetry(m_docPref.symmetry.xAxis())); + break; - m_symmetry.reset(new app::tools::HorizontalSymmetry(m_docPref.symmetry.xAxis())); - break; - - case app::gen::SymmetryMode::VERTICAL: - if (m_docPref.symmetry.yAxis() == 0) - m_docPref.symmetry.yAxis(m_sprite->height()/2); - - m_symmetry.reset(new app::tools::VerticalSymmetry(m_docPref.symmetry.yAxis())); - break; + case app::gen::SymmetryMode::VERTICAL: + m_symmetry.reset(new app::tools::VerticalSymmetry(m_docPref.symmetry.yAxis())); + break; + } } // Ignore opacity for these inks