diff --git a/data/pref.xml b/data/pref.xml
index aea4a0424..1cd907dc4 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -128,6 +128,17 @@
+
diff --git a/data/widgets/brush_slot_params.xml b/data/widgets/brush_slot_params.xml
new file mode 100644
index 000000000..6ad243acd
--- /dev/null
+++ b/data/widgets/brush_slot_params.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 490c21cfd..275655bb6 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 3c1e7bc49..ba1ae1c65 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 m_mainWindow;
FileList m_files;
base::UniquePtr 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 000000000..715433ff3
--- /dev/null
+++ b/src/app/app_brushes.cpp
@@ -0,0 +1,118 @@
+// 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::addBrushSlot(const BrushSlot& brush)
+{
+ // Use an empty slot
+ for (size_t i=0; i= 0 && slot < (int)m_slots.size()) {
+ m_slots[slot] = BrushSlot();
+
+ // Erase empty trailing slots
+ while (!m_slots.empty() &&
+ !m_slots[m_slots.size()-1].brush())
+ m_slots.erase(--m_slots.end());
+
+ ItemsChange();
+ }
+}
+
+void AppBrushes::removeAllBrushSlots()
+{
+ while (!m_slots.empty())
+ m_slots.erase(--m_slots.end());
+
+ ItemsChange();
+}
+
+bool AppBrushes::hasBrushSlot(slot_id slot) const
+{
+ --slot;
+ return (slot >= 0 && slot < (int)m_slots.size() &&
+ !m_slots[slot].isEmpty());
+}
+
+BrushSlot AppBrushes::getBrushSlot(slot_id slot) const
+{
+ --slot;
+ if (slot >= 0 && slot < (int)m_slots.size())
+ return m_slots[slot];
+ else
+ return BrushSlot();
+}
+
+void AppBrushes::setBrushSlot(slot_id slot, const BrushSlot& brush)
+{
+ --slot;
+ if (slot >= 0 && slot < (int)m_slots.size()) {
+ m_slots[slot] = brush;
+ ItemsChange();
+ }
+}
+
+void AppBrushes::lockBrushSlot(slot_id slot)
+{
+ --slot;
+ if (slot >= 0 && slot < (int)m_slots.size() &&
+ m_slots[slot].brush()) {
+ m_slots[slot].setLocked(true);
+ }
+}
+
+void AppBrushes::unlockBrushSlot(slot_id slot)
+{
+ --slot;
+ if (slot >= 0 && slot < (int)m_slots.size() &&
+ m_slots[slot].brush()) {
+ m_slots[slot].setLocked(false);
+ }
+}
+
+bool AppBrushes::isBrushSlotLocked(slot_id slot) const
+{
+ --slot;
+ if (slot >= 0 && slot < (int)m_slots.size() &&
+ !m_slots[slot].isEmpty()) {
+ 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 000000000..dc451c2b4
--- /dev/null
+++ b/src/app/app_brushes.h
@@ -0,0 +1,52 @@
+// 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 "app/brush_slot.h"
+#include "base/signal.h"
+#include "doc/brushes.h"
+
+#include
+
+namespace app {
+
+ class AppBrushes {
+ public:
+ // Number of slot (a range from 1 to AppBrushes::size() inclusive)
+ typedef int slot_id;
+ typedef std::vector BrushSlots;
+
+ AppBrushes();
+
+ // Adds a new brush and returns the slot number where the brush
+ // is now available.
+ slot_id addBrushSlot(const BrushSlot& brush);
+ void removeBrushSlot(slot_id slot);
+ void removeAllBrushSlots();
+ bool hasBrushSlot(slot_id slot) const;
+ const doc::Brushes& getStandardBrushes() { return m_standard; }
+ BrushSlot getBrushSlot(slot_id slot) const;
+ void setBrushSlot(slot_id slot, const BrushSlot& brush);
+ const BrushSlots& getBrushSlots() const { return m_slots; }
+
+ void lockBrushSlot(slot_id slot);
+ void unlockBrushSlot(slot_id slot);
+ bool isBrushSlotLocked(slot_id slot) const;
+
+ base::Signal0 ItemsChange;
+
+ private:
+ doc::Brushes m_standard;
+ BrushSlots m_slots;
+ };
+
+} // namespace app
+
+#endif
diff --git a/src/app/brush_slot.h b/src/app/brush_slot.h
new file mode 100644
index 000000000..d07cdac6a
--- /dev/null
+++ b/src/app/brush_slot.h
@@ -0,0 +1,107 @@
+// 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_BRUSH_SLOT_H_INCLUDED
+#define APP_BRUSH_SLOT_H_INCLUDED
+#pragma once
+
+#include "app/color.h"
+#include "app/shade.h"
+#include "app/tools/ink_type.h"
+#include "doc/brush.h"
+
+namespace app {
+
+// Custom brush slot
+class BrushSlot {
+public:
+ enum class Flags {
+ Locked = 0x0001,
+ BrushType = 0x0002,
+ BrushSize = 0x0004,
+ BrushAngle = 0x0008,
+ FgColor = 0x0010,
+ BgColor = 0x0020,
+ InkType = 0x0040,
+ InkOpacity = 0x0080,
+ Shade = 0x0100,
+ PixelPerfect = 0x0200
+ };
+
+ BrushSlot(Flags flags = Flags(0),
+ const doc::BrushRef& brush = doc::BrushRef(nullptr),
+ const app::Color& fgColor = app::Color::fromMask(),
+ const app::Color& bgColor = app::Color::fromMask(),
+ tools::InkType inkType = tools::InkType::DEFAULT,
+ int inkOpacity = 255,
+ const Shade& shade = Shade(),
+ bool pixelPerfect = false)
+ : m_flags(flags)
+ , m_brush(brush)
+ , m_fgColor(fgColor)
+ , m_bgColor(bgColor)
+ , m_inkType(inkType)
+ , m_inkOpacity(inkOpacity)
+ , m_shade(shade)
+ , m_pixelPerfect(pixelPerfect) {
+ }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags flags) { m_flags = flags; }
+
+ bool isEmpty() const {
+ return int(m_flags) == 0;
+ }
+
+ bool hasFlag(Flags flag) const {
+ return ((int(m_flags) & int(flag)) == int(flag));
+ }
+
+ bool hasBrush() const {
+ return
+ (brush() &&
+ (hasFlag(Flags::BrushType) ||
+ hasFlag(Flags::BrushSize) ||
+ hasFlag(Flags::BrushAngle)));
+ }
+
+ // Can be null if the user deletes the brush.
+ doc::BrushRef brush() const { return m_brush; }
+ app::Color fgColor() const { return m_fgColor; }
+ app::Color bgColor() const { return m_bgColor; }
+ tools::InkType inkType() const { return m_inkType; }
+ int inkOpacity() const { return m_inkOpacity; }
+ const Shade& shade() const { return m_shade; }
+ bool pixelPerfect() const { return m_pixelPerfect; }
+
+ // True if the user locked the brush using the shortcut key to
+ // access it.
+ bool locked() const {
+ return hasFlag(Flags::Locked);
+ }
+
+ void setLocked(bool locked) {
+ if (locked)
+ m_flags = static_cast(int(m_flags) | int(Flags::Locked));
+ else
+ m_flags = static_cast(int(m_flags) & ~int(Flags::Locked));
+ }
+
+private:
+ Flags m_flags;
+ doc::BrushRef m_brush;
+ app::Color m_fgColor;
+ app::Color m_bgColor;
+ tools::InkType m_inkType;
+ int m_inkOpacity;
+ Shade m_shade;
+ bool m_pixelPerfect;
+};
+
+} // namespace app
+
+#endif
diff --git a/src/app/commands/cmd_keyboard_shortcuts.cpp b/src/app/commands/cmd_keyboard_shortcuts.cpp
index e2ffa7982..acd25cd79 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(&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 aa7b71a8b..165770367 100644
--- a/src/app/commands/cmd_new_brush.cpp
+++ b/src/app/commands/cmd_new_brush.cpp
@@ -149,9 +149,9 @@ 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);
+ int slot = App::instance()->brushes().addBrushSlot(
+ BrushSlot(BrushSlot::Flags::BrushType, 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 238f06075..44c613743 100644
--- a/src/app/ui/brush_popup.cpp
+++ b/src/app/ui/brush_popup.cpp
@@ -11,12 +11,19 @@
#include "app/ui/brush_popup.h"
+#include "app/app.h"
+#include "app/app_brushes.h"
+#include "app/brush_slot.h"
+#include "app/commands/command.h"
#include "app/commands/commands.h"
+#include "app/modules/gui.h"
#include "app/modules/palettes.h"
+#include "app/pref/preferences.h"
#include "app/ui/app_menuitem.h"
#include "app/ui/button_set.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 +35,14 @@
#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"
+
+#include "brush_slot_params.xml.h"
namespace app {
@@ -41,51 +52,166 @@ using namespace ui;
namespace {
-class Item : public ButtonSet::Item {
+void show_popup_menu(PopupWindow* popupWindow, Menu* popupMenu,
+ const gfx::Point& pt)
+{
+ // Here we make the popup window temporaly floating, so it's
+ // not closed by the popup menu.
+ popupWindow->makeFloating();
+
+ popupMenu->showPopup(pt);
+
+ // Add the menu popup region to the window popup hot region so it's
+ // not closed after we close the menu.
+ popupWindow->makeFixed();
+
+ gfx::Region rgn;
+ rgn.createUnion(gfx::Region(popupWindow->bounds()),
+ gfx::Region(popupMenu->bounds()));
+ popupWindow->setHotRegion(rgn);
+}
+
+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 BrushSlot& brush, int slot = -1)
+ : m_delegate(delegate)
, m_brush(brush)
, m_slot(slot) {
- SkinPartPtr icon(new SkinPart);
- icon->setBitmap(0, BrushPopup::createSurfaceForBrush(brush));
- setIcon(icon);
+ if (m_brush.hasBrush()) {
+ SkinPartPtr icon(new SkinPart);
+ icon->setBitmap(0, BrushPopup::createSurfaceForBrush(m_brush.brush()));
+ setIcon(icon);
+ }
}
- const BrushRef& brush() const {
+ const BrushSlot& brush() const {
return m_brush;
}
-protected:
- bool onProcessMessage(Message* msg) override {
- if (msg->type() == kMouseUpMessage && m_slot > 0) {
- MouseMessage* mouseMsg = static_cast(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);
+private:
- // 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);
- }
- }
- return ButtonSet::Item::onProcessMessage(msg);
+ void onClick() override {
+ if (m_slot >= 0)
+ m_delegate->onSelectBrushSlot(m_slot);
+ else if (m_brush.hasBrush())
+ m_delegate->onSelectBrush(m_brush.brush());
}
private:
+
+ BrushPopupDelegate* m_delegate;
+ BrushSlot 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(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);
+ }
+ }
+
+ 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 save("Save Brush Here");
+ AppMenuItem lockItem("Locked");
+ AppMenuItem deleteItem("Delete");
+ AppMenuItem deleteAllItem("Delete All");
+
+ lockItem.setSelected(m_delegate->onIsBrushSlotLocked(m_slot));
+
+ save.Click.connect(&BrushOptionsItem::onSaveBrush, this);
+ lockItem.Click.connect(&BrushOptionsItem::onLockBrush, this);
+ deleteItem.Click.connect(&BrushOptionsItem::onDeleteBrush, this);
+ deleteAllItem.Click.connect(&BrushOptionsItem::onDeleteAllBrushes, this);
+
+ menu.addChild(&save);
+ menu.addChild(new MenuSeparator);
+ menu.addChild(&lockItem);
+ menu.addChild(&deleteItem);
+ menu.addChild(new MenuSeparator);
+ menu.addChild(&deleteAllItem);
+ menu.addChild(new Label(""));
+ menu.addChild(new Separator("Saved Parameters", HORIZONTAL));
+
+ app::gen::BrushSlotParams params;
+ menu.addChild(¶ms);
+
+ // Load preferences
+ AppBrushes& brushes = App::instance()->brushes();
+ BrushSlot brush = brushes.getBrushSlot(m_slot);
+
+ params.brushType()->setSelected(brush.hasFlag(BrushSlot::Flags::BrushType));
+ params.brushSize()->setSelected(brush.hasFlag(BrushSlot::Flags::BrushSize));
+ params.brushAngle()->setSelected(brush.hasFlag(BrushSlot::Flags::BrushAngle));
+ params.fgColor()->setSelected(brush.hasFlag(BrushSlot::Flags::FgColor));
+ params.bgColor()->setSelected(brush.hasFlag(BrushSlot::Flags::BgColor));
+ params.inkType()->setSelected(brush.hasFlag(BrushSlot::Flags::InkType));
+ params.inkOpacity()->setSelected(brush.hasFlag(BrushSlot::Flags::InkOpacity));
+ params.shade()->setSelected(brush.hasFlag(BrushSlot::Flags::Shade));
+ params.pixelPerfect()->setSelected(brush.hasFlag(BrushSlot::Flags::PixelPerfect));
+
+ show_popup_menu(m_popup, &menu,
+ gfx::Point(origin().x, origin().y+bounds().h));
+
+ int flags = 0;
+ if (params.brushType()->isSelected()) flags |= int(BrushSlot::Flags::BrushType);
+ if (params.brushSize()->isSelected()) flags |= int(BrushSlot::Flags::BrushSize);
+ if (params.brushAngle()->isSelected()) flags |= int(BrushSlot::Flags::BrushAngle);
+ if (params.fgColor()->isSelected()) flags |= int(BrushSlot::Flags::FgColor);
+ if (params.bgColor()->isSelected()) flags |= int(BrushSlot::Flags::BgColor);
+ if (params.inkType()->isSelected()) flags |= int(BrushSlot::Flags::InkType);
+ if (params.inkOpacity()->isSelected()) flags |= int(BrushSlot::Flags::InkOpacity);
+ if (params.shade()->isSelected()) flags |= int(BrushSlot::Flags::Shade);
+ if (params.pixelPerfect()->isSelected()) flags |= int(BrushSlot::Flags::PixelPerfect);
+
+ if (brush.flags() != BrushSlot::Flags(flags)) {
+ brush.setFlags(BrushSlot::Flags(flags));
+ brushes.setBrushSlot(m_slot, brush);
+ }
+ }
+
+private:
+
+ void onSaveBrush() {
+ AppBrushes& brushes = App::instance()->brushes();
+ brushes.setBrushSlot(
+ m_slot, m_delegate->onCreateBrushSlotFromActivePreferences());
+ brushes.lockBrushSlot(m_slot);
+ }
+
void onLockBrush() {
if (m_delegate->onIsBrushSlotLocked(m_slot))
m_delegate->onUnlockBrushSlot(m_slot);
@@ -107,98 +233,186 @@ private:
int m_slot;
};
-} // anonymous namespace
+class NewCustomBrushItem : public ButtonSet::Item {
+public:
+ NewCustomBrushItem(BrushPopupDelegate* delegate)
+ : m_delegate(delegate) {
+ setText("Save Brush");
+ }
-static BrushRef defBrushes[3];
+private:
+ void onClick() override {
+ AppBrushes& brushes = App::instance()->brushes();
+ auto slot = brushes.addBrushSlot(
+ m_delegate->onCreateBrushSlotFromActivePreferences());
+ brushes.lockBrushSlot(slot);
+ }
+
+ BrushPopupDelegate* m_delegate;
+};
+
+class NewBrushOptionsItem : public ButtonSet::Item {
+public:
+ NewBrushOptionsItem() {
+ setIcon(SkinTheme::instance()->parts.iconArrowDown(), true);
+ }
+
+private:
+ void onClick() override {
+ Menu menu;
+
+ menu.addChild(new Separator("Parameters to Save", HORIZONTAL));
+
+ app::gen::BrushSlotParams params;
+ menu.addChild(¶ms);
+
+ // Load preferences
+ auto& saveBrush = Preferences::instance().saveBrush;
+ params.brushType()->setSelected(saveBrush.brushType());
+ params.brushSize()->setSelected(saveBrush.brushSize());
+ params.brushAngle()->setSelected(saveBrush.brushAngle());
+ params.fgColor()->setSelected(saveBrush.fgColor());
+ params.bgColor()->setSelected(saveBrush.bgColor());
+ params.inkType()->setSelected(saveBrush.inkType());
+ params.inkOpacity()->setSelected(saveBrush.inkOpacity());
+ params.shade()->setSelected(saveBrush.shade());
+ params.pixelPerfect()->setSelected(saveBrush.pixelPerfect());
+
+ show_popup_menu(static_cast(window()), &menu,
+ gfx::Point(origin().x, origin().y+bounds().h));
+
+ // Save preferences
+ if (saveBrush.brushType() != params.brushType()->isSelected())
+ saveBrush.brushType(params.brushType()->isSelected());
+ if (saveBrush.brushSize() != params.brushSize()->isSelected())
+ saveBrush.brushSize(params.brushSize()->isSelected());
+ if (saveBrush.brushAngle() != params.brushAngle()->isSelected())
+ saveBrush.brushAngle(params.brushAngle()->isSelected());
+ if (saveBrush.fgColor() != params.fgColor()->isSelected())
+ saveBrush.fgColor(params.fgColor()->isSelected());
+ if (saveBrush.bgColor() != params.bgColor()->isSelected())
+ saveBrush.bgColor(params.bgColor()->isSelected());
+ if (saveBrush.inkType() != params.inkType()->isSelected())
+ saveBrush.inkType(params.inkType()->isSelected());
+ if (saveBrush.inkOpacity() != params.inkOpacity()->isSelected())
+ saveBrush.inkOpacity(params.inkOpacity()->isSelected());
+ if (saveBrush.shade() != params.shade()->isSelected())
+ saveBrush.shade(params.shade()->isSelected());
+ if (saveBrush.pixelPerfect() != params.pixelPerfect()->isSelected())
+ saveBrush.pixelPerfect(params.pixelPerfect()->isSelected());
+ }
+};
+
+} // 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();
+ m_standardBrushes.setTriggerOnMouseUp(true);
+
+ 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, BrushSlot(BrushSlot::Flags::BrushType, 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- (child);
+ for (auto child : m_standardBrushes.children()) {
+ SelectBrushItem* item = static_cast(child);
// Same type and same image
- if (item->brush() &&
- item->brush()->type() == brush->type() &&
+ if (item->brush().hasBrush() &&
+ item->brush().brush()->type() == brush->type() &&
(brush->type() != kImageBrushType ||
- item->brush()->image() == brush->image())) {
- m_buttons->setSelectedItem(item);
- break;
+ item->brush().brush()->image() == brush->image())) {
+ 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;
+ auto& brushSlots = App::instance()->brushes().getBrushSlots();
- if (m_buttons) {
- for (auto child : m_buttons->children())
- m_tooltipManager->removeTooltipFor(child);
- removeChild(m_buttons.get());
- m_buttons.reset();
+ 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_customBrushes = new ButtonSet(3);
+ m_customBrushes->setTriggerOnMouseUp(true);
- 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]));
+ auto& parts = SkinTheme::instance()->parts;
+ int slot = 0;
+ for (const auto& brush : brushSlots) {
+ ++slot;
- int slot = 1;
- 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(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);
+ // Get shortcut
+ std::string shortcut;
+ {
+ Params params;
+ params.set("change", "custom");
+ params.set("slot", base::convert_to(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(&BrushPopup::onButtonChange, this));
- m_buttons->setTransparent(true);
- m_buttons->setBgColor(gfx::ColorNone);
- addChild(m_buttons.get());
+ m_customBrushes->addItem(new NewCustomBrushItem(m_delegate), 2, 1);
+ m_customBrushes->addItem(new NewBrushOptionsItem);
+ m_customBrushes->setExpansive(true);
+ m_box.addChild(m_customBrushes);
- 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();
-
- 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
- (m_buttons->getItem(m_buttons->selectedItem()));
- if (item->brush())
- BrushChange(item->brush());
+ if (isVisible()) {
+ gfx::Region rgn;
+ getDrawableRegion(rgn, DrawableRegionFlags(kCutTopWindows | kUseChildArea));
+
+ regenerate(bounds());
+ invalidate();
+
+ parent()->invalidateRegion(rgn);
+ }
}
// static
diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h
index ccd1c2715..fd109802c 100644
--- a/src/app/ui/brush_popup.h
+++ b/src/app/ui/brush_popup.h
@@ -9,26 +9,23 @@
#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 BrushSlot;
class BrushPopupDelegate {
public:
virtual ~BrushPopupDelegate() { }
+ virtual BrushSlot onCreateBrushSlotFromActivePreferences() = 0;
+ 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 +38,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 BrushChange;
-
static she::Surface* createSurfaceForBrush(const doc::BrushRef& brush);
private:
- void onButtonChange();
+ void onStandardBrush();
+ void onBrushChanges();
- base::SharedPtr 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 910802cc1..16f2d20b8 100644
--- a/src/app/ui/button_set.cpp
+++ b/src/app/ui/button_set.cpp
@@ -33,6 +33,10 @@ namespace app {
using namespace ui;
using namespace app::skin;
+// Last selected item for ButtonSet activated on mouse up when the
+// mouse capture is get.
+static int g_itemBeforeCapture = -1;
+
WidgetType buttonset_item_type()
{
static WidgetType type = kGenericWidget;
@@ -50,9 +54,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 +128,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,41 +170,64 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg)
if (mnemonicPressed ||
(hasFocus() && keymsg->scancode() == kKeySpace)) {
buttonSet()->setSelectedItem(this);
- buttonSet()->onItemChange(this);
+ onClick();
}
}
break;
case ui::kMouseDownMessage:
+ // Only for single-item and trigerred on mouse up ButtonSets: We
+ // save the current selected item to restore it just in case the
+ // user leaves the ButtonSet without releasing the mouse button
+ // and the mouse capture if offered to other ButtonSet.
+ if (buttonSet()->m_triggerOnMouseUp) {
+ ASSERT(g_itemBeforeCapture < 0);
+ g_itemBeforeCapture = buttonSet()->selectedItem();
+ }
+
captureMouse();
buttonSet()->setSelectedItem(this);
invalidate();
if (static_cast(msg)->left() &&
!buttonSet()->m_triggerOnMouseUp) {
- buttonSet()->onItemChange(this);
+ onClick();
}
break;
case ui::kMouseUpMessage:
if (hasCapture()) {
+ if (g_itemBeforeCapture >= 0)
+ g_itemBeforeCapture = -1;
+
releaseMouse();
invalidate();
if (static_cast(msg)->left()) {
if (buttonSet()->m_triggerOnMouseUp)
- buttonSet()->onItemChange(this);
+ onClick();
}
else if (static_cast(msg)->right()) {
- buttonSet()->onRightClick(this);
+ onRightClick();
}
}
break;
case ui::kMouseMoveMessage:
if (hasCapture()) {
- if (buttonSet()->m_offerCapture)
- offerCapture(static_cast(msg), buttonset_item_type());
+ if (buttonSet()->m_offerCapture) {
+ if (offerCapture(static_cast(msg), buttonset_item_type())) {
+ // Only for ButtonSets trigerred on mouse up.
+ if (buttonSet()->m_triggerOnMouseUp &&
+ g_itemBeforeCapture >= 0) {
+ // As we never received a kMouseUpMessage (so we never
+ // called onClick()), we have to restore the selected
+ // item at the point when we received the mouse capture.
+ buttonSet()->setSelectedItem(g_itemBeforeCapture);
+ g_itemBeforeCapture = -1;
+ }
+ }
+ }
}
break;
@@ -232,6 +265,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 968551e7a..40f71534c 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 7e89e301e..b5904dc12 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,69 @@ protected:
}
// BrushPopupDelegate impl
+ BrushSlot onCreateBrushSlotFromActivePreferences() override {
+ auto& pref = Preferences::instance();
+ auto& saveBrush = pref.saveBrush;
+ auto& toolPref = pref.tool(App::instance()->activeTool());
+
+ int flags = 0;
+ if (saveBrush.brushType()) flags |= int(BrushSlot::Flags::BrushType);
+ if (saveBrush.brushSize()) flags |= int(BrushSlot::Flags::BrushSize);
+ if (saveBrush.brushAngle()) flags |= int(BrushSlot::Flags::BrushAngle);
+ if (saveBrush.fgColor()) flags |= int(BrushSlot::Flags::FgColor);
+ if (saveBrush.bgColor()) flags |= int(BrushSlot::Flags::BgColor);
+ if (saveBrush.inkType()) flags |= int(BrushSlot::Flags::InkType);
+ if (saveBrush.inkOpacity()) flags |= int(BrushSlot::Flags::InkOpacity);
+ if (saveBrush.shade()) flags |= int(BrushSlot::Flags::Shade);
+ if (saveBrush.pixelPerfect()) flags |= int(BrushSlot::Flags::PixelPerfect);
+
+ return BrushSlot(
+ BrushSlot::Flags(flags),
+ ContextBar::createBrushFromPreferences(),
+ pref.colorBar.fgColor(),
+ pref.colorBar.bgColor(),
+ toolPref.ink(),
+ toolPref.opacity(),
+ m_owner->getShade(),
+ toolPref.freehandAlgorithm() == tools::FreehandAlgorithm::PIXEL_PERFECT);
+ }
+
+ void onSelectBrush(const BrushRef& brush) override {
+ 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(brush->type()));
+
+ m_owner->setActiveBrush(
+ ContextBar::createBrushFromPreferences(&brushPref));
+ }
+ }
+
+ void onSelectBrushSlot(int slot) override {
+ m_owner->setActiveBrushBySlot(slot);
+ }
+
void onDeleteBrushSlot(int slot) override {
- m_owner->removeBrush(slot);
+ m_brushes.removeBrushSlot(slot);
}
void onDeleteAllBrushes() override {
- m_owner->removeAllBrushes();
+ m_brushes.removeAllBrushSlots();
}
bool onIsBrushSlotLocked(int slot) const override {
- return m_owner->isBrushSlotLocked(slot);
+ return m_brushes.isBrushSlotLocked(slot);
}
void onLockBrushSlot(int slot) override {
- m_owner->lockBrushSlot(slot);
+ m_brushes.lockBrushSlot(slot);
}
void onUnlockBrushSlot(int slot) override {
- m_owner->unlockBrushSlot(slot);
+ m_brushes.unlockBrushSlot(slot);
}
private:
@@ -151,7 +201,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 +215,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(brush->type()));
-
- m_owner->setActiveBrush(
- ContextBar::createBrushFromPreferences(&brushPref));
- }
- }
-
ContextBar* m_owner;
+ AppBrushes& m_brushes;
BrushPopup m_popupWindow;
};
@@ -696,6 +733,14 @@ public:
return m_shade.createShadeRemap(left);
}
+ Shade getShade() const {
+ return m_shade.getShade();
+ }
+
+ void setShade(const Shade& shade) {
+ m_shade.setShade(shade);
+ }
+
private:
void onShowMenu() {
loadShades();
@@ -1633,85 +1678,61 @@ 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= 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);
- }
-}
+ AppBrushes& brushes = App::instance()->brushes();
+ BrushSlot brush = brushes.getBrushSlot(slot);
+ if (!brush.isEmpty()) {
+ brushes.lockBrushSlot(slot);
-Brushes ContextBar::getBrushes()
-{
- Brushes brushes;
- for (const auto& slot : m_brushes)
- brushes.push_back(slot.brush);
- return brushes;
-}
+ Tool* tool = App::instance()->activeTool();
+ Preferences& pref = Preferences::instance();
+ ToolPreferences& toolPref = pref.tool(tool);
+ ToolPreferences::Brush& brushPref = toolPref.brush;
-void ContextBar::lockBrushSlot(int slot)
-{
- --slot;
- if (slot >= 0 && slot < (int)m_brushes.size() &&
- m_brushes[slot].brush) {
- m_brushes[slot].locked = true;
- }
-}
+ if (brush.brush()) {
+ if (brush.brush()->type() == doc::kImageBrushType) {
+ setActiveBrush(brush.brush());
+ }
+ else {
+ setActiveBrush(ContextBar::createBrushFromPreferences());
-void ContextBar::unlockBrushSlot(int slot)
-{
- --slot;
- if (slot >= 0 && slot < (int)m_brushes.size() &&
- m_brushes[slot].brush) {
- m_brushes[slot].locked = false;
- }
-}
+ if (brush.hasFlag(BrushSlot::Flags::BrushType))
+ brushPref.type(static_cast(brush.brush()->type()));
-bool ContextBar::isBrushSlotLocked(int slot) const
-{
- --slot;
- if (slot >= 0 && slot < (int)m_brushes.size() &&
- m_brushes[slot].brush) {
- return m_brushes[slot].locked;
+ if (brush.hasFlag(BrushSlot::Flags::BrushSize))
+ brushPref.size(brush.brush()->size());
+
+ if (brush.hasFlag(BrushSlot::Flags::BrushAngle))
+ brushPref.angle(brush.brush()->angle());
+ }
+ }
+
+ if (brush.hasFlag(BrushSlot::Flags::FgColor))
+ pref.colorBar.fgColor(brush.fgColor());
+
+ if (brush.hasFlag(BrushSlot::Flags::BgColor))
+ pref.colorBar.bgColor(brush.bgColor());
+
+ if (brush.hasFlag(BrushSlot::Flags::InkType))
+ setInkType(brush.inkType());
+
+ if (brush.hasFlag(BrushSlot::Flags::InkOpacity))
+ toolPref.opacity(brush.inkOpacity());
+
+ if (brush.hasFlag(BrushSlot::Flags::Shade))
+ m_inkShades->setShade(brush.shade());
+
+ if (brush.hasFlag(BrushSlot::Flags::PixelPerfect))
+ toolPref.freehandAlgorithm(
+ (brush.pixelPerfect() ?
+ tools::FreehandAlgorithm::PIXEL_PERFECT:
+ tools::FreehandAlgorithm::REGULAR));
+ }
+ else {
+ updateForTool(App::instance()->activeTool());
+ m_brushType->showPopupAndHighlightSlot(slot);
}
- else
- return false;
}
void ContextBar::setActiveBrush(const doc::BrushRef& brush)
@@ -1757,6 +1778,11 @@ doc::BrushRef ContextBar::createBrushFromPreferences(ToolPreferences::Brush* bru
return brush;
}
+Shade ContextBar::getShade() const
+{
+ return m_inkShades->getShade();
+}
+
doc::Remap* ContextBar::createShadeRemap(bool left)
{
return m_inkShades->createShadeRemap(left);
diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h
index 097cb166c..df0082c73 100644
--- a/src/app/ui/context_bar.h
+++ b/src/app/ui/context_bar.h
@@ -10,13 +10,13 @@
#pragma once
#include "app/pref/preferences.h"
+#include "app/shade.h"
#include "app/tools/ink_type.h"
#include "app/tools/selection_mode.h"
#include "app/ui/context_bar_observer.h"
#include "base/connection.h"
#include "base/observable.h"
#include "doc/brush.h"
-#include "doc/brushes.h"
#include "ui/box.h"
#include
@@ -53,26 +53,16 @@ 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);
doc::Remap* createShadeRemap(bool left);
void reverseShadeColors();
+ Shade getShade() const;
void setInkType(tools::InkType type);
@@ -91,21 +81,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 BrushSlots;
-
class BrushTypeField;
class BrushAngleField;
class BrushSizeField;
@@ -155,7 +130,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;
diff --git a/src/app/widget_loader.cpp b/src/app/widget_loader.cpp
index 9db8c9acc..af2ff07d3 100644
--- a/src/app/widget_loader.cpp
+++ b/src/app/widget_loader.cpp
@@ -434,6 +434,12 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
if (!widget && columns)
widget = new ButtonSet(strtol(columns, NULL, 10));
+
+ if (ButtonSet* buttonset = dynamic_cast(widget)) {
+ bool multiple = bool_attr_is_true(elem, "multiple");
+ if (multiple)
+ buttonset->setMultipleSelection(multiple);
+ }
}
else if (elem_name == "item") {
if (!parent)
diff --git a/src/gen/ui_class.cpp b/src/gen/ui_class.cpp
index 757745e3d..600ca6f17 100644
--- a/src/gen/ui_class.cpp
+++ b/src/gen/ui_class.cpp
@@ -49,6 +49,10 @@ static void collect_widgets_with_ids(TiXmlElement* elem, XmlElements& widgets)
static std::string convert_type(const std::string& name)
{
+ static std::string parent;
+ if (name != "item")
+ parent = name;
+
if (name == "box") return "ui::Box";
if (name == "button") return "ui::Button";
if (name == "buttonset") return "app::ButtonSet";
@@ -59,6 +63,7 @@ static std::string convert_type(const std::string& name)
if (name == "entry") return "ui::Entry";
if (name == "grid") return "ui::Grid";
if (name == "hbox") return "ui::HBox";
+ if (name == "item" && parent == "buttonset") return "app::ButtonSet::Item";
if (name == "label") return "ui::Label";
if (name == "link") return "ui::LinkLabel";
if (name == "listbox") return "ui::ListBox";
diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp
index de2033bc5..5266fa0e8 100644
--- a/src/ui/widget.cpp
+++ b/src/ui/widget.cpp
@@ -1236,7 +1236,7 @@ void Widget::releaseMouse()
}
}
-void Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
+bool Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
{
if (hasCapture()) {
Widget* pick = manager()->pick(mouseMsg->position());
@@ -1250,8 +1250,10 @@ void Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
mouseMsg->position());
mouseMsg2->addRecipient(pick);
manager()->enqueueMessage(mouseMsg2);
+ return true;
}
}
+ return false;
}
bool Widget::hasFocus()
diff --git a/src/ui/widget.h b/src/ui/widget.h
index c41839d29..0d48ac78b 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -340,8 +340,9 @@ namespace ui {
bool hasMouseOver();
bool hasCapture();
- // Offer the capture to widgets of the given type
- void offerCapture(ui::MouseMessage* mouseMsg, int widget_type);
+ // Offer the capture to widgets of the given type. Returns true if
+ // the capture was passed to other widget.
+ bool offerCapture(ui::MouseMessage* mouseMsg, int widget_type);
// Returns lower-case letter that represet the mnemonic of the widget
// (the underscored character, i.e. the letter after & symbol).