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.
This commit is contained in:
David Capello 2015-12-15 17:12:11 -03:00
parent cff6d1cc65
commit 2d64a1926e
12 changed files with 478 additions and 238 deletions

View File

@ -90,6 +90,7 @@ endif()
add_library(app-lib
app.cpp
app_brushes.cpp
app_menus.cpp
app_options.cpp
app_render.cpp

View File

@ -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();

117
src/app/app_brushes.cpp Normal file
View File

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

78
src/app/app_brushes.h Normal file
View File

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

View File

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

View File

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

View File

@ -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);
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
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);
}
}
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;
const doc::Brushes& brushes = App::instance()->brushes().getCustomBrushes();
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]));
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);
++slot;
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);
// 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());
m_customBrushes->addItem(new NewCustomBrushItem, 3, 1);
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<Item*>(m_buttons->getItem(m_buttons->selectedItem()));
if (item->brush())
BrushChange(item->brush());
if (isVisible()) {
regenerate(bounds());
invalidate();
}
}
// static

View File

@ -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;
};

View File

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

View File

@ -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);

View File

@ -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,87 +1656,20 @@ 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);
}
}
AppBrushes& brushes = App::instance()->brushes();
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;
if (brushes.hasCustomBrush(slot)) {
brushes.lockCustomBrush(slot);
setActiveBrush(brushes.getCustomBrush(slot));
}
}
void ContextBar::unlockBrushSlot(int slot)
{
--slot;
if (slot >= 0 && slot < (int)m_brushes.size() &&
m_brushes[slot].brush) {
m_brushes[slot].locked = false;
else {
updateForTool(App::instance()->activeTool());
m_brushType->showPopupAndHighlightSlot(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
return false;
}
void ContextBar::setActiveBrush(const doc::BrushRef& brush)
{
m_activeBrush = brush;

View File

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