aseprite/src/app/commands/cmd_new_brush.cpp

195 lines
5.4 KiB
C++

// Aseprite
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/app.h"
#include "app/cmd/clear_rect.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/console.h"
#include "app/context_access.h"
#include "app/modules/editors.h"
#include "app/tools/active_tool.h"
#include "app/tools/ink.h"
#include "app/tools/tool_box.h"
#include "app/transaction.h"
#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/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 {
class NewBrushCommand : public Command
, public SelectBoxDelegate {
public:
NewBrushCommand();
Command* clone() const override { return new NewBrushCommand(*this); }
protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
// SelectBoxDelegate impl
void onQuickboxEnd(Editor* editor, const gfx::Rect& rect, ui::MouseButtons buttons) override;
void onQuickboxCancel(Editor* editor) override;
std::string onGetContextBarHelp() override {
return "Select brush bounds | Right-click to cut";
}
private:
void createBrush(const Site& site, const Mask* mask);
void selectPencilTool();
};
NewBrushCommand::NewBrushCommand()
: Command("NewBrush",
"New Brush",
CmdUIOnlyFlag)
{
}
bool NewBrushCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
void NewBrushCommand::onExecute(Context* context)
{
ASSERT(current_editor);
if (!current_editor)
return;
// If there is no visible mask, the brush must be selected from the
// current editor.
if (!context->activeDocument()->isMaskVisible()) {
EditorStatePtr state = current_editor->getState();
if (dynamic_cast<SelectBoxState*>(state.get())) {
// If already are in "SelectBoxState" state, in this way we
// avoid creating a stack of several "SelectBoxState" states.
return;
}
current_editor->setState(
EditorStatePtr(
new SelectBoxState(
this, current_editor->sprite()->bounds(),
SelectBoxState::Flags(
int(SelectBoxState::Flags::DarkOutside) |
int(SelectBoxState::Flags::QuickBox)))));
}
// Create a brush from the active selection
else {
createBrush(context->activeSite(),
context->activeDocument()->mask());
selectPencilTool();
// Deselect mask
Command* cmd =
CommandsModule::instance()->getCommandByName(CommandId::DeselectMask);
UIContext::instance()->executeCommand(cmd);
}
}
void NewBrushCommand::onQuickboxEnd(Editor* editor, const gfx::Rect& rect, ui::MouseButtons buttons)
{
Mask mask;
mask.replace(rect);
createBrush(editor->getSite(), &mask);
selectPencilTool();
// If the right-button was used, we clear the selected area.
if (buttons & ui::kButtonRight) {
try {
ContextWriter writer(UIContext::instance(), 250);
if (writer.cel()) {
Transaction transaction(writer.context(), "Clear");
transaction.execute(new cmd::ClearRect(writer.cel(), rect));
transaction.commit();
}
}
catch (const std::exception& ex) {
Console::showException(ex);
}
}
// Update the context bar
// TODO find a way to avoid all these singletons. Maybe a simple
// signal in the context like "brush has changed" could be enough.
App::instance()->contextBar()->updateForActiveTool();
editor->backToPreviousState();
}
void NewBrushCommand::onQuickboxCancel(Editor* editor)
{
editor->backToPreviousState();
}
void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
{
doc::ImageRef image(new_image_from_mask(site, mask));
if (!image)
return;
// New brush
doc::BrushRef brush(new doc::Brush());
brush->setImage(image.get(), mask->bitmap());
brush->setPatternOrigin(mask->bounds().origin());
ContextBar* ctxBar = App::instance()->contextBar();
int flags = int(BrushSlot::Flags::BrushType);
{
// TODO merge this code with ContextBar::createBrushSlotFromPreferences()?
auto& pref = Preferences::instance();
auto& saveBrush = pref.saveBrush;
if (saveBrush.imageColor())
flags |= int(BrushSlot::Flags::ImageColor);
}
int slot = App::instance()->brushes().addBrushSlot(
BrushSlot(BrushSlot::Flags(flags), 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());
}
}
void NewBrushCommand::selectPencilTool()
{
App* app = App::instance();
if (app->activeToolManager()->selectedTool()->getInk(0)->isSelection()) {
app->activeToolManager()->setSelectedTool(
app->toolBox()->getToolById(tools::WellKnownTools::Pencil));
}
}
Command* CommandFactory::createNewBrushCommand()
{
return new NewBrushCommand();
}
} // namespace app