From 2d64a1926e6119d6c38a7d12fdc2fb6bed82b640 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 15 Dec 2015 17:12:11 -0300 Subject: [PATCH 01/10] 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 | 252 +++++++++++++------- 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, 478 insertions(+), 238 deletions(-) create mode 100644 src/app/app_brushes.cpp create mode 100644 src/app/app_brushes.h 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..a3a2addfb --- /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= 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 000000000..12662ed20 --- /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 + +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 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 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 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..d082d0ae1 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 238f06075..42d7e3c64 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(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(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(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() && (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(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, 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(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 ccd1c2715..00f2cad6f 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 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..60d727785 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(msg)->left() && !buttonSet()->m_triggerOnMouseUp) { - buttonSet()->onItemChange(this); + onClick(); } break; @@ -183,10 +189,10 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg) if (static_cast(msg)->left()) { if (buttonSet()->m_triggerOnMouseUp) - buttonSet()->onItemChange(this); + onClick(); } else if (static_cast(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 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..56807f64f 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(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(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(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= 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; diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h index 097cb166c..0d30e4501 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 @@ -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 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; From 6ff62c899e6acd5a898440e308e2a5d566006916 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 15 Dec 2015 17:58:55 -0300 Subject: [PATCH 02/10] Add "Save Brush Here" option to brush slot popup --- src/app/app_brushes.cpp | 9 +++++++++ src/app/app_brushes.h | 1 + src/app/ui/brush_popup.cpp | 11 +++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/app/app_brushes.cpp b/src/app/app_brushes.cpp index a3a2addfb..0cdba53bf 100644 --- a/src/app/app_brushes.cpp +++ b/src/app/app_brushes.cpp @@ -77,6 +77,15 @@ BrushRef AppBrushes::getCustomBrush(slot_id slot) const return BrushRef(); } +void AppBrushes::setCustomBrush(slot_id slot, const doc::BrushRef& brush) +{ + --slot; + if (slot >= 0 && slot < (int)m_slots.size()) { + m_slots[slot].setBrush(brush); + ItemsChange(); + } +} + Brushes AppBrushes::getCustomBrushes() { Brushes brushes; diff --git a/src/app/app_brushes.h b/src/app/app_brushes.h index 12662ed20..aa011035d 100644 --- a/src/app/app_brushes.h +++ b/src/app/app_brushes.h @@ -32,6 +32,7 @@ namespace app { bool hasCustomBrush(slot_id slot) const; const doc::Brushes& getStandardBrushes() { return m_standard; } doc::BrushRef getCustomBrush(slot_id slot) const; + void setCustomBrush(slot_id slot, const doc::BrushRef& brush); doc::Brushes getCustomBrushes(); void lockCustomBrush(slot_id slot); diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index 42d7e3c64..fa2c8f5c0 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -121,12 +121,16 @@ private: void onClick() override { Menu menu; + AppMenuItem save("Save Brush Here"); AppMenuItem lockItem(m_delegate->onIsBrushSlotLocked(m_slot) ? "Unlock Brush": "Lock Brush"); AppMenuItem deleteItem("Delete"); AppMenuItem deleteAllItem("Delete All"); + 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); @@ -142,6 +146,13 @@ private: private: + void onSaveBrush() { + AppBrushes& brushes = App::instance()->brushes(); + brushes.setCustomBrush( + m_slot, ContextBar::createBrushFromPreferences()); + brushes.lockCustomBrush(m_slot); + } + void onLockBrush() { if (m_delegate->onIsBrushSlotLocked(m_slot)) m_delegate->onUnlockBrushSlot(m_slot); From 53b8eb7c4d06c97be282f51881d07000b36e90c0 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 15 Dec 2015 18:17:21 -0300 Subject: [PATCH 03/10] Don't use ContextBar from BrushPopup --- src/app/ui/brush_popup.cpp | 10 ++++++---- src/app/ui/brush_popup.h | 1 + src/app/ui/context_bar.cpp | 4 ++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index fa2c8f5c0..c27ca0474 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -19,7 +19,6 @@ #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" @@ -149,7 +148,7 @@ private: void onSaveBrush() { AppBrushes& brushes = App::instance()->brushes(); brushes.setCustomBrush( - m_slot, ContextBar::createBrushFromPreferences()); + m_slot, m_delegate->onCreateBrushFromActivePreferences()); brushes.lockCustomBrush(m_slot); } @@ -176,7 +175,8 @@ private: class NewCustomBrushItem : public ButtonSet::Item { public: - NewCustomBrushItem() { + NewCustomBrushItem(BrushPopupDelegate* delegate) + : m_delegate(delegate) { setText("Save Brush"); } @@ -184,9 +184,11 @@ private: void onClick() override { AppBrushes& brushes = App::instance()->brushes(); auto slot = brushes.addCustomBrush( - ContextBar::createBrushFromPreferences()); + m_delegate->onCreateBrushFromActivePreferences()); brushes.lockCustomBrush(slot); } + + BrushPopupDelegate* m_delegate; }; } // anonymous namespace diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h index 00f2cad6f..f7a2356a3 100644 --- a/src/app/ui/brush_popup.h +++ b/src/app/ui/brush_popup.h @@ -21,6 +21,7 @@ namespace app { class BrushPopupDelegate { public: virtual ~BrushPopupDelegate() { } + virtual doc::BrushRef onCreateBrushFromActivePreferences() = 0; virtual void onSelectBrush(const doc::BrushRef& brush) = 0; virtual void onSelectBrushSlot(int slot) = 0; virtual void onDeleteBrushSlot(int slot) = 0; diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 56807f64f..044bd56fa 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -123,6 +123,10 @@ protected: } // BrushPopupDelegate impl + BrushRef onCreateBrushFromActivePreferences() { + return ContextBar::createBrushFromPreferences(); + } + void onSelectBrush(const BrushRef& brush) { if (brush->type() == kImageBrushType) m_owner->setActiveBrush(brush); From 3024bc76042431c71765d197bff5d17be066f408 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 15 Dec 2015 18:17:42 -0300 Subject: [PATCH 04/10] Fix redrawing issue when we "delete all brushes" --- src/app/ui/brush_popup.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index c27ca0474..4f205c719 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -289,8 +289,13 @@ void BrushPopup::regenerate(const gfx::Rect& box) void BrushPopup::onBrushChanges() { if (isVisible()) { + gfx::Region rgn; + getDrawableRegion(rgn, DrawableRegionFlags(kCutTopWindows | kUseChildArea)); + regenerate(bounds()); invalidate(); + + parent()->invalidateRegion(rgn); } } From fd8e8686d59e55419ef24ae14452e5723f69f2e5 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 15 Dec 2015 19:29:12 -0300 Subject: [PATCH 05/10] Fix NewCustomBrushItem construction --- src/app/ui/brush_popup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index 4f205c719..a333d8d2d 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -277,7 +277,7 @@ void BrushPopup::regenerate(const gfx::Rect& box) m_customBrushes->addItem(new BrushOptionsItem(this, m_delegate, slot)); } - m_customBrushes->addItem(new NewCustomBrushItem, 3, 1); + m_customBrushes->addItem(new NewCustomBrushItem(m_delegate), 3, 1); m_customBrushes->setExpansive(true); m_box.addChild(m_customBrushes); From bd99a301469fcc9856085c953526d0fab331e33a Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 16 Dec 2015 10:02:52 -0300 Subject: [PATCH 06/10] Avoid closing BrushPopup when we click the menu popup option --- src/app/ui/brush_popup.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index a333d8d2d..bcd2fa2a6 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -138,9 +138,17 @@ private: // 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)); + + // Add the menu popup region to the hot region so the BrushPopup (m_popup) + // isn't closed after we click the menu popup. m_popup->makeFixed(); - m_popup->closeWindow(nullptr); + + gfx::Region rgn; + rgn.createUnion(gfx::Region(m_popup->bounds()), + gfx::Region(menu.bounds())); + m_popup->setHotRegion(rgn); } private: From 0f35102a4bd68b61f0ab96ee270813f567c28bc9 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 16 Dec 2015 10:49:15 -0300 Subject: [PATCH 07/10] Add show_popup_menu() internal function in brush_popup.cpp --- src/app/ui/brush_popup.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index bcd2fa2a6..3336751f6 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -48,6 +48,25 @@ using namespace ui; namespace { +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: SelectBrushItem(BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1) @@ -135,20 +154,8 @@ private: 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)); - - // Add the menu popup region to the hot region so the BrushPopup (m_popup) - // isn't closed after we click the menu popup. - m_popup->makeFixed(); - - gfx::Region rgn; - rgn.createUnion(gfx::Region(m_popup->bounds()), - gfx::Region(menu.bounds())); - m_popup->setHotRegion(rgn); + show_popup_menu(m_popup, &menu, + gfx::Point(origin().x, origin().y+bounds().h)); } private: From 1f513a1ed5ff610c90f349eaec4efa55ce5de9e9 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 16 Dec 2015 16:55:49 -0300 Subject: [PATCH 08/10] Restore selected ButtonSet item when we offer the mouse capture When a ButtonSet triggers an onClick() when a mouse up message is received (ButtonSet::m_multipleSelection == true), in case the mouse capture is offered to other ButtonSet, we've to restore the selected item when we capture the mouse, because we've never generated the onClick() event in the first place. --- src/app/ui/brush_popup.cpp | 1 + src/app/ui/button_set.cpp | 31 +++++++++++++++++++++++++++++-- src/ui/widget.cpp | 4 +++- src/ui/widget.h | 5 +++-- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index 3336751f6..3eec4aa31 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -219,6 +219,7 @@ BrushPopup::BrushPopup(BrushPopupDelegate* delegate) setBorder(gfx::Border(2)*guiscale()); setChildSpacing(0); m_box.noBorderNoChildSpacing(); + m_standardBrushes.setTriggerOnMouseUp(true); addChild(&m_box); diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp index 60d727785..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; @@ -172,6 +176,15 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg) 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(); @@ -184,6 +197,9 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg) case ui::kMouseUpMessage: if (hasCapture()) { + if (g_itemBeforeCapture >= 0) + g_itemBeforeCapture = -1; + releaseMouse(); invalidate(); @@ -199,8 +215,19 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg) 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; 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). From 0cdc71bf05b47af0f66447082b31422b462909c5 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 16 Dec 2015 17:27:04 -0300 Subject: [PATCH 09/10] Add possibility to save brush type/size/angle, fg/bg colors, ink type/opacity, shade, etc. into brush slots Fix #904 --- data/pref.xml | 11 +++ src/app/app_brushes.cpp | 44 ++++------ src/app/app_brushes.h | 51 +++-------- src/app/brush_slot.h | 104 +++++++++++++++++++++++ src/app/commands/cmd_new_brush.cpp | 3 +- src/app/ui/brush_popup.cpp | 130 ++++++++++++++++++++++++----- src/app/ui/brush_popup.h | 4 +- src/app/ui/context_bar.cpp | 118 ++++++++++++++++++++------ src/app/ui/context_bar.h | 2 + 9 files changed, 353 insertions(+), 114 deletions(-) create mode 100644 src/app/brush_slot.h 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 @@
+
+