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.
This commit is contained in:
David Capello 2015-04-29 12:32:44 -03:00
parent b08b226aef
commit 1b25c4e9e9
15 changed files with 309 additions and 76 deletions

View File

@ -149,6 +149,44 @@
<param name="change" value="decrement-size" />
</key>
<!-- Custom brushes -->
<key command="ChangeBrush" shortcut="Alt+1">
<param name="change" value="custom" />
<param name="slot" value="1" />
</key>
<key command="ChangeBrush" shortcut="Alt+2">
<param name="change" value="custom" />
<param name="slot" value="2" />
</key>
<key command="ChangeBrush" shortcut="Alt+3">
<param name="change" value="custom" />
<param name="slot" value="3" />
</key>
<key command="ChangeBrush" shortcut="Alt+4">
<param name="change" value="custom" />
<param name="slot" value="4" />
</key>
<key command="ChangeBrush" shortcut="Alt+5">
<param name="change" value="custom" />
<param name="slot" value="5" />
</key>
<key command="ChangeBrush" shortcut="Alt+6">
<param name="change" value="custom" />
<param name="slot" value="6" />
</key>
<key command="ChangeBrush" shortcut="Alt+7">
<param name="change" value="custom" />
<param name="slot" value="7" />
</key>
<key command="ChangeBrush" shortcut="Alt+8">
<param name="change" value="custom" />
<param name="slot" value="8" />
</key>
<key command="ChangeBrush" shortcut="Alt+9">
<param name="change" value="custom" />
<param name="slot" value="9" />
</key>
<!-- Zoom -->
<key command="Zoom" shortcut="~"><param name="percentage" value="50" /></key>
<key command="Zoom" shortcut="1"><param name="percentage" value="100" /></key>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -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<int>("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<std::string>(m_slot);
break;
}
return text;

View File

@ -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<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();
StatusBar::instance()->showTip(2000, tooltip.c_str());
}
}
Command* CommandFactory::createNewBrushCommand()

View File

@ -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<SkinTheme*>(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<Item*>(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<SkinTheme*>(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<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, 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<Item*>(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

View File

@ -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<void, doc::Brush*> BrushChange;
void setupTooltips(ui::TooltipManager* tooltipManager) {
m_tooltipManager = tooltipManager;
}
Signal1<void, const doc::BrushRef&> BrushChange;
static she::Surface* createSurfaceForBrush(const doc::BrushRef& brush);
private:
void onBrushTypeChange();
void onButtonChange();
ButtonSet* m_brushTypeButton;
base::SharedPtr<ButtonSet> m_buttons;
ui::TooltipManager* m_tooltipManager;
};
} // namespace app

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

21
src/doc/brushes.h Normal file
View File

@ -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 <vector>
namespace doc {
typedef std::vector<BrushRef> Brushes;
} // namespace doc
#endif

View File

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

View File

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