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 ec2cad030..824716a46 100644
Binary files a/data/skins/default/sheet.png and b/data/skins/default/sheet.png differ
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;