[aseprite] 59/128: Fix synchronization of different UI elements depending on the active tool

Tobias Hansen thansen at moszumanska.debian.org
Mon May 9 21:24:23 UTC 2016


This is an automated email from the git hooks/post-receive script.

thansen pushed a commit to branch master
in repository aseprite.

commit a09b00dba8bf303941c8ef03d1582f3b4f80f0a6
Author: David Capello <davidcapello at gmail.com>
Date:   Mon Apr 25 15:20:53 2016 -0300

    Fix synchronization of different UI elements depending on the active tool
    
    With this patch now the context bar shows the real active tool
    preferences. For example, if we choose the option to use the Eraser
    tool with right-click, now right-clicking will draw with the Eraser
    brush size (instead of using the selected tool/left-click tool brush
    size).
    
    Changes:
    * Removed the toolBox.activeTool() field from preferences
      (as the option isn't persisted between sessions)
    * Added an observable app::tools::ActiveToolManager class to sync all
      UI elements that depend on the active tool and ink.
    * Moved a lot of "active tool" logic from app::Editor to the
      new ActiveToolManager
    * Moved app::tools::ToolLoopManager::Pointer as an indepedent
      app::tools::Pointer class
---
 data/pref.xml                             |   3 -
 src/app/CMakeLists.txt                    |   1 +
 src/app/app.cpp                           |  16 ++-
 src/app/app.h                             |   2 +
 src/app/commands/cmd_new_brush.cpp        |  10 +-
 src/app/tools/active_tool.cpp             | 220 ++++++++++++++++++++++++++++++
 src/app/tools/active_tool.h               |  72 ++++++++++
 src/app/tools/active_tool_observer.h      |  31 +++++
 src/app/tools/pointer.h                   |  39 ++++++
 src/app/tools/tool_loop_manager.cpp       |   6 +-
 src/app/tools/tool_loop_manager.h         | 145 +++++++++-----------
 src/app/ui/context_bar.cpp                |  24 ++--
 src/app/ui/context_bar.h                  |  13 +-
 src/app/ui/editor/drawing_state.cpp       |  24 +---
 src/app/ui/editor/editor.cpp              | 196 +++++---------------------
 src/app/ui/editor/editor.h                |  24 ++--
 src/app/ui/editor/editor_state.h          |   3 +-
 src/app/ui/editor/glue.h                  |  35 +++++
 src/app/ui/editor/moving_pixels_state.cpp |  10 +-
 src/app/ui/editor/moving_pixels_state.h   |   4 +-
 src/app/ui/editor/select_box_state.cpp    |   2 +-
 src/app/ui/editor/standby_state.cpp       |   9 +-
 src/app/ui/editor/standby_state.h         |   3 +-
 src/app/ui/main_window.cpp                |   2 +-
 src/app/ui/status_bar.cpp                 |  17 +--
 src/app/ui/status_bar.h                   |   8 +-
 src/app/ui/toolbar.cpp                    |  25 +++-
 src/app/ui/toolbar.h                      |   9 +-
 28 files changed, 606 insertions(+), 347 deletions(-)

diff --git a/data/pref.xml b/data/pref.xml
index 299b18e..9b6daa1 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -128,9 +128,6 @@
       <option id="wheel_model" type="int" default="0" />
       <option id="harmony" type="int" default="0" />
     </section>
-    <section id="tool_box">
-      <option id="active_tool" type="std::string" default=""pencil"" />
-    </section>
     <section id="updater">
       <option id="inits" type="int" default="0" migrate="Updater.Inits" />
       <option id="exits" type="int" default="0" migrate="Updater.Exits" />
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 74e2e66..2c0e402 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -330,6 +330,7 @@ add_library(app-lib
   shell.cpp
   snap_to_grid.cpp
   thumbnail_generator.cpp
+  tools/active_tool.cpp
   tools/ink_type.cpp
   tools/intertwine.cpp
   tools/pick_ink.cpp
diff --git a/src/app/app.cpp b/src/app/app.cpp
index c028111..53fa3e2 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -39,6 +39,7 @@
 #include "app/script/app_scripting.h"
 #include "app/send_crash.h"
 #include "app/shell.h"
+#include "app/tools/active_tool.h"
 #include "app/tools/tool_box.h"
 #include "app/ui/color_bar.h"
 #include "app/ui/document_view.h"
@@ -89,12 +90,6 @@ class App::CoreModules {
 public:
   ConfigModule m_configModule;
   Preferences m_preferences;
-
-  CoreModules() {
-    // Reset the active tool ("pencil" is the default one).
-    // We don't want to keep the selected tool from previous session.
-    m_preferences.toolBox.activeTool(tools::WellKnownTools::Pencil);
-  }
 };
 
 class App::Modules {
@@ -102,6 +97,7 @@ public:
   LoggerModule m_loggerModule;
   FileSystemModule m_file_system_module;
   tools::ToolBox m_toolbox;
+  tools::ActiveToolManager m_activeToolManager;
   CommandsModule m_commands_modules;
   UIContext m_ui_context;
   RecentFiles m_recent_files;
@@ -112,6 +108,7 @@ public:
 
   Modules(bool createLogInDesktop)
     : m_loggerModule(createLogInDesktop)
+    , m_activeToolManager(&m_toolbox)
     , m_recovery(nullptr) {
   }
 
@@ -793,7 +790,12 @@ tools::ToolBox* App::toolBox() const
 
 tools::Tool* App::activeTool() const
 {
-  return toolBox()->getToolById(preferences().toolBox.activeTool());
+  return m_modules->m_activeToolManager.activeTool();
+}
+
+tools::ActiveToolManager* App::activeToolManager() const
+{
+  return &m_modules->m_activeToolManager;
 }
 
 RecentFiles* App::recentFiles() const
diff --git a/src/app/app.h b/src/app/app.h
index 6f3ee5e..ea10618 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -42,6 +42,7 @@ namespace app {
   class Workspace;
 
   namespace tools {
+    class ActiveToolManager;
     class Tool;
     class ToolBox;
   }
@@ -69,6 +70,7 @@ namespace app {
 
     tools::ToolBox* toolBox() const;
     tools::Tool* activeTool() const;
+    tools::ActiveToolManager* activeToolManager() const;
     RecentFiles* recentFiles() const;
     MainWindow* mainWindow() const { return m_mainWindow; }
     Workspace* workspace() const;
diff --git a/src/app/commands/cmd_new_brush.cpp b/src/app/commands/cmd_new_brush.cpp
index d0c5532..70286bb 100644
--- a/src/app/commands/cmd_new_brush.cpp
+++ b/src/app/commands/cmd_new_brush.cpp
@@ -16,6 +16,7 @@
 #include "app/console.h"
 #include "app/context_access.h"
 #include "app/modules/editors.h"
+#include "app/tools/active_tool.h"
 #include "app/tools/ink.h"
 #include "app/tools/tool_box.h"
 #include "app/transaction.h"
@@ -126,7 +127,7 @@ void NewBrushCommand::onQuickboxEnd(Editor* editor, const gfx::Rect& rect, ui::M
   // Update the context bar
   // TODO find a way to avoid all these singletons. Maybe a simple
   // signal in the context like "brush has changed" could be enough.
-  App::instance()->contextBar()->updateForCurrentTool();
+  App::instance()->contextBar()->updateForActiveTool();
 
   editor->backToPreviousState();
 }
@@ -168,8 +169,11 @@ void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
 
 void NewBrushCommand::selectPencilTool()
 {
-  if (App::instance()->activeTool()->getInk(0)->isSelection())
-    Preferences::instance().toolBox.activeTool(tools::WellKnownTools::Pencil);
+  App* app = App::instance();
+  if (app->activeToolManager()->selectedTool()->getInk(0)->isSelection()) {
+    app->activeToolManager()->setSelectedTool(
+      app->toolBox()->getToolById(tools::WellKnownTools::Pencil));
+  }
 }
 
 Command* CommandFactory::createNewBrushCommand()
diff --git a/src/app/tools/active_tool.cpp b/src/app/tools/active_tool.cpp
new file mode 100644
index 0000000..64d86cc
--- /dev/null
+++ b/src/app/tools/active_tool.cpp
@@ -0,0 +1,220 @@
+// Aseprite
+// Copyright (C) 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/tools/active_tool.h"
+
+#include "app/pref/preferences.h"
+#include "app/tools/active_tool_observer.h"
+#include "app/tools/ink.h"
+#include "app/tools/pointer.h"
+#include "app/tools/tool_box.h"
+#include "app/ui/color_bar.h"
+
+namespace app {
+namespace tools {
+
+class ActiveToolChangeTrigger {
+public:
+  ActiveToolChangeTrigger(ActiveToolManager* manager)
+    : m_manager(manager)
+    , m_oldTool(manager->activeTool()) {
+  }
+
+  ~ActiveToolChangeTrigger() {
+    Tool* newTool = m_manager->activeTool();
+    if (m_oldTool != newTool) {
+      m_manager->notifyObservers(
+        &ActiveToolObserver::onActiveToolChange, newTool);
+    }
+  }
+
+private:
+  ActiveToolManager* m_manager;
+  Tool* m_oldTool;
+};
+
+ActiveToolManager::ActiveToolManager(ToolBox* toolbox)
+  : m_toolbox(toolbox)
+  , m_quickTool(nullptr)
+  , m_rightClick(false)
+  , m_rightClickTool(nullptr)
+  , m_rightClickInk(nullptr)
+  , m_proximityTool(nullptr)
+  , m_selectedTool(m_toolbox->getToolById(WellKnownTools::Pencil)) // "pencil" is the active tool by default
+{
+}
+
+Tool* ActiveToolManager::activeTool() const
+{
+  if (m_quickTool)
+    return m_quickTool;
+
+  if (m_rightClickTool)
+    return m_rightClickTool;
+
+  if (m_proximityTool)
+    return m_proximityTool;
+
+  // Active tool should never returns null
+  ASSERT(m_selectedTool);
+  return m_selectedTool;
+}
+
+Ink* ActiveToolManager::activeInk() const
+{
+  if (!m_quickTool && m_rightClickInk)
+    return m_rightClickInk;
+
+  Tool* tool = activeTool();
+  Ink* ink = tool->getInk(m_rightClick ? 1: 0);
+  if (ink->isPaint() && !ink->isEffect()) {
+    tools::InkType inkType = Preferences::instance().tool(tool).ink();
+    const char* id = nullptr;
+
+    switch (inkType) {
+
+      case tools::InkType::SIMPLE: {
+        id = tools::WellKnownInks::Paint;
+
+        ColorBar* colorbar = ColorBar::instance();
+        app::Color color = (m_rightClick ? colorbar->getBgColor():
+                                           colorbar->getFgColor());
+        if (color.getAlpha() == 0)
+          id = tools::WellKnownInks::PaintCopy;
+        break;
+      }
+
+      case tools::InkType::ALPHA_COMPOSITING:
+        id = tools::WellKnownInks::Paint;
+        break;
+      case tools::InkType::COPY_COLOR:
+        id = tools::WellKnownInks::PaintCopy;
+        break;
+      case tools::InkType::LOCK_ALPHA:
+        id = tools::WellKnownInks::PaintLockAlpha;
+        break;
+      case tools::InkType::SHADING:
+        id = tools::WellKnownInks::Shading;
+        break;
+    }
+
+    if (id)
+      ink = m_toolbox->getInkById(id);
+  }
+
+  return ink;
+}
+
+Tool* ActiveToolManager::quickTool() const
+{
+  return m_quickTool;
+}
+
+Tool* ActiveToolManager::selectedTool() const
+{
+  return m_selectedTool;
+}
+
+void ActiveToolManager::newToolSelectedInToolBar(Tool* tool)
+{
+  ActiveToolChangeTrigger trigger(this);
+  m_selectedTool = tool;
+}
+
+void ActiveToolManager::newQuickToolSelectedFromEditor(Tool* tool)
+{
+  ActiveToolChangeTrigger trigger(this);
+  m_quickTool = tool;
+}
+
+void ActiveToolManager::regularTipProximity()
+{
+  if (m_proximityTool != nullptr) {
+    ActiveToolChangeTrigger trigger(this);
+    m_proximityTool = nullptr;
+  }
+}
+
+void ActiveToolManager::eraserTipProximity()
+{
+  Tool* eraser = m_toolbox->getToolById(WellKnownTools::Eraser);
+  if (m_proximityTool != eraser) {
+    ActiveToolChangeTrigger trigger(this);
+    m_proximityTool = eraser;
+  }
+}
+
+void ActiveToolManager::pressButton(const Pointer& pointer)
+{
+  ActiveToolChangeTrigger trigger(this);
+  Tool* tool = nullptr;
+  Ink* ink = nullptr;
+
+  if (pointer.button() == Pointer::Right) {
+    m_rightClick = true;
+
+    if (isToolAffectedByRightClickMode(activeTool())) {
+      switch (Preferences::instance().editor.rightClickMode()) {
+        case app::gen::RightClickMode::PAINT_BGCOLOR:
+          // Do nothing, use the active tool
+          break;
+        case app::gen::RightClickMode::PICK_FGCOLOR:
+          tool = m_toolbox->getToolById(WellKnownTools::Eyedropper);
+          ink = m_toolbox->getInkById(tools::WellKnownInks::PickFg);
+          break;
+        case app::gen::RightClickMode::ERASE:
+          tool = m_toolbox->getToolById(WellKnownTools::Eraser);
+          ink = m_toolbox->getInkById(tools::WellKnownInks::Eraser);
+          break;
+        case app::gen::RightClickMode::SCROLL:
+          tool = m_toolbox->getToolById(WellKnownTools::Hand);
+          ink = m_toolbox->getInkById(tools::WellKnownInks::Scroll);
+          break;
+      }
+    }
+  }
+  else {
+    m_rightClick = false;
+  }
+
+  m_rightClickTool = tool;
+  m_rightClickInk = ink;
+}
+
+void ActiveToolManager::releaseButtons()
+{
+  ActiveToolChangeTrigger trigger(this);
+
+  m_rightClick = false;
+  m_rightClickTool = nullptr;
+  m_rightClickInk = nullptr;
+}
+
+void ActiveToolManager::setSelectedTool(Tool* tool)
+{
+  ActiveToolChangeTrigger trigger(this);
+
+  m_selectedTool = tool;
+  notifyObservers(&ActiveToolObserver::onSelectedToolChange, tool);
+}
+
+// static
+bool ActiveToolManager::isToolAffectedByRightClickMode(Tool* tool)
+{
+  bool shadingMode = (Preferences::instance().tool(tool).ink() == InkType::SHADING);
+  return
+    ((tool->getInk(0)->isPaint() && !shadingMode) ||
+     (tool->getInk(0)->isEffect())) &&
+    (!tool->getInk(0)->isEraser());
+}
+
+} // namespace tools
+} // namespace app
diff --git a/src/app/tools/active_tool.h b/src/app/tools/active_tool.h
new file mode 100644
index 0000000..f7be361
--- /dev/null
+++ b/src/app/tools/active_tool.h
@@ -0,0 +1,72 @@
+// Aseprite
+// Copyright (C) 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.
+
+#ifndef APP_TOOLS_ACTIVE_TOOL_H_INCLUDED
+#define APP_TOOLS_ACTIVE_TOOL_H_INCLUDED
+#pragma once
+
+#include "base/observable.h"
+
+namespace app {
+namespace tools {
+
+class ActiveToolObserver;
+class Ink;
+class Pointer;
+class Tool;
+class ToolBox;
+
+// Manages the coordination between different UI elements that show
+// information about the active tool.
+class ActiveToolManager : public base::Observable<ActiveToolObserver> {
+public:
+  ActiveToolManager(ToolBox* toolbox);
+
+  Tool* activeTool() const;
+  Ink* activeInk() const;
+
+  // Returns the quick tool.
+  Tool* quickTool() const;
+
+  // Returns the selected tool in the toolbar/box.
+  Tool* selectedTool() const;
+
+  // These are different events that came from UI elements and
+  // modify the active tool.
+  void newToolSelectedInToolBar(Tool* tool);
+  void newQuickToolSelectedFromEditor(Tool* tool);
+  void regularTipProximity();
+  void eraserTipProximity();
+  void pressButton(const Pointer& pointer);
+  void releaseButtons();
+  void setSelectedTool(Tool* tool);
+
+private:
+  static bool isToolAffectedByRightClickMode(Tool* tool);
+
+  ToolBox* m_toolbox;
+
+  // Quick tool in the active sprite editor (activated by keyboard
+  // shortuts).
+  Tool* m_quickTool;
+
+  // Special tool by stylus proximity.
+  bool m_rightClick;
+  Tool* m_rightClickTool;
+  Ink* m_rightClickInk;
+
+  // Special tool by stylus proximity (e.g. eraser).
+  Tool* m_proximityTool;
+
+  // Selected tool in the toolbar/toolbox.
+  Tool* m_selectedTool;
+};
+
+} // namespace tools
+} // namespace app
+
+#endif
diff --git a/src/app/tools/active_tool_observer.h b/src/app/tools/active_tool_observer.h
new file mode 100644
index 0000000..966c818
--- /dev/null
+++ b/src/app/tools/active_tool_observer.h
@@ -0,0 +1,31 @@
+// Aseprite
+// Copyright (C) 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.
+
+#ifndef APP_TOOLS_ACTIVE_TOOL_OBSERVER_H_INCLUDED
+#define APP_TOOLS_ACTIVE_TOOL_OBSERVER_H_INCLUDED
+#pragma once
+
+namespace app {
+namespace tools {
+
+  class Tool;
+
+  class ActiveToolObserver {
+  public:
+    virtual ~ActiveToolObserver() { }
+
+    // Called when a new tool is active.
+    virtual void onActiveToolChange(tools::Tool* tool) { }
+
+    // Called when a new tool is selected in the tool box.
+    virtual void onSelectedToolChange(tools::Tool* tool) { }
+  };
+
+} // namespace tools
+} // namespace app
+
+#endif
diff --git a/src/app/tools/pointer.h b/src/app/tools/pointer.h
new file mode 100644
index 0000000..256eb26
--- /dev/null
+++ b/src/app/tools/pointer.h
@@ -0,0 +1,39 @@
+// Aseprite
+// Copyright (C) 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.
+
+#ifndef APP_TOOLS_POINTER_H_INCLUDED
+#define APP_TOOLS_POINTER_H_INCLUDED
+#pragma once
+
+#include "gfx/point.h"
+
+namespace app {
+namespace tools {
+
+// Simple container of mouse events information.
+class Pointer {
+public:
+  enum Button { None, Left, Middle, Right };
+
+  Pointer()
+    : m_point(0, 0), m_button(None) { }
+
+  Pointer(const gfx::Point& point, Button button)
+    : m_point(point), m_button(button) { }
+
+  const gfx::Point& point() const { return m_point; }
+  Button button() const { return m_button; }
+
+private:
+  gfx::Point m_point;
+  Button m_button;
+};
+
+} // namespace tools
+} // namespace app
+
+#endif
diff --git a/src/app/tools/tool_loop_manager.cpp b/src/app/tools/tool_loop_manager.cpp
index 387f34d..06f2da7 100644
--- a/src/app/tools/tool_loop_manager.cpp
+++ b/src/app/tools/tool_loop_manager.cpp
@@ -64,7 +64,7 @@ void ToolLoopManager::notifyToolLoopModifiersChange()
   if (isCanceled())
     return;
 
-  if (m_lastPointer.getButton() != Pointer::None)
+  if (m_lastPointer.button() != Pointer::None)
     movement(m_lastPointer);
 }
 
@@ -76,8 +76,8 @@ void ToolLoopManager::pressButton(const Pointer& pointer)
     return;
 
   // If the user pressed the other mouse button...
-  if ((m_toolLoop->getMouseButton() == ToolLoop::Left && pointer.getButton() == Pointer::Right) ||
-      (m_toolLoop->getMouseButton() == ToolLoop::Right && pointer.getButton() == Pointer::Left)) {
+  if ((m_toolLoop->getMouseButton() == ToolLoop::Left && pointer.button() == Pointer::Right) ||
+      (m_toolLoop->getMouseButton() == ToolLoop::Right && pointer.button() == Pointer::Left)) {
     // Cancel the tool-loop (the destination image should be completelly discarded)
     m_toolLoop->cancel();
     return;
diff --git a/src/app/tools/tool_loop_manager.h b/src/app/tools/tool_loop_manager.h
index 8de1823..5678d1b 100644
--- a/src/app/tools/tool_loop_manager.h
+++ b/src/app/tools/tool_loop_manager.h
@@ -9,6 +9,7 @@
 #define APP_TOOLS_TOOL_LOOP_MANAGER_H_INCLUDED
 #pragma once
 
+#include "app/tools/pointer.h"
 #include "app/tools/stroke.h"
 #include "gfx/point.h"
 #include "gfx/region.h"
@@ -18,88 +19,68 @@
 namespace gfx { class Region; }
 
 namespace app {
-  namespace tools {
-
-    class ToolLoop;
-
-    // Class to manage the drawing tool (editor <-> tool interface).
-    //
-    // The flow is this:
-    // 1. The user press a mouse button in a Editor widget
-    // 2. The Editor creates an implementation of ToolLoop and use it
-    //    with the ToolLoopManager constructor
-    // 3. The ToolLoopManager is used to call
-    //    the following methods:
-    //    - ToolLoopManager::prepareLoop
-    //    - ToolLoopManager::pressButton
-    // 4. If the user moves the mouse, the method
-    //    - ToolLoopManager::movement
-    //    is called.
-    // 5. When the user release the mouse:
-    //    - ToolLoopManager::releaseButton
-    //
-    class ToolLoopManager {
-    public:
-
-      // Simple container of mouse events information.
-      class Pointer {
-      public:
-        enum Button { None, Left, Middle, Right };
-
-        Pointer()
-          : m_point(0, 0), m_button(None) { }
-
-        Pointer(const gfx::Point& point, Button button)
-          : m_point(point), m_button(button) { }
-
-        const gfx::Point& point() const { return m_point; }
-        Button getButton() const { return m_button; }
-
-      private:
-        gfx::Point m_point;
-        Button m_button;
-      };
-
-      // Contructs a manager for the ToolLoop delegate.
-      ToolLoopManager(ToolLoop* toolLoop);
-      virtual ~ToolLoopManager();
-
-      bool isCanceled() const;
-
-      // Should be called when the user start a tool-trace (pressing the
-      // left or right button for first time in the editor).
-      void prepareLoop(const Pointer& pointer);
-
-      // Should be called when the ToolLoop::getModifiers()
-      // value was modified (e.g. when the user press/release a key).
-      void notifyToolLoopModifiersChange();
-
-      // Should be called each time the user presses a mouse button.
-      void pressButton(const Pointer& pointer);
-
-      // Should be called each time the user releases a mouse button.
-      //
-      // Returns true if the tool-loop should continue, or false
-      // if the editor should release the mouse capture.
-      bool releaseButton(const Pointer& pointer);
-
-      // Should be called each time the user moves the mouse inside the editor.
-      void movement(const Pointer& pointer);
-
-    private:
-      void doLoopStep(bool last_step);
-      void snapToGrid(gfx::Point& point);
-
-      void calculateDirtyArea(const Strokes& strokes);
-
-      ToolLoop* m_toolLoop;
-      Stroke m_stroke;
-      Pointer m_lastPointer;
-      gfx::Point m_oldPoint;
-      gfx::Region& m_dirtyArea;
-    };
-
-  } // namespace tools
+namespace tools {
+
+class ToolLoop;
+
+// Class to manage the drawing tool (editor <-> tool interface).
+//
+// The flow is this:
+// 1. The user press a mouse button in a Editor widget
+// 2. The Editor creates an implementation of ToolLoop and use it
+//    with the ToolLoopManager constructor
+// 3. The ToolLoopManager is used to call
+//    the following methods:
+//    - ToolLoopManager::prepareLoop
+//    - ToolLoopManager::pressButton
+// 4. If the user moves the mouse, the method
+//    - ToolLoopManager::movement
+//    is called.
+// 5. When the user release the mouse:
+//    - ToolLoopManager::releaseButton
+//
+class ToolLoopManager {
+public:
+  // Contructs a manager for the ToolLoop delegate.
+  ToolLoopManager(ToolLoop* toolLoop);
+  virtual ~ToolLoopManager();
+
+  bool isCanceled() const;
+
+  // Should be called when the user start a tool-trace (pressing the
+  // left or right button for first time in the editor).
+  void prepareLoop(const Pointer& pointer);
+
+  // Should be called when the ToolLoop::getModifiers()
+  // value was modified (e.g. when the user press/release a key).
+  void notifyToolLoopModifiersChange();
+
+  // Should be called each time the user presses a mouse button.
+  void pressButton(const Pointer& pointer);
+
+  // Should be called each time the user releases a mouse button.
+  //
+  // Returns true if the tool-loop should continue, or false
+  // if the editor should release the mouse capture.
+  bool releaseButton(const Pointer& pointer);
+
+  // Should be called each time the user moves the mouse inside the editor.
+  void movement(const Pointer& pointer);
+
+private:
+  void doLoopStep(bool last_step);
+  void snapToGrid(gfx::Point& point);
+
+  void calculateDirtyArea(const Strokes& strokes);
+
+  ToolLoop* m_toolLoop;
+  Stroke m_stroke;
+  Pointer m_lastPointer;
+  gfx::Point m_oldPoint;
+  gfx::Region& m_dirtyArea;
+};
+
+} // namespace tools
 } // namespace app
 
 #endif
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 4e69ce9..21e1272 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -23,6 +23,7 @@
 #include "app/modules/palettes.h"
 #include "app/pref/preferences.h"
 #include "app/shade.h"
+#include "app/tools/active_tool.h"
 #include "app/tools/controller.h"
 #include "app/tools/ink.h"
 #include "app/tools/ink_type.h"
@@ -322,7 +323,7 @@ public:
       pref.tool(tool).ink(inkType);
     }
 
-    m_owner->updateForCurrentTool();
+    m_owner->updateForActiveTool();
   }
 
   void setInkTypeIcon(InkType inkType) {
@@ -1344,9 +1345,9 @@ ContextBar::ContextBar()
   m_freehandAlgo->setupTooltips(tooltipManager);
   m_symmetry->setupTooltips(tooltipManager);
 
+  App::instance()->activeToolManager()->addObserver(this);
+
   auto& pref = Preferences::instance();
-  pref.toolBox.activeTool.AfterChange.connect(
-    base::Bind<void>(&ContextBar::onCurrentToolChange, this));
   pref.symmetryMode.enabled.AfterChange.connect(
     base::Bind<void>(&ContextBar::onSymmetryModeChange, this));
   pref.colorBar.fgColor.AfterChange.connect(
@@ -1359,6 +1360,11 @@ ContextBar::ContextBar()
   setActiveBrush(createBrushFromPreferences());
 }
 
+ContextBar::~ContextBar()
+{
+  App::instance()->activeToolManager()->removeObserver(this);
+}
+
 void ContextBar::onSizeHint(SizeHintEvent& ev)
 {
   ev.setSizeHint(gfx::Size(0, 18*guiscale())); // TODO calculate height
@@ -1386,7 +1392,7 @@ void ContextBar::onBrushSizeChange()
   if (m_activeBrush->type() != kImageBrushType)
     discardActiveBrush();
 
-  updateForCurrentTool();
+  updateForActiveTool();
 }
 
 void ContextBar::onBrushAngleChange()
@@ -1395,18 +1401,18 @@ void ContextBar::onBrushAngleChange()
     discardActiveBrush();
 }
 
-void ContextBar::onCurrentToolChange()
+void ContextBar::onActiveToolChange(tools::Tool* tool)
 {
   if (m_activeBrush->type() != kImageBrushType)
     setActiveBrush(ContextBar::createBrushFromPreferences());
   else {
-    updateForCurrentTool();
+    updateForTool(tool);
   }
 }
 
 void ContextBar::onSymmetryModeChange()
 {
-  updateForCurrentTool();
+  updateForActiveTool();
 }
 
 void ContextBar::onFgOrBgColorChange(doc::Brush::ImageColor imageColor)
@@ -1433,7 +1439,7 @@ void ContextBar::onDropPixels(ContextBarObserver::DropAction action)
   notifyObservers(&ContextBarObserver::onDropPixels, action);
 }
 
-void ContextBar::updateForCurrentTool()
+void ContextBar::updateForActiveTool()
 {
   updateForTool(App::instance()->activeTool());
 }
@@ -1719,7 +1725,7 @@ void ContextBar::setActiveBrush(const doc::BrushRef& brush)
 
   BrushChange();
 
-  updateForCurrentTool();
+  updateForActiveTool();
 }
 
 doc::BrushRef ContextBar::activeBrush(tools::Tool* tool) const
diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h
index add5091..0f3075f 100644
--- a/src/app/ui/context_bar.h
+++ b/src/app/ui/context_bar.h
@@ -11,6 +11,7 @@
 
 #include "app/pref/preferences.h"
 #include "app/shade.h"
+#include "app/tools/active_tool_observer.h"
 #include "app/tools/ink_type.h"
 #include "app/tools/tool_loop_modifiers.h"
 #include "app/ui/context_bar_observer.h"
@@ -39,12 +40,14 @@ namespace app {
 
   class BrushSlot;
 
-  class ContextBar : public ui::Box,
-                     public base::Observable<ContextBarObserver> {
+  class ContextBar : public ui::Box
+                   , public base::Observable<ContextBarObserver>
+                   , public tools::ActiveToolObserver {
   public:
     ContextBar();
+    ~ContextBar();
 
-    void updateForCurrentTool();
+    void updateForActiveTool();
     void updateForTool(tools::Tool* tool);
     void updateForMovingPixels();
     void updateForSelectingBox(const std::string& text);
@@ -77,11 +80,13 @@ namespace app {
   private:
     void onBrushSizeChange();
     void onBrushAngleChange();
-    void onCurrentToolChange();
     void onSymmetryModeChange();
     void onFgOrBgColorChange(doc::Brush::ImageColor imageColor);
     void onDropPixels(ContextBarObserver::DropAction action);
 
+    // ActiveToolObserver impl
+    void onActiveToolChange(tools::Tool* tool) override;
+
     class BrushTypeField;
     class BrushAngleField;
     class BrushSizeField;
diff --git a/src/app/ui/editor/drawing_state.cpp b/src/app/ui/editor/drawing_state.cpp
index 8fee64b..75f26bb 100644
--- a/src/app/ui/editor/drawing_state.cpp
+++ b/src/app/ui/editor/drawing_state.cpp
@@ -21,6 +21,7 @@
 #include "app/tools/tool_loop_manager.h"
 #include "app/ui/editor/editor.h"
 #include "app/ui/editor/editor_customization_delegate.h"
+#include "app/ui/editor/glue.h"
 #include "app/ui/keyboard_shortcuts.h"
 #include "app/ui_context.h"
 #include "ui/message.h"
@@ -37,21 +38,6 @@ namespace app {
 
 using namespace ui;
 
-static tools::ToolLoopManager::Pointer::Button button_from_msg(MouseMessage* msg)
-{
-  return
-    (msg->right() ? tools::ToolLoopManager::Pointer::Right:
-     (msg->middle() ? tools::ToolLoopManager::Pointer::Middle:
-                      tools::ToolLoopManager::Pointer::Left));
-}
-
-static tools::ToolLoopManager::Pointer pointer_from_msg(Editor* editor, MouseMessage* msg)
-{
-  return
-    tools::ToolLoopManager::Pointer(editor->screenToEditor(msg->position()),
-                                    button_from_msg(msg));
-}
-
 DrawingState::DrawingState(tools::ToolLoop* toolLoop)
   : m_toolLoop(toolLoop)
   , m_toolLoopManager(new tools::ToolLoopManager(toolLoop))
@@ -79,7 +65,7 @@ void DrawingState::initToolLoop(Editor* editor, MouseMessage* msg)
 
   m_lastPoint = editor->lastDrawingPosition();
 
-  tools::ToolLoopManager::Pointer pointer;
+  tools::Pointer pointer;
   bool movement = false;
 
   if (m_toolLoop->getController()->isFreehand() &&
@@ -87,7 +73,7 @@ void DrawingState::initToolLoop(Editor* editor, MouseMessage* msg)
       (editor->getCustomizationDelegate()
          ->getPressedKeyAction(KeyContext::FreehandTool) & KeyAction::StraightLineFromLastPoint) == KeyAction::StraightLineFromLastPoint &&
       m_lastPoint.x >= 0) {
-    pointer = tools::ToolLoopManager::Pointer(m_lastPoint, button_from_msg(msg));
+    pointer = tools::Pointer(m_lastPoint, button_from_msg(msg));
     movement = true;
   }
   else {
@@ -170,8 +156,8 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg)
 
   // Infinite scroll
   gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir);
-  tools::ToolLoopManager::Pointer pointer(editor->screenToEditor(mousePos),
-                                          button_from_msg(msg));
+  tools::Pointer pointer(editor->screenToEditor(mousePos),
+                         button_from_msg(msg));
 
   // Notify mouse movement to the tool
   ASSERT(m_toolLoopManager != NULL);
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index 1d66ba5..7de475f 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -25,6 +25,7 @@
 #include "app/modules/palettes.h"
 #include "app/pref/preferences.h"
 #include "app/pref/preferences.h"
+#include "app/tools/active_tool.h"
 #include "app/tools/ink.h"
 #include "app/tools/tool.h"
 #include "app/tools/tool_box.h"
@@ -33,6 +34,7 @@
 #include "app/ui/editor/drawing_state.h"
 #include "app/ui/editor/editor_customization_delegate.h"
 #include "app/ui/editor/editor_decorator.h"
+#include "app/ui/editor/glue.h"
 #include "app/ui/editor/moving_pixels_state.h"
 #include "app/ui/editor/pixels_movement.h"
 #include "app/ui/editor/play_state.h"
@@ -157,14 +159,12 @@ Editor::Editor(Document* document, EditorFlags flags)
   , m_docPref(Preferences::instance().document(document))
   , m_brushPreview(this)
   , m_lastDrawingPosition(-1, -1)
-  , m_quicktool(NULL)
   , m_toolLoopModifiers(tools::ToolLoopModifiers::kNone)
   , m_padding(0, 0)
   , m_antsTimer(100, this)
   , m_antsOffset(0)
   , m_customizationDelegate(NULL)
   , m_docView(NULL)
-  , m_lastPointerType(ui::PointerType::Unknown)
   , m_flags(flags)
   , m_secondaryButton(false)
   , m_aniSpeed(1.0)
@@ -174,9 +174,7 @@ Editor::Editor(Document* document, EditorFlags flags)
 
   this->setFocusStop(true);
 
-  m_currentToolChangeConn =
-    Preferences::instance().toolBox.activeTool.AfterChange.connect(
-      base::Bind<void>(&Editor::onCurrentToolChange, this));
+  App::instance()->activeToolManager()->addObserver(this);
 
   m_fgColorChangeConn =
     Preferences::instance().colorBar.fgColor.AfterChange.connect(
@@ -218,6 +216,7 @@ Editor::~Editor()
 
   m_observers.notifyDestroyEditor(this);
   m_document->removeObserver(this);
+  App::instance()->activeToolManager()->removeObserver(this);
 
   setCustomizationDelegate(NULL);
 
@@ -905,50 +904,9 @@ gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir)
   return mousePos;
 }
 
-bool Editor::isCurrentToolAffectedByRightClickMode()
-{
-  tools::Tool* tool = App::instance()->activeTool();
-  bool shadingMode = (Preferences::instance().tool(tool).ink() == tools::InkType::SHADING);
-  return
-    ((tool->getInk(0)->isPaint() && !shadingMode) ||
-     (tool->getInk(0)->isEffect())) &&
-    (!tool->getInk(0)->isEraser());
-}
-
 tools::Tool* Editor::getCurrentEditorTool()
 {
-  if (m_quicktool)
-    return m_quicktool;
-
-  // Eraser tip
-  if (m_lastPointerType == ui::PointerType::Eraser) {
-    tools::ToolBox* toolbox = App::instance()->toolBox();
-    return toolbox->getToolById(tools::WellKnownTools::Eraser);
-  }
-
-  tools::Tool* tool = App::instance()->activeTool();
-
-  if (m_secondaryButton &&
-      isCurrentToolAffectedByRightClickMode()) {
-    tools::ToolBox* toolbox = App::instance()->toolBox();
-
-    switch (Preferences::instance().editor.rightClickMode()) {
-      case app::gen::RightClickMode::PAINT_BGCOLOR:
-        // Do nothing, use the current tool
-        break;
-      case app::gen::RightClickMode::PICK_FGCOLOR:
-        tool = toolbox->getToolById(tools::WellKnownTools::Eyedropper);
-        break;
-      case app::gen::RightClickMode::ERASE:
-        tool = toolbox->getToolById(tools::WellKnownTools::Eraser);
-        break;
-      case app::gen::RightClickMode::SCROLL:
-        tool = toolbox->getToolById(tools::WellKnownTools::Hand);
-        break;
-    }
-  }
-
-  return tool;
+  return App::instance()->activeTool();
 }
 
 tools::Ink* Editor::getCurrentEditorInk()
@@ -956,72 +914,8 @@ tools::Ink* Editor::getCurrentEditorInk()
   tools::Ink* ink = m_state->getStateInk();
   if (ink)
     return ink;
-
-  tools::Tool* tool = getCurrentEditorTool();
-  ink = tool->getInk(m_secondaryButton ? 1: 0);
-
-  if (m_quicktool)
-    return ink;
-
-  app::gen::RightClickMode rightClickMode = Preferences::instance().editor.rightClickMode();
-
-  if (m_secondaryButton &&
-      rightClickMode != app::gen::RightClickMode::DEFAULT &&
-      isCurrentToolAffectedByRightClickMode()) {
-    tools::ToolBox* toolbox = App::instance()->toolBox();
-
-    switch (rightClickMode) {
-      case app::gen::RightClickMode::DEFAULT:
-        // Do nothing
-        break;
-      case app::gen::RightClickMode::PICK_FGCOLOR:
-        ink = toolbox->getInkById(tools::WellKnownInks::PickFg);
-        break;
-      case app::gen::RightClickMode::ERASE:
-        ink = toolbox->getInkById(tools::WellKnownInks::Eraser);
-        break;
-      case app::gen::RightClickMode::SCROLL:
-        ink = toolbox->getInkById(tools::WellKnownInks::Scroll);
-        break;
-    }
-  }
-  // Only paint tools can have different inks
-  else if (ink->isPaint() && !ink->isEffect()) {
-    tools::InkType inkType = Preferences::instance().tool(tool).ink();
-    const char* id = NULL;
-
-    switch (inkType) {
-
-      case tools::InkType::SIMPLE: {
-        id = tools::WellKnownInks::Paint;
-
-        ColorBar* colorbar = ColorBar::instance();
-        app::Color color = (m_secondaryButton ? colorbar->getBgColor():
-                                                colorbar->getFgColor());
-        if (color.getAlpha() == 0)
-          id = tools::WellKnownInks::PaintCopy;
-        break;
-      }
-
-      case tools::InkType::ALPHA_COMPOSITING:
-        id = tools::WellKnownInks::Paint;
-        break;
-      case tools::InkType::COPY_COLOR:
-        id = tools::WellKnownInks::PaintCopy;
-        break;
-      case tools::InkType::LOCK_ALPHA:
-        id = tools::WellKnownInks::PaintLockAlpha;
-        break;
-      case tools::InkType::SHADING:
-        id = tools::WellKnownInks::Shading;
-        break;
-    }
-
-    if (id)
-      ink = App::instance()->toolBox()->getInkById(id);
-  }
-
-  return ink;
+  else
+    return App::instance()->activeToolManager()->activeInk();
 }
 
 gfx::Point Editor::screenToEditor(const gfx::Point& pt)
@@ -1119,53 +1013,38 @@ void Editor::updateStatusBar()
 void Editor::updateQuicktool()
 {
   if (m_customizationDelegate && !hasCapture()) {
-    tools::Tool* current_tool = App::instance()->activeTool();
+    auto activeToolManager = App::instance()->activeToolManager();
+    tools::Tool* selectedTool = activeToolManager->selectedTool();
+    tools::Tool* oldQuicktool = activeToolManager->quickTool();
 
     // Don't change quicktools if we are in a selection tool and using
     // the selection modifiers.
-    if (current_tool->getInk(0)->isSelection() &&
+    if (selectedTool->getInk(0)->isSelection() &&
         int(m_customizationDelegate->getPressedKeyAction(KeyContext::SelectionTool)) != 0)
       return;
 
-    tools::Tool* old_quicktool = m_quicktool;
-    tools::Tool* new_quicktool = m_customizationDelegate->getQuickTool(current_tool);
+    tools::Tool* newQuicktool =
+      m_customizationDelegate->getQuickTool(selectedTool);
 
     // Check if the current state accept the given quicktool.
-    if (new_quicktool &&
-        !m_state->acceptQuickTool(new_quicktool))
+    if (newQuicktool && !m_state->acceptQuickTool(newQuicktool))
       return;
 
-    // If the quick tool has changed we update the brush cursor, the
-    // context bar, and the status bar. Things that depends on the
-    // current tool.
-    //
-    // TODO We could add a quick tool observer for this
-    if (old_quicktool != new_quicktool) {
-      // Hide the brush preview with the current tool brush size
-      // before we change the quicktool. In this way we avoid using
-      // the quicktool brush size to clean the current tool cursor.
-      //
-      // TODO Create a new Document concept of multiple extra cels: we
-      // need an extra cel for the drawing cursor, other for the moving
-      // pixels, etc. In this way we'll not have conflicts between
-      // different uses of the same extra cel.
-      {
-        HideBrushPreview hide(m_brushPreview);
-        m_quicktool = new_quicktool;
-      }
-
-      m_state->onQuickToolChange(this);
-
-      updateStatusBar();
-
-      App::instance()->contextBar()->updateForTool(getCurrentEditorTool());
-    }
+    activeToolManager
+      ->newQuickToolSelectedFromEditor(newQuicktool);
   }
 }
 
-void Editor::updateContextBar()
+void Editor::updateToolByTipProximity(ui::PointerType pointerType)
 {
-  App::instance()->contextBar()->updateForTool(getCurrentEditorTool());
+  auto activeToolManager = App::instance()->activeToolManager();
+
+  if (pointerType == ui::PointerType::Eraser) {
+    activeToolManager->eraserTipProximity();
+  }
+  else {
+    activeToolManager->regularTipProximity();
+  }
 }
 
 void Editor::updateToolLoopModifiersIndicators()
@@ -1288,17 +1167,19 @@ bool Editor::onProcessMessage(Message* msg)
         MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
 
         m_oldPos = mouseMsg->position();
-        m_lastPointerType = mouseMsg->pointerType();
+        updateToolByTipProximity(mouseMsg->pointerType());
 
         if (!m_secondaryButton && mouseMsg->right()) {
           m_secondaryButton = mouseMsg->right();
 
           updateQuicktool();
-          updateContextBar();
           updateToolLoopModifiersIndicators();
           setCursor(mouseMsg->position());
         }
 
+        App::instance()->activeToolManager()
+          ->pressButton(pointer_from_msg(this, mouseMsg));
+
         EditorStatePtr holdState(m_state);
         return m_state->onMouseDown(this, mouseMsg);
       }
@@ -1307,14 +1188,9 @@ bool Editor::onProcessMessage(Message* msg)
     case kMouseMoveMessage:
       if (m_sprite) {
         EditorStatePtr holdState(m_state);
+        MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
 
-        PointerType newPointerType = static_cast<MouseMessage*>(msg)->pointerType();
-        if (m_lastPointerType != newPointerType) {
-          m_lastPointerType = newPointerType;
-
-          updateQuicktool();
-          updateContextBar();
-        }
+        updateToolByTipProximity(mouseMsg->pointerType());
 
         return m_state->onMouseMove(this, static_cast<MouseMessage*>(msg));
       }
@@ -1326,13 +1202,13 @@ bool Editor::onProcessMessage(Message* msg)
         MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
         bool result = m_state->onMouseUp(this, mouseMsg);
 
-        m_lastPointerType = mouseMsg->pointerType();
+        updateToolByTipProximity(mouseMsg->pointerType());
 
         if (!hasCapture()) {
+          App::instance()->activeToolManager()->releaseButtons();
           m_secondaryButton = false;
 
           updateQuicktool();
-          updateContextBar();
           updateToolLoopModifiersIndicators();
           setCursor(mouseMsg->position());
         }
@@ -1347,7 +1223,7 @@ bool Editor::onProcessMessage(Message* msg)
         MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
         EditorStatePtr holdState(m_state);
 
-        m_lastPointerType = mouseMsg->pointerType();
+        updateToolByTipProximity(mouseMsg->pointerType());
 
         bool used = m_state->onDoubleClick(this, mouseMsg);
         if (used)
@@ -1483,9 +1359,9 @@ void Editor::onInvalidateRegion(const gfx::Region& region)
 }
 
 // When the current tool is changed
-void Editor::onCurrentToolChange()
+void Editor::onActiveToolChange(tools::Tool* tool)
 {
-  m_state->onCurrentToolChange(this);
+  m_state->onActiveToolChange(this, tool);
 }
 
 void Editor::onFgColorChange()
diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h
index c8628ac..d96f743 100644
--- a/src/app/ui/editor/editor.h
+++ b/src/app/ui/editor/editor.h
@@ -13,6 +13,7 @@
 #include "app/color.h"
 #include "app/document.h"
 #include "app/pref/preferences.h"
+#include "app/tools/active_tool_observer.h"
 #include "app/tools/tool_loop_modifiers.h"
 #include "app/ui/color_source.h"
 #include "app/ui/editor/brush_preview.h"
@@ -61,9 +62,10 @@ namespace app {
     ScrollDir,
   };
 
-  class Editor : public ui::Widget,
-                 public doc::DocumentObserver
-               , public IColorSource {
+  class Editor : public ui::Widget
+               , public doc::DocumentObserver
+               , public IColorSource
+               , public tools::ActiveToolObserver {
   public:
     enum EditorFlags {
       kNoneFlag = 0,
@@ -232,18 +234,19 @@ namespace app {
     void onResize(ui::ResizeEvent& ev) override;
     void onPaint(ui::PaintEvent& ev) override;
     void onInvalidateRegion(const gfx::Region& region) override;
-    void onCurrentToolChange();
     void onFgColorChange();
     void onContextBarBrushChange();
     void onShowExtrasChange();
     void onExposeSpritePixels(doc::DocumentEvent& ev) override;
 
+    // ActiveToolObserver impl
+    void onActiveToolChange(tools::Tool* tool) override;
+
   private:
     void setStateInternal(const EditorStatePtr& newState);
     void updateQuicktool();
-    void updateContextBar();
+    void updateToolByTipProximity(ui::PointerType pointerType);
     void updateToolLoopModifiersIndicators();
-    bool isCurrentToolAffectedByRightClickMode();
 
     void drawMaskSafe();
     void drawMask(ui::Graphics* g);
@@ -285,10 +288,6 @@ namespace app {
     // (EditorCustomizationDelegate::isStraightLineFromLastPoint() modifier)
     gfx::Point m_lastDrawingPosition;
 
-    // Current selected quicktool (this genererally should be NULL if
-    // the user is not pressing any keyboard key).
-    tools::Tool* m_quicktool;
-
     tools::ToolLoopModifiers m_toolLoopModifiers;
     bool m_autoSelectLayer;
 
@@ -299,11 +298,6 @@ namespace app {
     ui::Timer m_antsTimer;
     int m_antsOffset;
 
-    // This slot is used to disconnect the Editor from CurrentToolChange
-    // signal (because the editor can be destroyed and the application
-    // still continue running and generating CurrentToolChange
-    // signals).
-    base::ScopedConnection m_currentToolChangeConn;
     base::ScopedConnection m_fgColorChangeConn;
     base::ScopedConnection m_contextBarBrushChangeConn;
     base::ScopedConnection m_showExtrasConn;
diff --git a/src/app/ui/editor/editor_state.h b/src/app/ui/editor/editor_state.h
index a08b3e5..aa9033c 100644
--- a/src/app/ui/editor/editor_state.h
+++ b/src/app/ui/editor/editor_state.h
@@ -71,8 +71,7 @@ namespace app {
     // useful for states which depends on the selected current tool (as
     // MovingPixelsState which drops the pixels in case the user selects
     // other drawing tool).
-    virtual void onCurrentToolChange(Editor* editor) { }
-    virtual void onQuickToolChange(Editor* editor) { }
+    virtual void onActiveToolChange(Editor* editor, tools::Tool* tool) { }
 
     // Called when the user presses a mouse button over the editor.
     virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) { return false; }
diff --git a/src/app/ui/editor/glue.h b/src/app/ui/editor/glue.h
new file mode 100644
index 0000000..766795d
--- /dev/null
+++ b/src/app/ui/editor/glue.h
@@ -0,0 +1,35 @@
+// Aseprite
+// Copyright (C) 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.
+
+#ifndef APP_UI_EDITOR_GLUE_H_INCLUDED
+#define APP_UI_EDITOR_GLUE_H_INCLUDED
+#pragma once
+
+#include "app/tools/pointer.h"
+#include "app/ui/editor/editor.h"
+#include "ui/message.h"
+
+namespace app {
+
+// Code to convert "ui" messages to "tools" mouse pointers
+
+inline tools::Pointer::Button button_from_msg(ui::MouseMessage* msg) {
+  return
+    (msg->right() ? tools::Pointer::Right:
+     (msg->middle() ? tools::Pointer::Middle:
+                      tools::Pointer::Left));
+}
+
+inline tools::Pointer pointer_from_msg(Editor* editor, ui::MouseMessage* msg) {
+  return
+    tools::Pointer(editor->screenToEditor(msg->position()),
+                   button_from_msg(msg));
+}
+
+} // namespace app
+
+#endif
diff --git a/src/app/ui/editor/moving_pixels_state.cpp b/src/app/ui/editor/moving_pixels_state.cpp
index b178e5f..d855072 100644
--- a/src/app/ui/editor/moving_pixels_state.cpp
+++ b/src/app/ui/editor/moving_pixels_state.cpp
@@ -110,7 +110,7 @@ MovingPixelsState::~MovingPixelsState()
 {
   ContextBar* contextBar = App::instance()->contextBar();
   contextBar->removeObserver(this);
-  contextBar->updateForCurrentTool();
+  contextBar->updateForActiveTool();
 
   m_pixelsMovement.reset(NULL);
 
@@ -192,18 +192,16 @@ EditorState::LeaveAction MovingPixelsState::onLeaveState(Editor* editor, EditorS
   }
 }
 
-void MovingPixelsState::onCurrentToolChange(Editor* editor)
+void MovingPixelsState::onActiveToolChange(Editor* editor, tools::Tool* tool)
 {
   ASSERT(m_pixelsMovement);
   ASSERT(editor == m_editor);
 
-  tools::Tool* current_tool = editor->getCurrentEditorTool();
-
   // If the user changed the tool when he/she is moving pixels,
   // we have to drop the pixels only if the new tool is not selection...
   if (m_pixelsMovement &&
-      (!current_tool->getInk(0)->isSelection() ||
-       !current_tool->getInk(1)->isSelection())) {
+      (!tool->getInk(0)->isSelection() ||
+       !tool->getInk(1)->isSelection())) {
     // We have to drop pixels
     dropPixels();
   }
diff --git a/src/app/ui/editor/moving_pixels_state.h b/src/app/ui/editor/moving_pixels_state.h
index 39b47c8..4070304 100644
--- a/src/app/ui/editor/moving_pixels_state.h
+++ b/src/app/ui/editor/moving_pixels_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
@@ -39,7 +39,7 @@ namespace app {
     // EditorState
     virtual void onEnterState(Editor* editor) override;
     virtual LeaveAction onLeaveState(Editor* editor, EditorState* newState) override;
-    virtual void onCurrentToolChange(Editor* editor) override;
+    virtual void onActiveToolChange(Editor* editor, tools::Tool* tool) override;
     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;
diff --git a/src/app/ui/editor/select_box_state.cpp b/src/app/ui/editor/select_box_state.cpp
index d7793c8..dc9a8d7 100644
--- a/src/app/ui/editor/select_box_state.cpp
+++ b/src/app/ui/editor/select_box_state.cpp
@@ -41,7 +41,7 @@ SelectBoxState::SelectBoxState(SelectBoxDelegate* delegate, const gfx::Rect& rc,
 SelectBoxState::~SelectBoxState()
 {
   ContextBar* contextBar = App::instance()->contextBar();
-  contextBar->updateForCurrentTool();
+  contextBar->updateForActiveTool();
 }
 
 void SelectBoxState::setFlags(Flags flags)
diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp
index a38be6d..cbaf5a8 100644
--- a/src/app/ui/editor/standby_state.cpp
+++ b/src/app/ui/editor/standby_state.cpp
@@ -112,10 +112,8 @@ void StandbyState::onEnterState(Editor* editor)
       base::Bind<void>(&StandbyState::onPivotChange, this, editor));
 }
 
-void StandbyState::onCurrentToolChange(Editor* editor)
+void StandbyState::onActiveToolChange(Editor* editor, tools::Tool* tool)
 {
-  //tools::Tool* currentTool = editor->getCurrentEditorTool();
-
   // If the user change from a selection tool to a non-selection tool,
   // or viceversa, we've to show or hide the transformation handles.
   // TODO Compare the ink (isSelection()) of the previous tool with
@@ -123,11 +121,6 @@ void StandbyState::onCurrentToolChange(Editor* editor)
   editor->invalidate();
 }
 
-void StandbyState::onQuickToolChange(Editor* editor)
-{
-  editor->invalidate();
-}
-
 bool StandbyState::checkForScroll(Editor* editor, MouseMessage* msg)
 {
   tools::Ink* clickedInk = editor->getCurrentEditorInk();
diff --git a/src/app/ui/editor/standby_state.h b/src/app/ui/editor/standby_state.h
index c5954c7..d3da603 100644
--- a/src/app/ui/editor/standby_state.h
+++ b/src/app/ui/editor/standby_state.h
@@ -27,8 +27,7 @@ namespace app {
     StandbyState();
     virtual ~StandbyState();
     virtual void onEnterState(Editor* editor) override;
-    virtual void onCurrentToolChange(Editor* editor) override;
-    virtual void onQuickToolChange(Editor* editor) override;
+    virtual void onActiveToolChange(Editor* editor, tools::Tool* tool) override;
     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;
diff --git a/src/app/ui/main_window.cpp b/src/app/ui/main_window.cpp
index 919ecc8..818ea79 100644
--- a/src/app/ui/main_window.cpp
+++ b/src/app/ui/main_window.cpp
@@ -384,7 +384,7 @@ void MainWindow::configureWorkspaceLayout()
     Preferences::instance().general.visibleTimeline());
 
   if (m_contextBar->isVisible()) {
-    m_contextBar->updateForCurrentTool();
+    m_contextBar->updateForActiveTool();
   }
 
   layout();
diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp
index 4a4c476..5131ad1 100644
--- a/src/app/ui/status_bar.cpp
+++ b/src/app/ui/status_bar.cpp
@@ -20,6 +20,7 @@
 #include "app/modules/gui.h"
 #include "app/modules/palettes.h"
 #include "app/pref/preferences.h"
+#include "app/tools/active_tool.h"
 #include "app/tools/tool.h"
 #include "app/ui/button_set.h"
 #include "app/ui/color_button.h"
@@ -529,15 +530,14 @@ StatusBar::StatusBar()
   tooltipManager->addTooltipFor(m_currentFrame, "Current Frame", BOTTOM);
   tooltipManager->addTooltipFor(m_zoomEntry, "Zoom Level", BOTTOM);
 
-  Preferences::instance().toolBox.activeTool.AfterChange.connect(
-    base::Bind<void>(&StatusBar::onCurrentToolChange, this));
-
   UIContext::instance()->addObserver(this);
   UIContext::instance()->documents().addObserver(this);
+  App::instance()->activeToolManager()->addObserver(this);
 }
 
 StatusBar::~StatusBar()
 {
+  App::instance()->activeToolManager()->removeObserver(this);
   UIContext::instance()->documents().removeObserver(this);
   UIContext::instance()->removeObserver(this);
 
@@ -545,14 +545,11 @@ StatusBar::~StatusBar()
   delete m_snapToGridWindow;
 }
 
-void StatusBar::onCurrentToolChange()
+void StatusBar::onSelectedToolChange(tools::Tool* tool)
 {
-  if (isVisible()) {
-    tools::Tool* tool = App::instance()->activeTool();
-    if (tool) {
-      showTool(500, tool);
-      setTextf("%s Selected", tool->getText().c_str());
-    }
+  if (isVisible() && tool) {
+    showTool(500, tool);
+    setTextf("%s Selected", tool->getText().c_str());
   }
 }
 
diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h
index 06a3bdd..1156a8f 100644
--- a/src/app/ui/status_bar.h
+++ b/src/app/ui/status_bar.h
@@ -10,6 +10,7 @@
 #pragma once
 
 #include "app/color.h"
+#include "app/tools/active_tool_observer.h"
 #include "base/observers.h"
 #include "base/time.h"
 #include "doc/context_observer.h"
@@ -46,7 +47,8 @@ namespace app {
   class StatusBar : public ui::HBox
                   , public doc::ContextObserver
                   , public doc::DocumentsObserver
-                  , public doc::DocumentObserver {
+                  , public doc::DocumentObserver
+                  , public tools::ActiveToolObserver {
     static StatusBar* m_instance;
   public:
     static StatusBar* instance() { return m_instance; }
@@ -77,8 +79,10 @@ namespace app {
     // DocumentObserver impl
     void onPixelFormatChanged(DocumentEvent& ev) override;
 
+    // ActiveToolObserver impl
+    void onSelectedToolChange(tools::Tool* tool) override;
+
   private:
-    void onCurrentToolChange();
     void onCelOpacitySliderChange();
     void newFrame();
     void onChangeZoom(const render::Zoom& zoom);
diff --git a/src/app/ui/toolbar.cpp b/src/app/ui/toolbar.cpp
index 1ee298a..42cb7aa 100644
--- a/src/app/ui/toolbar.cpp
+++ b/src/app/ui/toolbar.cpp
@@ -16,7 +16,7 @@
 #include "app/commands/commands.h"
 #include "app/modules/editors.h"
 #include "app/modules/gfx.h"
-#include "app/pref/preferences.h"
+#include "app/tools/active_tool.h"
 #include "app/tools/tool_box.h"
 #include "app/ui/keyboard_shortcuts.h"
 #include "app/ui/main_window.h"
@@ -101,10 +101,14 @@ ToolBar::ToolBar()
     if (m_selectedInGroup.find(tool->getGroup()) == m_selectedInGroup.end())
       m_selectedInGroup[tool->getGroup()] = tool;
   }
+
+  App::instance()->activeToolManager()->addObserver(this);
 }
 
 ToolBar::~ToolBar()
 {
+  App::instance()->activeToolManager()->removeObserver(this);
+
   delete m_popupWindow;
   delete m_tipWindow;
 }
@@ -293,6 +297,7 @@ void ToolBar::onPaint(ui::PaintEvent& ev)
   gfx::Color normalFace = theme->colors.buttonNormalFace();
   gfx::Color hotFace = theme->colors.buttonHotFace();
   ToolBox* toolbox = App::instance()->toolBox();
+  Tool* activeTool = App::instance()->activeTool();
   ToolGroupList::iterator it = toolbox->begin_group();
   int groups = toolbox->getGroupsCount();
   Rect toolrc;
@@ -305,7 +310,7 @@ void ToolBar::onPaint(ui::PaintEvent& ev)
     gfx::Color face;
     SkinPartPtr nw;
 
-    if (App::instance()->activeTool() == tool || m_hotIndex == c) {
+    if (activeTool == tool || m_hotIndex == c) {
       nw = theme->parts.toolbuttonHot();
       face = hotFace;
     }
@@ -541,8 +546,8 @@ void ToolBar::selectTool(Tool* tool)
 
   m_selectedInGroup[tool->getGroup()] = tool;
 
-  // Set active tool in preferences.
-  Preferences::instance().toolBox.activeTool(tool->getId());
+  // Inform to the active tool manager about this tool change.
+  App::instance()->activeToolManager()->setSelectedTool(tool);
 
   if (m_currentStrip)
     m_currentStrip->invalidate();
@@ -679,6 +684,7 @@ void ToolBar::ToolStrip::onPaint(PaintEvent& ev)
   Graphics* g = ev.graphics();
   SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
   ToolBox* toolbox = App::instance()->toolBox();
+  Tool* activeTool = App::instance()->activeTool();
   Rect toolrc;
   int index = 0;
 
@@ -688,8 +694,7 @@ void ToolBar::ToolStrip::onPaint(PaintEvent& ev)
       gfx::Color face;
       SkinPartPtr nw;
 
-      if (App::instance()->activeTool() == tool ||
-          m_hotTool == tool) {
+      if (activeTool == tool || m_hotTool == tool) {
         nw = theme->parts.toolbuttonHot();
         face = theme->colors.buttonHotFace();
       }
@@ -723,4 +728,12 @@ Rect ToolBar::ToolStrip::getToolBounds(int index)
               iconsize.w, bounds.h);
 }
 
+void ToolBar::onSelectedToolChange(tools::Tool* tool)
+{
+  if (tool && m_selectedInGroup[tool->getGroup()] != tool)
+    m_selectedInGroup[tool->getGroup()] = tool;
+
+  invalidate();
+}
+
 } // namespace app
diff --git a/src/app/ui/toolbar.h b/src/app/ui/toolbar.h
index 560b071..ac56fda 100644
--- a/src/app/ui/toolbar.h
+++ b/src/app/ui/toolbar.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
@@ -9,6 +9,7 @@
 #define APP_UI_TOOLBAR_H_INCLUDED
 #pragma once
 
+#include "app/tools/active_tool_observer.h"
 #include "base/connection.h"
 #include "gfx/point.h"
 #include "ui/timer.h"
@@ -29,7 +30,8 @@ namespace app {
   }
 
   // Class to show selected tools for each tool (vertically)
-  class ToolBar : public ui::Widget {
+  class ToolBar : public ui::Widget
+                , public tools::ActiveToolObserver {
     static ToolBar* m_instance;
   public:
     static ToolBar* instance() { return m_instance; }
@@ -59,6 +61,9 @@ namespace app {
     void openTipWindow(int group_index, tools::Tool* tool);
     void onClosePopup();
 
+    // ActiveToolObserver impl
+    void onSelectedToolChange(tools::Tool* tool) override;
+
     // What tool is selected for each tool-group
     std::map<const tools::ToolGroup*, tools::Tool*> m_selectedInGroup;
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git



More information about the Pkg-games-commits mailing list