From 1b25c4e9e9baec67c62973707693de00ee6a4e6d Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 29 Apr 2015 12:32:44 -0300 Subject: [PATCH] Add slots in the ContextBar to select brushes with Alt+1, Alt+2, etc. Now the ContextBar contains a set of brushes. The ChangeBrushCommand supports a new "slot" parameter and "change" = "custom" to select a specific custom brush from the ContextBar. Alt+1, Alt+2, etc. are mapped to this ChangeBrushCommand (see changes in gui.xml). Also, as the ButtonSet that represent different brushes in the ContextBar uses icons generated from the brush, we don't need the skin parts that represent each brush type (we can generate those icons from some standard brushes). Those skin parts were removed. --- data/gui.xml | 38 +++++++ data/skins/default/sheet.png | Bin 12607 -> 12517 bytes src/app/commands/cmd_change_brush.cpp | 25 ++++- src/app/commands/cmd_new_brush.cpp | 17 +++ src/app/ui/brush_popup.cpp | 152 ++++++++++++++++++++++---- src/app/ui/brush_popup.h | 20 +++- src/app/ui/button_set.cpp | 5 + src/app/ui/button_set.h | 2 + src/app/ui/context_bar.cpp | 69 ++++++------ src/app/ui/context_bar.h | 10 ++ src/app/ui/skin/skin_parts.h | 7 -- src/app/ui/skin/skin_theme.cpp | 6 - src/doc/brushes.h | 21 ++++ src/ui/tooltips.cpp | 9 +- src/ui/tooltips.h | 4 +- 15 files changed, 309 insertions(+), 76 deletions(-) create mode 100644 src/doc/brushes.h diff --git a/data/gui.xml b/data/gui.xml index 420292921..17bc7baeb 100644 --- a/data/gui.xml +++ b/data/gui.xml @@ -149,6 +149,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png index ec2cad03030c68fbf4c226814cc63a27a75695b7..824716a46029b1939f36130df69f4c6d7de318eb 100644 GIT binary patch delta 4542 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 Ef@OL7a{vGU diff --git a/src/app/commands/cmd_change_brush.cpp b/src/app/commands/cmd_change_brush.cpp index d670900c7..fba7b302e 100644 --- a/src/app/commands/cmd_change_brush.cpp +++ b/src/app/commands/cmd_change_brush.cpp @@ -17,6 +17,9 @@ #include "app/context.h" #include "app/settings/settings.h" #include "app/tools/tool.h" +#include "app/ui/context_bar.h" +#include "app/ui/main_window.h" +#include "base/convert_to.h" #include "doc/brush.h" namespace app { @@ -28,10 +31,9 @@ class ChangeBrushCommand : public Command { DecrementSize, IncrementAngle, DecrementAngle, + CustomBrush, }; - Change m_change; - public: ChangeBrushCommand(); @@ -39,6 +41,10 @@ protected: void onLoadParams(const Params& params) override; void onExecute(Context* context) override; std::string onGetFriendlyName() const override; + +private: + Change m_change; + int m_slot; }; ChangeBrushCommand::ChangeBrushCommand() @@ -47,6 +53,7 @@ ChangeBrushCommand::ChangeBrushCommand() CmdUIOnlyFlag) { m_change = None; + m_slot = 0; } void ChangeBrushCommand::onLoadParams(const Params& params) @@ -56,6 +63,12 @@ void ChangeBrushCommand::onLoadParams(const Params& params) else if (change == "decrement-size") m_change = DecrementSize; else if (change == "increment-angle") m_change = IncrementAngle; else if (change == "decrement-angle") m_change = DecrementAngle; + else if (change == "custom") m_change = CustomBrush; + + if (m_change == CustomBrush) + m_slot = params.get_as("slot"); + else + m_slot = 0; } void ChangeBrushCommand::onExecute(Context* context) @@ -84,6 +97,10 @@ void ChangeBrushCommand::onExecute(Context* context) if (brush->getAngle() > 0) brush->setAngle(brush->getAngle()-1); break; + case CustomBrush: + App::instance()->getMainWindow()->getContextBar() + ->setActiveBrushBySlot(m_slot); + break; } } @@ -106,6 +123,10 @@ std::string ChangeBrushCommand::onGetFriendlyName() const case DecrementAngle: text += ": Decrement Angle"; break; + case CustomBrush: + text += ": Custom Brush #"; + text += base::convert_to(m_slot); + break; } return text; diff --git a/src/app/commands/cmd_new_brush.cpp b/src/app/commands/cmd_new_brush.cpp index 03d3ff46a..ab5415de6 100644 --- a/src/app/commands/cmd_new_brush.cpp +++ b/src/app/commands/cmd_new_brush.cpp @@ -22,9 +22,12 @@ #include "app/ui/context_bar.h" #include "app/ui/editor/editor.h" #include "app/ui/editor/select_box_state.h" +#include "app/ui/keyboard_shortcuts.h" #include "app/ui/main_window.h" +#include "app/ui/status_bar.h" #include "app/ui_context.h" #include "app/util/new_image_from_mask.h" +#include "base/convert_to.h" #include "doc/mask.h" namespace app { @@ -142,7 +145,21 @@ void NewBrushCommand::createBrush(const Mask* mask) // TODO add a active stock property in app::Context ContextBar* ctxBar = App::instance()->getMainWindow()->getContextBar(); + int slot = ctxBar->addBrush(brush); ctxBar->setActiveBrush(brush); + + // Get the shortcut for this brush and show it to the user + 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(); + StatusBar::instance()->showTip(2000, tooltip.c_str()); + } } Command* CommandFactory::createNewBrushCommand() diff --git a/src/app/ui/brush_popup.cpp b/src/app/ui/brush_popup.cpp index 2e278ef86..d4049a3a3 100644 --- a/src/app/ui/brush_popup.cpp +++ b/src/app/ui/brush_popup.cpp @@ -11,48 +11,158 @@ #include "app/ui/brush_popup.h" -#include "gfx/region.h" -#include "gfx/border.h" -#include "app/ui/skin/skin_theme.h" +#include "app/commands/commands.h" +#include "app/modules/palettes.h" #include "app/ui/button_set.h" +#include "app/ui/keyboard_shortcuts.h" +#include "app/ui/skin/skin_theme.h" +#include "base/convert_to.h" #include "doc/brush.h" +#include "doc/conversion_she.h" +#include "doc/image.h" +#include "doc/palette.h" +#include "gfx/border.h" +#include "gfx/region.h" +#include "she/surface.h" +#include "she/system.h" +#include "ui/tooltips.h" namespace app { using namespace app::skin; +using namespace doc; using namespace ui; +class Item : public ButtonSet::Item { +public: + Item(const BrushRef& brush) + : m_brush(brush) { + setIcon(BrushPopup::createSurfaceForBrush(brush)); + } + + ~Item() { + icon()->dispose(); + } + + const BrushRef& brush() const { return m_brush; } + +private: + BrushRef m_brush; +}; + +static BrushRef defBrushes[3]; + BrushPopup::BrushPopup() : PopupWindow("", kCloseOnClickInOtherWindow) { - SkinTheme* theme = static_cast(getTheme()); - setAutoRemap(false); setBorder(gfx::Border(0)); child_spacing = 0; - - m_brushTypeButton = new ButtonSet(3); - m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_CIRCLE)); - m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_SQUARE)); - m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_LINE)); - m_brushTypeButton->ItemChange.connect(&BrushPopup::onBrushTypeChange, this); - m_brushTypeButton->setTransparent(true); - m_brushTypeButton->setBgColor(gfx::ColorNone); - addChild(m_brushTypeButton); } -void BrushPopup::setBrush(doc::Brush* brush) +void BrushPopup::setBrush(Brush* brush) { - m_brushTypeButton->setSelectedItem(brush->type()); + for (auto child : m_buttons->getChildren()) { + Item* item = static_cast(child); + + // Same type and same image + if (item->brush()->type() == brush->type() && + (brush->type() != kImageBrushType || + item->brush()->image() == brush->image())) { + m_buttons->setSelectedItem(item); + break; + } + } } -void BrushPopup::onBrushTypeChange() +void BrushPopup::regenerate(const gfx::Rect& box, const Brushes& brushes) { - doc::BrushType brushType = (doc::BrushType)m_brushTypeButton->selectedItem(); - doc::Brush brush; - brush.setType(brushType); + SkinTheme* theme = static_cast(getTheme()); - BrushChange(&brush); + if (m_buttons) { + for (auto child : m_buttons->getChildren()) + m_tooltipManager->removeTooltipFor(child); + removeChild(m_buttons.get()); + m_buttons.reset(); + } + + if (!defBrushes[0]) { + defBrushes[0].reset(new Brush(kCircleBrushType, 7, 0)); + defBrushes[1].reset(new Brush(kSquareBrushType, 7, 0)); + defBrushes[2].reset(new Brush(kLineBrushType, 7, 44)); + } + + m_buttons.reset(new ButtonSet(3 + brushes.size())); + m_buttons->addItem(new Item(defBrushes[0])); + m_buttons->addItem(new Item(defBrushes[1])); + m_buttons->addItem(new Item(defBrushes[2])); + + int slot = 1; + for (const auto& brush : brushes) { + Item* item = new Item(brush); + 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, JI_TOP); + } + slot++; + } + + m_buttons->ItemChange.connect(&BrushPopup::onButtonChange, this); + m_buttons->setTransparent(true); + m_buttons->setBgColor(gfx::ColorNone); + addChild(m_buttons.get()); + + gfx::Rect rc = box; + rc.w *= m_buttons->getChildren().size(); + setBounds(rc); +} + +void BrushPopup::onButtonChange() +{ + Item* item = static_cast(m_buttons->getItem(m_buttons->selectedItem())); + BrushChange(item->brush()); +} + +// static +she::Surface* BrushPopup::createSurfaceForBrush(const BrushRef& origBrush) +{ + BrushRef brush = origBrush; + + if (brush->type() != kImageBrushType && brush->size() > 10) { + brush.reset(new Brush(*brush)); + brush->setSize(10); + } + + Image* image = brush->image(); + + she::Surface* surface = she::instance()->createRgbaSurface( + std::min(10, image->width()), + std::min(10, image->height())); + + Palette* palette = get_current_palette(); + if (image->pixelFormat() == IMAGE_BITMAP) { + palette = new Palette(frame_t(0), 2); + palette->setEntry(0, rgba(0, 0, 0, 0)); + palette->setEntry(1, rgba(0, 0, 0, 255)); + } + + convert_image_to_surface( + image, palette, surface, + 0, 0, 0, 0, image->width(), image->height()); + + if (image->pixelFormat() == IMAGE_BITMAP) + delete palette; + + return surface; } } // namespace app diff --git a/src/app/ui/brush_popup.h b/src/app/ui/brush_popup.h index d01ad1081..0bb0b50cd 100644 --- a/src/app/ui/brush_popup.h +++ b/src/app/ui/brush_popup.h @@ -9,13 +9,19 @@ #define APP_UI_BRUSH_POPUP_H_INCLUDED #pragma once +#include "base/shared_ptr.h" #include "base/signal.h" +#include "doc/brushes.h" #include "ui/popup_window.h" namespace doc { class Brush; } +namespace ui { + class TooltipManager; +} + namespace app { class ButtonSet; @@ -24,13 +30,21 @@ namespace app { BrushPopup(); void setBrush(doc::Brush* brush); + void regenerate(const gfx::Rect& box, const doc::Brushes& brushes); - Signal1 BrushChange; + void setupTooltips(ui::TooltipManager* tooltipManager) { + m_tooltipManager = tooltipManager; + } + + Signal1 BrushChange; + + static she::Surface* createSurfaceForBrush(const doc::BrushRef& brush); private: - void onBrushTypeChange(); + void onButtonChange(); - ButtonSet* m_brushTypeButton; + base::SharedPtr m_buttons; + ui::TooltipManager* m_tooltipManager; }; } // namespace app diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp index 4002f2af5..52deedc10 100644 --- a/src/app/ui/button_set.cpp +++ b/src/app/ui/button_set.cpp @@ -162,6 +162,11 @@ void ButtonSet::addItem(she::Surface* icon, int hspan, int vspan) { Item* item = new Item(); item->setIcon(icon); + addItem(item, hspan, vspan); +} + +void ButtonSet::addItem(Item* item, int hspan, int vspan) +{ addChildInCell(item, hspan, vspan, JI_CENTER | JI_MIDDLE); } diff --git a/src/app/ui/button_set.h b/src/app/ui/button_set.h index 0f64d6f03..fedc4d787 100644 --- a/src/app/ui/button_set.h +++ b/src/app/ui/button_set.h @@ -22,6 +22,7 @@ namespace app { public: Item(); void setIcon(she::Surface* icon); + she::Surface* icon() const { return m_icon; } ButtonSet* buttonSet(); protected: void onPaint(ui::PaintEvent& ev) override; @@ -34,6 +35,7 @@ namespace app { ButtonSet(int columns); void addItem(she::Surface* icon, int hspan = 1, int vspan = 1); + void addItem(Item* item, int hspan = 1, int vspan = 1); Item* getItem(int index); int selectedItem() const; diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 9cb7aa412..010e566f5 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -78,35 +78,19 @@ public: } void updateBrush(tools::Tool* tool = nullptr) { - doc::BrushRef brush = m_owner->activeBrush(tool); - if (brush->type() != kImageBrushType && brush->size() > 10) { - brush.reset(new Brush(*brush)); - brush->setSize(10); - } - - Image* image = brush->image(); if (m_bitmap) m_bitmap->dispose(); - m_bitmap = she::instance()->createRgbaSurface( - std::min(10, image->width()), - std::min(10, image->height())); - Palette* palette = get_current_palette(); - if (image->pixelFormat() == IMAGE_BITMAP) { - palette = new Palette(frame_t(0), 2); - palette->setEntry(0, doc::rgba(0, 0, 0, 0)); - palette->setEntry(1, doc::rgba(0, 0, 0, 255)); - } - - convert_image_to_surface(image, palette, m_bitmap, - 0, 0, 0, 0, image->width(), image->height()); - - if (image->pixelFormat() == IMAGE_BITMAP) - delete palette; + m_bitmap = BrushPopup::createSurfaceForBrush( + m_owner->activeBrush(tool)); getItem(0)->setIcon(m_bitmap); } + void setupTooltips(TooltipManager* tooltipManager) { + m_popupWindow.setupTooltips(tooltipManager); + } + protected: void onItemChange() override { ButtonSet::onItemChange(); @@ -126,42 +110,42 @@ private: Rect rc = getBounds(); rc.y += rc.h - 2*guiscale(); rc.setSize(getPreferredSize()); - rc.w *= 3; ISettings* settings = UIContext::instance()->settings(); Tool* currentTool = settings->getCurrentTool(); IBrushSettings* brushSettings = settings->getToolSettings(currentTool)->getBrush(); doc::BrushRef brush = m_owner->activeBrush(); - m_popupWindow.setBounds(rc); + m_popupWindow.regenerate(rc, m_owner->brushes()); m_popupWindow.setBrush(brush.get()); Region rgn(m_popupWindow.getBounds().createUnion(getBounds())); m_popupWindow.setHotRegion(rgn); m_popupWindow.openWindow(); - m_popupWindow.BrushChange.connect(&BrushTypeField::onBrushTypeChange, this); + m_popupWindow.BrushChange.connect(&BrushTypeField::onBrushChange, this); } void closePopup() { m_popupWindow.closeWindow(NULL); } - void onBrushTypeChange(Brush* brush) { - m_brushType = brush->type(); + void onBrushChange(const BrushRef& brush) { + if (brush->type() == kImageBrushType) + m_owner->setActiveBrush(brush); + else { + ISettings* settings = UIContext::instance()->settings(); + Tool* currentTool = settings->getCurrentTool(); + IBrushSettings* brushSettings = settings->getToolSettings(currentTool)->getBrush(); + brushSettings->setType(brush->type()); - ISettings* settings = UIContext::instance()->settings(); - Tool* currentTool = settings->getCurrentTool(); - IBrushSettings* brushSettings = settings->getToolSettings(currentTool)->getBrush(); - brushSettings->setType(m_brushType); - - m_owner->setActiveBrush(ContextBar::createBrushFromSettings( - brushSettings)); + m_owner->setActiveBrush( + ContextBar::createBrushFromSettings(brushSettings)); + } } ContextBar* m_owner; she::Surface* m_bitmap; - BrushType m_brushType; BrushPopup m_popupWindow; }; @@ -840,6 +824,8 @@ ContextBar::ContextBar() "component is used to setup the opacity level of all drawing tools.\n\n" "When unchecked -the default behavior- the color is picked\n" "from the composition of all sprite layers.", JI_LEFT | JI_TOP); + + m_brushType->setupTooltips(tooltipManager); m_selectionMode->setupTooltips(tooltipManager); m_dropPixels->setupTooltips(tooltipManager); m_freehandAlgo->setupTooltips(tooltipManager); @@ -1021,6 +1007,19 @@ void ContextBar::updateAutoSelectLayer(bool state) m_autoSelectLayer->setSelected(state); } +int ContextBar::addBrush(const doc::BrushRef& brush) +{ + m_brushes.push_back(brush); + return (int)m_brushes.size(); // Returns the slot +} + +void ContextBar::setActiveBrushBySlot(int slot) +{ + --slot; + if (slot >= 0 && slot < (int)m_brushes.size()) + setActiveBrush(m_brushes[slot]); +} + 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 6661131fc..682da2880 100644 --- a/src/app/ui/context_bar.h +++ b/src/app/ui/context_bar.h @@ -13,8 +13,11 @@ #include "app/ui/context_bar_observer.h" #include "base/observable.h" #include "doc/brush.h" +#include "doc/brushes.h" #include "ui/box.h" +#include + namespace ui { class Box; class Button; @@ -46,6 +49,12 @@ namespace app { 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); + const doc::Brushes& brushes() const { return m_brushes; } + void setActiveBrushBySlot(int slot); + static doc::BrushRef createBrushFromSettings( IBrushSettings* brushSettings = nullptr); @@ -104,6 +113,7 @@ namespace app { RotAlgorithmField* m_rotAlgo; DropPixelsField* m_dropPixels; doc::BrushRef m_activeBrush; + doc::Brushes m_brushes; }; } // namespace app diff --git a/src/app/ui/skin/skin_parts.h b/src/app/ui/skin/skin_parts.h index 5287e6898..b111a947f 100644 --- a/src/app/ui/skin/skin_parts.h +++ b/src/app/ui/skin/skin_parts.h @@ -129,13 +129,6 @@ namespace app { PART_TARGET_FRAMES_LAYERS, PART_TARGET_FRAMES_LAYERS_SELECTED, - PART_BRUSH_CIRCLE, - PART_BRUSH_CIRCLE_SELECTED, - PART_BRUSH_SQUARE, - PART_BRUSH_SQUARE_SELECTED, - PART_BRUSH_LINE, - PART_BRUSH_LINE_SELECTED, - PART_SCALE_ARROW_1, PART_SCALE_ARROW_2, PART_SCALE_ARROW_3, diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index 9e72cb6fe..46c7be9a4 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -238,12 +238,6 @@ SkinTheme::SkinTheme() sheet_mapping["target_layers_selected"] = PART_TARGET_LAYERS_SELECTED; sheet_mapping["target_frames_layers"] = PART_TARGET_FRAMES_LAYERS; sheet_mapping["target_frames_layers_selected"] = PART_TARGET_FRAMES_LAYERS_SELECTED; - sheet_mapping["brush_circle"] = PART_BRUSH_CIRCLE; - sheet_mapping["brush_circle_selected"] = PART_BRUSH_CIRCLE_SELECTED; - sheet_mapping["brush_square"] = PART_BRUSH_SQUARE; - sheet_mapping["brush_square_selected"] = PART_BRUSH_SQUARE_SELECTED; - sheet_mapping["brush_line"] = PART_BRUSH_LINE; - sheet_mapping["brush_line_selected"] = PART_BRUSH_LINE_SELECTED; sheet_mapping["scale_arrow_1"] = PART_SCALE_ARROW_1; sheet_mapping["scale_arrow_2"] = PART_SCALE_ARROW_2; sheet_mapping["scale_arrow_3"] = PART_SCALE_ARROW_3; diff --git a/src/doc/brushes.h b/src/doc/brushes.h new file mode 100644 index 000000000..29238befb --- /dev/null +++ b/src/doc/brushes.h @@ -0,0 +1,21 @@ +// Aseprite Document Library +// Copyright (c) 2001-2015 David Capello +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef DOC_BRUSHES_H_INCLUDED +#define DOC_BRUSHES_H_INCLUDED +#pragma once + +#include "doc/brush.h" + +#include + +namespace doc { + + typedef std::vector Brushes; + +} // namespace doc + +#endif diff --git a/src/ui/tooltips.cpp b/src/ui/tooltips.cpp index 4cef8cb80..e2b689dff 100644 --- a/src/ui/tooltips.cpp +++ b/src/ui/tooltips.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2013 David Capello +// Copyright (C) 2001-2013, 2015 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -47,6 +47,13 @@ void TooltipManager::addTooltipFor(Widget* widget, const std::string& text, int m_tips[widget] = TipInfo(text, arrowAlign); } +void TooltipManager::removeTooltipFor(Widget* widget) +{ + auto it = m_tips.find(widget); + if (it != m_tips.end()) + m_tips.erase(it); +} + bool TooltipManager::onProcessMessage(Message* msg) { switch (msg->type()) { diff --git a/src/ui/tooltips.h b/src/ui/tooltips.h index 8bad58b2d..4b3c5f514 100644 --- a/src/ui/tooltips.h +++ b/src/ui/tooltips.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2013 David Capello +// Copyright (C) 2001-2013, 2015 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -8,6 +8,7 @@ #define UI_TOOLTIPS_H_INCLUDED #pragma once +#include "base/unique_ptr.h" #include "ui/base.h" #include "ui/popup_window.h" #include "ui/window.h" @@ -25,6 +26,7 @@ namespace ui { ~TooltipManager(); void addTooltipFor(Widget* widget, const std::string& text, int arrowAlign = 0); + void removeTooltipFor(Widget* widget); protected: bool onProcessMessage(Message* msg) override;