[aseprite] 112/308: Improve BrushPopup to save custom brushes (type/size/angle params)

Tobias Hansen thansen at moszumanska.debian.org
Tue Mar 8 02:44:58 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 2d64a1926e6119d6c38a7d12fdc2fb6bed82b640
Author: David Capello <davidcapello at gmail.com>
Date:   Tue Dec 15 17:12:11 2015 -0300

    Improve BrushPopup to save custom brushes (type/size/angle params)
    
    We moved custom brushes stuff from ContextBar to app::AppBrushes class.
    And now we can access the list of brushes from app::App::brushes() member.
---
 src/app/CMakeLists.txt                      |   1 +
 src/app/app.h                               |   3 +
 src/app/app_brushes.cpp                     | 117 +++++++++++++
 src/app/app_brushes.h                       |  78 +++++++++
 src/app/commands/cmd_keyboard_shortcuts.cpp |  24 ++-
 src/app/commands/cmd_new_brush.cpp          |   3 +-
 src/app/ui/brush_popup.cpp                  | 256 ++++++++++++++++++----------
 src/app/ui/brush_popup.h                    |  27 ++-
 src/app/ui/button_set.cpp                   |  30 +++-
 src/app/ui/button_set.h                     |   5 +-
 src/app/ui/context_bar.cpp                  | 146 ++++++----------
 src/app/ui/context_bar.h                    |  30 +---
 12 files changed, 480 insertions(+), 240 deletions(-)

diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 490c21c..275655b 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -90,6 +90,7 @@ endif()
 
 add_library(app-lib
   app.cpp
+  app_brushes.cpp
   app_menus.cpp
   app_options.cpp
   app_render.cpp
diff --git a/src/app/app.h b/src/app/app.h
index 3c1e7bc..ba1ae1c 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -9,6 +9,7 @@
 #define APP_APP_H_INCLUDED
 #pragma once
 
+#include "app/app_brushes.h"
 #include "base/signal.h"
 #include "base/string.h"
 #include "base/unique_ptr.h"
@@ -69,6 +70,7 @@ namespace app {
     RecentFiles* getRecentFiles() const;
     MainWindow* getMainWindow() const { return m_mainWindow; }
     Preferences& preferences() const;
+    AppBrushes& brushes() { return m_brushes; }
 
     void showNotification(INotificationDelegate* del);
     void updateDisplayTitleBar();
@@ -95,6 +97,7 @@ namespace app {
     base::UniquePtr<MainWindow> m_mainWindow;
     FileList m_files;
     base::UniquePtr<DocumentExporter> m_exporter;
+    AppBrushes m_brushes;
   };
 
   void app_refresh_screen();
diff --git a/src/app/app_brushes.cpp b/src/app/app_brushes.cpp
new file mode 100644
index 0000000..a3a2add
--- /dev/null
+++ b/src/app/app_brushes.cpp
@@ -0,0 +1,117 @@
+// Aseprite
+// Copyright (C) 2001-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/app_brushes.h"
+
+namespace app {
+
+using namespace doc;
+
+AppBrushes::AppBrushes()
+{
+  m_standard.push_back(BrushRef(new Brush(kCircleBrushType, 7, 0)));
+  m_standard.push_back(BrushRef(new Brush(kSquareBrushType, 7, 0)));
+  m_standard.push_back(BrushRef(new Brush(kLineBrushType, 7, 44)));
+}
+
+AppBrushes::slot_id AppBrushes::addCustomBrush(const BrushRef& brush)
+{
+  // Use an empty slot
+  for (size_t i=0; i<m_slots.size(); ++i) {
+    if (!m_slots[i].locked() ||
+        !m_slots[i].brush()) {
+      m_slots[i].setBrush(brush);
+      return i+1;
+    }
+  }
+
+  m_slots.push_back(BrushSlot(brush));
+  ItemsChange();
+  return (int)m_slots.size(); // Returns the slot
+}
+
+void AppBrushes::removeCustomBrush(slot_id slot)
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size()) {
+    m_slots[slot].setBrush(BrushRef(nullptr));
+
+    // Erase empty trailing slots
+    while (!m_slots.empty() &&
+           !m_slots[m_slots.size()-1].brush())
+      m_slots.erase(--m_slots.end());
+
+    ItemsChange();
+  }
+}
+
+void AppBrushes::removeAllCustomBrushes()
+{
+  while (!m_slots.empty())
+    m_slots.erase(--m_slots.end());
+
+  ItemsChange();
+}
+
+bool AppBrushes::hasCustomBrush(slot_id slot) const
+{
+  --slot;
+  return (slot >= 0 && slot < (int)m_slots.size() &&
+          m_slots[slot].brush());
+}
+
+BrushRef AppBrushes::getCustomBrush(slot_id slot) const
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size())
+    return m_slots[slot].brush();
+  else
+    return BrushRef();
+}
+
+Brushes AppBrushes::getCustomBrushes()
+{
+  Brushes brushes;
+  for (const auto& slot : m_slots)
+    brushes.push_back(slot.brush());
+  return brushes;
+}
+
+void AppBrushes::lockCustomBrush(slot_id slot)
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size() &&
+      m_slots[slot].brush()) {
+    m_slots[slot].setLocked(true);
+  }
+}
+
+void AppBrushes::unlockCustomBrush(slot_id slot)
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size() &&
+      m_slots[slot].brush()) {
+    m_slots[slot].setLocked(false);
+  }
+}
+
+bool AppBrushes::isCustomBrushLocked(slot_id slot) const
+{
+  --slot;
+  if (slot >= 0 && slot < (int)m_slots.size() &&
+      m_slots[slot].brush()) {
+    return m_slots[slot].locked();
+  }
+  else
+    return false;
+}
+
+} // namespace app
diff --git a/src/app/app_brushes.h b/src/app/app_brushes.h
new file mode 100644
index 0000000..12662ed
--- /dev/null
+++ b/src/app/app_brushes.h
@@ -0,0 +1,78 @@
+// Aseprite
+// Copyright (C) 2001-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_APP_BRUSHES_H_INCLUDED
+#define APP_APP_BRUSHES_H_INCLUDED
+#pragma once
+
+#include "base/signal.h"
+#include "doc/brush.h"
+#include "doc/brushes.h"
+
+#include <vector>
+
+namespace app {
+
+  class AppBrushes {
+  public:
+    // Number of slot (a range from 1 to AppBrushes::size() inclusive)
+    typedef int slot_id;
+
+    AppBrushes();
+
+    // Adds a new brush and returns the slot number where the brush
+    // is now available.
+    slot_id addCustomBrush(const doc::BrushRef& brush);
+    void removeCustomBrush(slot_id slot);
+    void removeAllCustomBrushes();
+    bool hasCustomBrush(slot_id slot) const;
+    const doc::Brushes& getStandardBrushes() { return m_standard; }
+    doc::BrushRef getCustomBrush(slot_id slot) const;
+    doc::Brushes getCustomBrushes();
+
+    void lockCustomBrush(slot_id slot);
+    void unlockCustomBrush(slot_id slot);
+    bool isCustomBrushLocked(slot_id slot) const;
+
+    base::Signal0<void> ItemsChange;
+
+  private:
+    // Custom brush slot
+    class BrushSlot {
+    public:
+      BrushSlot(const doc::BrushRef& brush)
+        : m_locked(false)
+        , m_brush(brush) {
+      }
+
+      // True if this is a standard brush.
+      bool standard() const { return m_standard; }
+
+      // True if the user locked the brush using the shortcut key to
+      // access it.
+      bool locked() const { return m_locked; }
+      void setLocked(bool locked) { m_locked = locked; }
+
+      // Can be null if the user deletes the brush.
+      doc::BrushRef brush() const { return m_brush; }
+      void setBrush(const doc::BrushRef& brush) { m_brush = brush; }
+
+    private:
+      bool m_standard;
+      bool m_locked;
+      doc::BrushRef m_brush;
+    };
+
+    typedef std::vector<BrushSlot> BrushSlots;
+
+    doc::Brushes m_standard;
+    BrushSlots m_slots;
+  };
+
+} // namespace app
+
+#endif
diff --git a/src/app/commands/cmd_keyboard_shortcuts.cpp b/src/app/commands/cmd_keyboard_shortcuts.cpp
index e2ffa79..acd25cd 100644
--- a/src/app/commands/cmd_keyboard_shortcuts.cpp
+++ b/src/app/commands/cmd_keyboard_shortcuts.cpp
@@ -287,7 +287,8 @@ private:
 
 class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts {
 public:
-  KeyboardShortcutsWindow() : m_searchChange(false) {
+  KeyboardShortcutsWindow(const std::string& searchText)
+    : m_searchChange(false) {
     setAutoRemap(false);
 
     section()->addChild(new ListItem("Menus"));
@@ -302,6 +303,11 @@ public:
     resetButton()->Click.connect(base::Bind<void>(&KeyboardShortcutsWindow::onReset, this));
 
     fillAllLists();
+
+    if (!searchText.empty()) {
+      search()->setText(searchText);
+      onSearchChange();
+    }
   }
 
   void restoreKeys() {
@@ -523,7 +529,11 @@ public:
   Command* clone() const override { return new KeyboardShortcutsCommand(*this); }
 
 protected:
+  void onLoadParams(const Params& params) override;
   void onExecute(Context* context) override;
+
+private:
+  std::string m_search;
 };
 
 KeyboardShortcutsCommand::KeyboardShortcutsCommand()
@@ -533,9 +543,19 @@ KeyboardShortcutsCommand::KeyboardShortcutsCommand()
 {
 }
 
+void KeyboardShortcutsCommand::onLoadParams(const Params& params)
+{
+  m_search = params.get("search");
+}
+
 void KeyboardShortcutsCommand::onExecute(Context* context)
 {
-  KeyboardShortcutsWindow window;
+  // Here we copy the m_search field because
+  // KeyboardShortcutsWindow::fillAllLists() modifies this same
+  // KeyboardShortcutsCommand instance (so m_search will be "")
+  // TODO Seeing this, we need a complete new way to handle UI commands execution
+  std::string neededSearchCopy = m_search;
+  KeyboardShortcutsWindow window(neededSearchCopy);
 
   window.setBounds(gfx::Rect(0, 0, ui::display_w()*3/4, ui::display_h()*3/4));
   g_sep = window.bounds().w / 2;
diff --git a/src/app/commands/cmd_new_brush.cpp b/src/app/commands/cmd_new_brush.cpp
index aa7b71a..d082d0a 100644
--- a/src/app/commands/cmd_new_brush.cpp
+++ b/src/app/commands/cmd_new_brush.cpp
@@ -149,9 +149,8 @@ void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
   brush->setImage(image.get());
   brush->setPatternOrigin(mask->bounds().origin());
 
-  // TODO add a active stock property in app::Context
   ContextBar* ctxBar = App::instance()->getMainWindow()->getContextBar();
-  int slot = ctxBar->addBrush(brush);
+  auto slot = App::instance()->brushes().addCustomBrush(brush);
   ctxBar->setActiveBrush(brush);
 
   // Get the shortcut for this brush and show it to the user
diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp
index 238f060..42d7e3c 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -11,12 +11,18 @@
 
 #include "app/ui/brush_popup.h"
 
+#include "app/app.h"
+#include "app/app_brushes.h"
+#include "app/commands/command.h"
 #include "app/commands/commands.h"
+#include "app/modules/gui.h"
 #include "app/modules/palettes.h"
 #include "app/ui/app_menuitem.h"
 #include "app/ui/button_set.h"
+#include "app/ui/context_bar.h"
 #include "app/ui/keyboard_shortcuts.h"
 #include "app/ui/skin/skin_theme.h"
+#include "app/ui_context.h"
 #include "base/bind.h"
 #include "base/convert_to.h"
 #include "doc/brush.h"
@@ -28,10 +34,12 @@
 #include "she/scoped_surface_lock.h"
 #include "she/surface.h"
 #include "she/system.h"
+#include "ui/button.h"
+#include "ui/link_label.h"
+#include "ui/listitem.h"
 #include "ui/menu.h"
 #include "ui/message.h"
 #include "ui/separator.h"
-#include "ui/tooltips.h"
 
 namespace app {
 
@@ -41,11 +49,10 @@ using namespace ui;
 
 namespace {
 
-class Item : public ButtonSet::Item {
+class SelectBrushItem : public ButtonSet::Item {
 public:
-  Item(BrushPopup* popup, BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1)
-    : m_popup(popup)
-    , m_delegate(delegate)
+  SelectBrushItem(BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1)
+    : m_delegate(delegate)
     , m_brush(brush)
     , m_slot(slot) {
     SkinPartPtr icon(new SkinPart);
@@ -57,35 +64,84 @@ public:
     return m_brush;
   }
 
-protected:
-  bool onProcessMessage(Message* msg) override {
-    if (msg->type() == kMouseUpMessage && m_slot > 0) {
-      MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
-      if (mouseMsg->buttons() == kButtonRight) {
-        Menu menu;
-        AppMenuItem lockItem(m_delegate->onIsBrushSlotLocked(m_slot) ? "Unlock Brush": "Lock Brush");
-        AppMenuItem deleteItem("Delete");
-        AppMenuItem deleteAllItem("Delete All");
-        lockItem.Click.connect(&Item::onLockBrush, this);
-        deleteItem.Click.connect(&Item::onDeleteBrush, this);
-        deleteAllItem.Click.connect(&Item::onDeleteAllBrushes, this);
-        menu.addChild(&lockItem);
-        menu.addChild(&deleteItem);
-        menu.addChild(new MenuSeparator);
-        menu.addChild(&deleteAllItem);
-
-        // Here we make the popup window temporaly floating, so it's
-        // not closed by the popup menu.
-        m_popup->makeFloating();
-        menu.showPopup(mouseMsg->position());
-        m_popup->makeFixed();
-        m_popup->closeWindow(nullptr);
-      }
+private:
+
+  void onClick() override {
+    if (m_slot >= 0)
+      m_delegate->onSelectBrushSlot(m_slot);
+    else
+      m_delegate->onSelectBrush(m_brush);
+  }
+
+private:
+
+  BrushPopupDelegate* m_delegate;
+  BrushRef m_brush;
+  int m_slot;
+};
+
+class BrushShortcutItem : public ButtonSet::Item {
+public:
+  BrushShortcutItem(const std::string& text, int slot)
+    : m_slot(slot) {
+    setText(text);
+  }
+
+private:
+  void onClick() override {
+    Params params;
+    params.set("change", "custom");
+    params.set("slot", base::convert_to<std::string>(m_slot).c_str());
+    Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::ChangeBrush);
+    cmd->loadParams(params);
+    std::string search = cmd->friendlyName();
+    if (!search.empty()) {
+      params.clear();
+      params.set("search", search.c_str());
+      cmd = CommandsModule::instance()->getCommandByName(CommandId::KeyboardShortcuts);
+      ASSERT(cmd);
+      if (cmd)
+        UIContext::instance()->executeCommand(cmd, params);
     }
-    return ButtonSet::Item::onProcessMessage(msg);
+  }
+
+  int m_slot;
+};
+
+class BrushOptionsItem : public ButtonSet::Item {
+public:
+  BrushOptionsItem(BrushPopup* popup, BrushPopupDelegate* delegate, int slot)
+    : m_popup(popup)
+    , m_delegate(delegate)
+    , m_slot(slot) {
+    setIcon(SkinTheme::instance()->parts.iconArrowDown(), true);
   }
 
 private:
+
+  void onClick() override {
+    Menu menu;
+    AppMenuItem lockItem(m_delegate->onIsBrushSlotLocked(m_slot) ? "Unlock Brush": "Lock Brush");
+    AppMenuItem deleteItem("Delete");
+    AppMenuItem deleteAllItem("Delete All");
+    lockItem.Click.connect(&BrushOptionsItem::onLockBrush, this);
+    deleteItem.Click.connect(&BrushOptionsItem::onDeleteBrush, this);
+    deleteAllItem.Click.connect(&BrushOptionsItem::onDeleteAllBrushes, this);
+    menu.addChild(&lockItem);
+    menu.addChild(&deleteItem);
+    menu.addChild(new MenuSeparator);
+    menu.addChild(&deleteAllItem);
+
+    // Here we make the popup window temporaly floating, so it's
+    // not closed by the popup menu.
+    m_popup->makeFloating();
+    menu.showPopup(gfx::Point(origin().x, origin().y+bounds().h));
+    m_popup->makeFixed();
+    m_popup->closeWindow(nullptr);
+  }
+
+private:
+
   void onLockBrush() {
     if (m_delegate->onIsBrushSlotLocked(m_slot))
       m_delegate->onUnlockBrushSlot(m_slot);
@@ -107,98 +163,122 @@ private:
   int m_slot;
 };
 
-} // anonymous namespace
+class NewCustomBrushItem : public ButtonSet::Item {
+public:
+  NewCustomBrushItem() {
+    setText("Save Brush");
+  }
 
-static BrushRef defBrushes[3];
+private:
+  void onClick() override {
+    AppBrushes& brushes = App::instance()->brushes();
+    auto slot = brushes.addCustomBrush(
+      ContextBar::createBrushFromPreferences());
+    brushes.lockCustomBrush(slot);
+  }
+};
+
+} // anonymous namespace
 
 BrushPopup::BrushPopup(BrushPopupDelegate* delegate)
   : PopupWindow("", ClickBehavior::CloseOnClickInOtherWindow)
+  , m_tooltipManager(nullptr)
+  , m_standardBrushes(3)
+  , m_customBrushes(nullptr)
   , m_delegate(delegate)
 {
   setAutoRemap(false);
-  setBorder(gfx::Border(0));
+  setBorder(gfx::Border(2)*guiscale());
   setChildSpacing(0);
+  m_box.noBorderNoChildSpacing();
+
+  addChild(&m_box);
+
+  HBox* top = new HBox;
+  top->addChild(&m_standardBrushes);
+  top->addChild(new BoxFiller);
+
+  m_box.addChild(top);
+  m_box.addChild(new Separator("", HORIZONTAL));
+
+  const doc::Brushes& brushes = App::instance()->brushes().getStandardBrushes();
+  for (const auto& brush : brushes)
+    m_standardBrushes.addItem(new SelectBrushItem(m_delegate, brush));
+
+  m_standardBrushes.setTransparent(true);
+  m_standardBrushes.setBgColor(gfx::ColorNone);
+
+  App::instance()->brushes()
+    .ItemsChange.connect(&BrushPopup::onBrushChanges, this);
 }
 
 void BrushPopup::setBrush(Brush* brush)
 {
-  for (auto child : m_buttons->children()) {
-    Item* item = static_cast<Item*>(child);
+  for (auto child : m_standardBrushes.children()) {
+    SelectBrushItem* item = static_cast<SelectBrushItem*>(child);
 
     // Same type and same image
     if (item->brush() &&
         item->brush()->type() == brush->type() &&
         (brush->type() != kImageBrushType ||
          item->brush()->image() == brush->image())) {
-      m_buttons->setSelectedItem(item);
-      break;
+      m_standardBrushes.setSelectedItem(item);
+      return;
     }
   }
 }
 
-void BrushPopup::regenerate(const gfx::Rect& box, const Brushes& brushes)
+void BrushPopup::regenerate(const gfx::Rect& box)
 {
-  int columns = 3;
-
-  if (m_buttons) {
-    for (auto child : m_buttons->children())
-      m_tooltipManager->removeTooltipFor(child);
-    removeChild(m_buttons.get());
-    m_buttons.reset();
+  const doc::Brushes& brushes = App::instance()->brushes().getCustomBrushes();
+
+  if (m_customBrushes) {
+    // As BrushPopup::regenerate() can be called when a
+    // "m_customBrushes" button is clicked we cannot delete
+    // "m_customBrushes" right now.
+    m_customBrushes->parent()->removeChild(m_customBrushes);
+    m_customBrushes->deferDelete();
   }
 
-  if (!defBrushes[0]) {
-    defBrushes[0].reset(new Brush(kCircleBrushType, 7, 0));
-    defBrushes[1].reset(new Brush(kSquareBrushType, 7, 0));
-    defBrushes[2].reset(new Brush(kLineBrushType, 7, 44));
-  }
-
-  m_buttons.reset(new ButtonSet(columns));
-  m_buttons->addItem(new Item(this, m_delegate, defBrushes[0]));
-  m_buttons->addItem(new Item(this, m_delegate, defBrushes[1]));
-  m_buttons->addItem(new Item(this, m_delegate, defBrushes[2]));
+  m_customBrushes = new ButtonSet(3);
+  m_customBrushes->setTriggerOnMouseUp(true);
 
-  int slot = 1;
+  auto& parts = SkinTheme::instance()->parts;
+  int slot = 0;
   for (const auto& brush : brushes) {
-    Item* item = new Item(this, m_delegate, brush, slot);
-    m_buttons->addItem(item);
-
-    Params params;
-    params.set("change", "custom");
-    params.set("slot", base::convert_to<std::string>(slot).c_str());
-    Key* key = KeyboardShortcuts::instance()->command(
-      CommandId::ChangeBrush, params);
-    if (key && !key->accels().empty()) {
-      std::string tooltip;
-      tooltip += "Shortcut: ";
-      tooltip += key->accels().front().toString();
-      m_tooltipManager->addTooltipFor(item, tooltip, TOP);
+    ++slot;
+
+    // Get shortcut
+    std::string shortcut;
+    {
+      Params params;
+      params.set("change", "custom");
+      params.set("slot", base::convert_to<std::string>(slot).c_str());
+      Key* key = KeyboardShortcuts::instance()->command(
+        CommandId::ChangeBrush, params);
+      if (key && !key->accels().empty())
+        shortcut = key->accels().front().toString();
     }
-    slot++;
+    m_customBrushes->addItem(new SelectBrushItem(m_delegate, brush, slot));
+    m_customBrushes->addItem(new BrushShortcutItem(shortcut, slot));
+    m_customBrushes->addItem(new BrushOptionsItem(this, m_delegate, slot));
   }
-  // Add empty spaces
-  while (((slot-1) % columns) > 0)
-    m_buttons->addItem(new Item(this, m_delegate, BrushRef(nullptr), slot++));
-
-  m_buttons->ItemChange.connect(base::Bind<void>(&BrushPopup::onButtonChange, this));
-  m_buttons->setTransparent(true);
-  m_buttons->setBgColor(gfx::ColorNone);
-  addChild(m_buttons.get());
 
-  gfx::Rect rc = box;
-  int buttons = m_buttons->children().size();
-  int rows = (buttons/columns + ((buttons%columns) > 0 ? 1: 0));
-  rc.w *= columns;
-  rc.h = rows * (rc.h-2*guiscale()) + 2*guiscale();
+  m_customBrushes->addItem(new NewCustomBrushItem, 3, 1);
+  m_customBrushes->setExpansive(true);
+  m_box.addChild(m_customBrushes);
 
-  setBounds(rc);
+  // Resize the window and change the hot region.
+  setBounds(gfx::Rect(box.origin(), sizeHint()));
+  setHotRegion(gfx::Region(bounds()));
 }
 
-void BrushPopup::onButtonChange()
+void BrushPopup::onBrushChanges()
 {
-  Item* item = static_cast<Item*>(m_buttons->getItem(m_buttons->selectedItem()));
-  if (item->brush())
-    BrushChange(item->brush());
+  if (isVisible()) {
+    regenerate(bounds());
+    invalidate();
+  }
 }
 
 // static
diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h
index ccd1c27..00f2cad 100644
--- a/src/app/ui/brush_popup.h
+++ b/src/app/ui/brush_popup.h
@@ -9,26 +9,20 @@
 #define APP_UI_BRUSH_POPUP_H_INCLUDED
 #pragma once
 
+#include "app/ui/button_set.h"
 #include "base/shared_ptr.h"
-#include "base/signal.h"
 #include "doc/brushes.h"
+#include "ui/box.h"
 #include "ui/popup_window.h"
-
-namespace doc {
-  class Brush;
-}
-
-namespace ui {
-  class Menu;
-  class TooltipManager;
-}
+#include "ui/tooltips.h"
 
 namespace app {
-  class ButtonSet;
 
   class BrushPopupDelegate {
   public:
     virtual ~BrushPopupDelegate() { }
+    virtual void onSelectBrush(const doc::BrushRef& brush) = 0;
+    virtual void onSelectBrushSlot(int slot) = 0;
     virtual void onDeleteBrushSlot(int slot) = 0;
     virtual void onDeleteAllBrushes() = 0;
     virtual bool onIsBrushSlotLocked(int slot) const = 0;
@@ -41,21 +35,22 @@ namespace app {
     BrushPopup(BrushPopupDelegate* delegate);
 
     void setBrush(doc::Brush* brush);
-    void regenerate(const gfx::Rect& box, const doc::Brushes& brushes);
+    void regenerate(const gfx::Rect& box);
 
     void setupTooltips(ui::TooltipManager* tooltipManager) {
       m_tooltipManager = tooltipManager;
     }
 
-    base::Signal1<void, const doc::BrushRef&> BrushChange;
-
     static she::Surface* createSurfaceForBrush(const doc::BrushRef& brush);
 
   private:
-    void onButtonChange();
+    void onStandardBrush();
+    void onBrushChanges();
 
-    base::SharedPtr<ButtonSet> m_buttons;
     ui::TooltipManager* m_tooltipManager;
+    ui::VBox m_box;
+    ButtonSet m_standardBrushes;
+    ButtonSet* m_customBrushes;
     BrushPopupDelegate* m_delegate;
   };
 
diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp
index 910802c..60d7277 100644
--- a/src/app/ui/button_set.cpp
+++ b/src/app/ui/button_set.cpp
@@ -50,9 +50,10 @@ ButtonSet::Item::Item()
   setFocusStop(true);
 }
 
-void ButtonSet::Item::setIcon(const SkinPartPtr& icon)
+void ButtonSet::Item::setIcon(const SkinPartPtr& icon, bool mono)
 {
   m_icon = icon;
+  m_mono = mono;
   invalidate();
 }
 
@@ -123,11 +124,16 @@ void ButtonSet::Item::onPaint(ui::PaintEvent& ev)
   theme->drawRect(g, rc, nw.get(), bg);
 
   if (m_icon) {
+    she::Surface* bmp = m_icon->bitmap(0);
+
     if (isSelected() && hasCapture())
-      g->drawColoredRgbaSurface(m_icon->bitmap(0), theme->colors.buttonSelectedText(),
+      g->drawColoredRgbaSurface(bmp, theme->colors.buttonSelectedText(),
+                                iconRc.x, iconRc.y);
+    else if (m_mono)
+      g->drawColoredRgbaSurface(bmp, theme->colors.buttonNormalText(),
                                 iconRc.x, iconRc.y);
     else
-      g->drawRgbaSurface(m_icon->bitmap(0), iconRc.x, iconRc.y);
+      g->drawRgbaSurface(bmp, iconRc.x, iconRc.y);
   }
 
   if (hasText()) {
@@ -160,7 +166,7 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
         if (mnemonicPressed ||
             (hasFocus() && keymsg->scancode() == kKeySpace)) {
           buttonSet()->setSelectedItem(this);
-          buttonSet()->onItemChange(this);
+          onClick();
         }
       }
       break;
@@ -172,7 +178,7 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
 
       if (static_cast<MouseMessage*>(msg)->left() &&
           !buttonSet()->m_triggerOnMouseUp) {
-        buttonSet()->onItemChange(this);
+        onClick();
       }
       break;
 
@@ -183,10 +189,10 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
 
         if (static_cast<MouseMessage*>(msg)->left()) {
           if (buttonSet()->m_triggerOnMouseUp)
-            buttonSet()->onItemChange(this);
+            onClick();
         }
         else if (static_cast<MouseMessage*>(msg)->right()) {
-          buttonSet()->onRightClick(this);
+          onRightClick();
         }
       }
       break;
@@ -232,6 +238,16 @@ void ButtonSet::Item::onSizeHint(ui::SizeHintEvent& ev)
   ev.setSizeHint(sz);
 }
 
+void ButtonSet::Item::onClick()
+{
+  buttonSet()->onItemChange(this);
+}
+
+void ButtonSet::Item::onRightClick()
+{
+  buttonSet()->onRightClick(this);
+}
+
 ButtonSet::ButtonSet(int columns)
   : Grid(columns, false)
   , m_offerCapture(true)
diff --git a/src/app/ui/button_set.h b/src/app/ui/button_set.h
index 968551e..40f7153 100644
--- a/src/app/ui/button_set.h
+++ b/src/app/ui/button_set.h
@@ -22,15 +22,18 @@ namespace app {
     class Item : public ui::Widget {
     public:
       Item();
-      void setIcon(const skin::SkinPartPtr& icon);
+      void setIcon(const skin::SkinPartPtr& icon, bool mono = false);
       skin::SkinPartPtr icon() const { return m_icon; }
       ButtonSet* buttonSet();
     protected:
       void onPaint(ui::PaintEvent& ev) override;
       bool onProcessMessage(ui::Message* msg) override;
       void onSizeHint(ui::SizeHintEvent& ev) override;
+      virtual void onClick();
+      virtual void onRightClick();
     private:
       skin::SkinPartPtr m_icon;
+      bool m_mono;
     };
 
     ButtonSet(int columns);
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 7e89e30..56807f6 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -12,6 +12,7 @@
 #include "app/ui/context_bar.h"
 
 #include "app/app.h"
+#include "app/app_brushes.h"
 #include "app/app_menus.h"
 #include "app/color_utils.h"
 #include "app/commands/commands.h"
@@ -77,13 +78,13 @@ public:
   BrushTypeField(ContextBar* owner)
     : ButtonSet(1)
     , m_owner(owner)
+    , m_brushes(App::instance()->brushes())
     , m_popupWindow(this) {
     SkinPartPtr part(new SkinPart);
     part->setBitmap(
       0, BrushPopup::createSurfaceForBrush(BrushRef(nullptr)));
 
     addItem(part);
-    m_popupWindow.BrushChange.connect(&BrushTypeField::onBrushChange, this);
   }
 
   ~BrushTypeField() {
@@ -103,6 +104,10 @@ public:
     m_popupWindow.setupTooltips(tooltipManager);
   }
 
+  void showPopupAndHighlightSlot(int slot) {
+    openPopup();
+  }
+
 protected:
   void onItemChange(Item* item) override {
     ButtonSet::onItemChange(item);
@@ -118,24 +123,55 @@ protected:
   }
 
   // BrushPopupDelegate impl
+  void onSelectBrush(const BrushRef& brush) {
+    if (brush->type() == kImageBrushType)
+      m_owner->setActiveBrush(brush);
+    else {
+      Tool* tool = App::instance()->activeTool();
+      ToolPreferences::Brush& brushPref = Preferences::instance().tool(tool).brush;
+
+      brushPref.type(static_cast<app::gen::BrushType>(brush->type()));
+
+      m_owner->setActiveBrush(
+        ContextBar::createBrushFromPreferences(&brushPref));
+    }
+  }
+
+  void onSelectBrushSlot(int slot) {
+    doc::BrushRef brush = App::instance()->brushes().getCustomBrush(slot);
+
+    // Is this an empty custom brush slot?
+    if (!brush)
+      return;
+
+    Tool* tool = App::instance()->activeTool();
+    ToolPreferences::Brush& brushPref = Preferences::instance().tool(tool).brush;
+
+    brushPref.type(static_cast<app::gen::BrushType>(brush->type()));
+    brushPref.size(brush->size());
+    brushPref.angle(brush->angle());
+
+    m_owner->setActiveBrush(brush);
+  }
+
   void onDeleteBrushSlot(int slot) override {
-    m_owner->removeBrush(slot);
+    m_brushes.removeCustomBrush(slot);
   }
 
   void onDeleteAllBrushes() override {
-    m_owner->removeAllBrushes();
+    m_brushes.removeAllCustomBrushes();
   }
 
   bool onIsBrushSlotLocked(int slot) const override {
-    return m_owner->isBrushSlotLocked(slot);
+    return m_brushes.isCustomBrushLocked(slot);
   }
 
   void onLockBrushSlot(int slot) override {
-    m_owner->lockBrushSlot(slot);
+    m_brushes.lockCustomBrush(slot);
   }
 
   void onUnlockBrushSlot(int slot) override {
-    m_owner->unlockBrushSlot(slot);
+    m_brushes.unlockCustomBrush(slot);
   }
 
 private:
@@ -151,7 +187,7 @@ private:
   void openPopup() {
     doc::BrushRef brush = m_owner->activeBrush();
 
-    m_popupWindow.regenerate(getPopupBox(), m_owner->getBrushes());
+    m_popupWindow.regenerate(getPopupBox());
     m_popupWindow.setBrush(brush.get());
 
     Region rgn(m_popupWindow.bounds().createUnion(bounds()));
@@ -165,21 +201,8 @@ private:
     deselectItems();
   }
 
-  void onBrushChange(const BrushRef& brush) {
-    if (brush->type() == kImageBrushType)
-      m_owner->setActiveBrush(brush);
-    else {
-      Tool* tool = App::instance()->activeTool();
-      ToolPreferences::Brush& brushPref = Preferences::instance().tool(tool).brush;
-
-      brushPref.type(static_cast<app::gen::BrushType>(brush->type()));
-
-      m_owner->setActiveBrush(
-        ContextBar::createBrushFromPreferences(&brushPref));
-    }
-  }
-
   ContextBar* m_owner;
+  AppBrushes& m_brushes;
   BrushPopup m_popupWindow;
 };
 
@@ -1633,85 +1656,18 @@ void ContextBar::updateAutoSelectLayer(bool state)
   m_autoSelectLayer->setSelected(state);
 }
 
-int ContextBar::addBrush(const doc::BrushRef& brush)
-{
-  // Use an empty slot
-  for (size_t i=0; i<m_brushes.size(); ++i) {
-    if (!m_brushes[i].locked ||
-        !m_brushes[i].brush) {
-      m_brushes[i].brush = brush;
-      return i+1;
-    }
-  }
-
-  m_brushes.push_back(BrushSlot(brush));
-  return (int)m_brushes.size(); // Returns the slot
-}
-
-void ContextBar::removeBrush(int slot)
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size()) {
-    m_brushes[slot].brush.reset();
-
-    // Erase empty trailing slots
-    while (!m_brushes.empty() &&
-           !m_brushes[m_brushes.size()-1].brush)
-      m_brushes.erase(--m_brushes.end());
-  }
-}
-
-void ContextBar::removeAllBrushes()
-{
-  while (!m_brushes.empty())
-    m_brushes.erase(--m_brushes.end());
-}
-
 void ContextBar::setActiveBrushBySlot(int slot)
 {
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    m_brushes[slot].locked = true;
-    setActiveBrush(m_brushes[slot].brush);
-  }
-}
-
-Brushes ContextBar::getBrushes()
-{
-  Brushes brushes;
-  for (const auto& slot : m_brushes)
-    brushes.push_back(slot.brush);
-  return brushes;
-}
-
-void ContextBar::lockBrushSlot(int slot)
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    m_brushes[slot].locked = true;
-  }
-}
+  AppBrushes& brushes = App::instance()->brushes();
 
-void ContextBar::unlockBrushSlot(int slot)
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    m_brushes[slot].locked = false;
+  if (brushes.hasCustomBrush(slot)) {
+    brushes.lockCustomBrush(slot);
+    setActiveBrush(brushes.getCustomBrush(slot));
   }
-}
-
-bool ContextBar::isBrushSlotLocked(int slot) const
-{
-  --slot;
-  if (slot >= 0 && slot < (int)m_brushes.size() &&
-      m_brushes[slot].brush) {
-    return m_brushes[slot].locked;
+  else {
+    updateForTool(App::instance()->activeTool());
+    m_brushType->showPopupAndHighlightSlot(slot);
   }
-  else
-    return false;
 }
 
 void ContextBar::setActiveBrush(const doc::BrushRef& brush)
diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h
index 097cb16..0d30e45 100644
--- a/src/app/ui/context_bar.h
+++ b/src/app/ui/context_bar.h
@@ -16,7 +16,6 @@
 #include "base/connection.h"
 #include "base/observable.h"
 #include "doc/brush.h"
-#include "doc/brushes.h"
 #include "ui/box.h"
 
 #include <vector>
@@ -53,21 +52,10 @@ namespace app {
     void updateAutoSelectLayer(bool state);
 
     void setActiveBrush(const doc::BrushRef& brush);
+    void setActiveBrushBySlot(int slot);
     doc::BrushRef activeBrush(tools::Tool* tool = nullptr) const;
     void discardActiveBrush();
 
-    // Adds a new brush and returns the slot number where the brush
-    // is now available.
-    int addBrush(const doc::BrushRef& brush);
-    void removeBrush(int slot);
-    void removeAllBrushes();
-    void setActiveBrushBySlot(int slot);
-    doc::Brushes getBrushes();
-
-    void lockBrushSlot(int slot);
-    void unlockBrushSlot(int slot);
-    bool isBrushSlotLocked(int slot) const;
-
     static doc::BrushRef createBrushFromPreferences(
       ToolPreferences::Brush* brushPref = nullptr);
 
@@ -91,21 +79,6 @@ namespace app {
     void onSymmetryModeChange();
     void onDropPixels(ContextBarObserver::DropAction action);
 
-    struct BrushSlot {
-      // True if the user locked the brush using the shortcut key to
-      // access it.
-      bool locked;
-
-      // Can be null if the user deletes the brush.
-      doc::BrushRef brush;
-
-      BrushSlot(const doc::BrushRef& brush)
-        : locked(false), brush(brush) {
-      }
-    };
-
-    typedef std::vector<BrushSlot> BrushSlots;
-
     class BrushTypeField;
     class BrushAngleField;
     class BrushSizeField;
@@ -155,7 +128,6 @@ namespace app {
     RotAlgorithmField* m_rotAlgo;
     DropPixelsField* m_dropPixels;
     doc::BrushRef m_activeBrush;
-    BrushSlots m_brushes;
     ui::Label* m_selectBoxHelp;
     SymmetryField* m_symmetry;
     base::ScopedConnection m_sizeConn;

-- 
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