mirror of
https://github.com/aseprite/aseprite.git
synced 2024-09-02 04:59:24 +00:00
Now we can create brushes selecting the box directly (fix #154)
This commit is contained in:
parent
215f594273
commit
d9fb81e5d1
@ -439,6 +439,7 @@
|
||||
<param name="orientation" value="vertical" />
|
||||
</item>
|
||||
<item command="MaskContent" text="Transfor&m" />
|
||||
<item command="NewBrush" text="New &Brush" />
|
||||
<separator />
|
||||
<item command="ReplaceColor" text="R&eplace Color..." />
|
||||
<item command="InvertColor" text="&Invert" />
|
||||
|
@ -92,7 +92,7 @@ public:
|
||||
protected:
|
||||
|
||||
// SelectBoxDelegate impleentation
|
||||
virtual void onChangeRectangle(const gfx::Rect& rect) override {
|
||||
void onChangeRectangle(const gfx::Rect& rect) override {
|
||||
m_rect = rect;
|
||||
|
||||
updateSizeFromRect();
|
||||
|
@ -13,9 +13,12 @@
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/context_bar.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/editor/select_box_state.h"
|
||||
#include "app/ui/editor/tool_loop_impl.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui_context.h"
|
||||
@ -24,7 +27,8 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
class NewBrushCommand : public Command {
|
||||
class NewBrushCommand : public Command
|
||||
, public SelectBoxDelegate {
|
||||
public:
|
||||
NewBrushCommand();
|
||||
Command* clone() const override { return new NewBrushCommand(*this); }
|
||||
@ -32,6 +36,12 @@ public:
|
||||
protected:
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
|
||||
// SelectBoxDelegate impl
|
||||
void onQuickboxEnd(const gfx::Rect& rect) override;
|
||||
|
||||
private:
|
||||
void createBrush(const Mask* mask);
|
||||
};
|
||||
|
||||
NewBrushCommand::NewBrushCommand()
|
||||
@ -43,34 +53,64 @@ NewBrushCommand::NewBrushCommand()
|
||||
|
||||
bool NewBrushCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
||||
ContextFlags::HasVisibleMask);
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
|
||||
}
|
||||
|
||||
void NewBrushCommand::onExecute(Context* context)
|
||||
{
|
||||
// TODO if there is no mask available, create a new Editor state to select the brush bounds
|
||||
|
||||
doc::ImageRef image(new_image_from_mask(UIContext::instance()->activeSite()));
|
||||
if (!image) {
|
||||
ui::Alert::show("Error<<There is no selected area to create a brush.||&OK");
|
||||
return;
|
||||
// If there is no visible mask, the brush must be selected from the
|
||||
// current editor.
|
||||
if (!context->activeDocument()->isMaskVisible()) {
|
||||
current_editor->setState(
|
||||
EditorStatePtr(
|
||||
new SelectBoxState(
|
||||
this, current_editor->sprite()->bounds(),
|
||||
SelectBoxState::DARKOUTSIDE |
|
||||
SelectBoxState::QUICKBOX)));
|
||||
}
|
||||
// Create a brush from the active selection
|
||||
else {
|
||||
createBrush(context->activeDocument()->mask());
|
||||
|
||||
// Set pencil as current tool
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
tools::Tool* pencil =
|
||||
App::instance()->getToolBox()->getToolById(tools::WellKnownTools::Pencil);
|
||||
settings->setCurrentTool(pencil);
|
||||
|
||||
// Deselect mask
|
||||
Command* cmd =
|
||||
CommandsModule::instance()->getCommandByName(CommandId::DeselectMask);
|
||||
UIContext::instance()->executeCommand(cmd);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NewBrushCommand::onQuickboxEnd(const gfx::Rect& rect)
|
||||
{
|
||||
Mask mask;
|
||||
mask.replace(rect);
|
||||
createBrush(&mask);
|
||||
|
||||
// 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()->getMainWindow()->getContextBar()
|
||||
->updateFromTool(UIContext::instance()->settings()->getCurrentTool());
|
||||
|
||||
current_editor->backToPreviousState();
|
||||
}
|
||||
|
||||
void NewBrushCommand::createBrush(const Mask* mask)
|
||||
{
|
||||
doc::ImageRef image(new_image_from_mask(
|
||||
UIContext::instance()->activeSite(), mask));
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
// Set brush
|
||||
set_tool_loop_brush_image(
|
||||
image, context->activeDocument()->mask()->bounds().getOrigin());
|
||||
|
||||
// Set pencil as current tool
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
tools::Tool* pencil =
|
||||
App::instance()->getToolBox()->getToolById(tools::WellKnownTools::Pencil);
|
||||
settings->setCurrentTool(pencil);
|
||||
|
||||
// Deselect mask
|
||||
Command* cmd =
|
||||
CommandsModule::instance()->getCommandByName(CommandId::DeselectMask);
|
||||
UIContext::instance()->executeCommand(cmd);
|
||||
image.get(), mask->bounds().getOrigin());
|
||||
}
|
||||
|
||||
Command* CommandFactory::createNewBrushCommand()
|
||||
|
@ -859,9 +859,13 @@ tools::Tool* Editor::getCurrentEditorTool()
|
||||
|
||||
tools::Ink* Editor::getCurrentEditorInk()
|
||||
{
|
||||
tools::Ink* ink = m_state->getStateInk();
|
||||
if (ink)
|
||||
return ink;
|
||||
|
||||
Context* context = UIContext::instance();
|
||||
tools::Tool* tool = getCurrentEditorTool();
|
||||
tools::Ink* ink = tool->getInk(m_secondaryButton ? 1: 0);
|
||||
ink = tool->getInk(m_secondaryButton ? 1: 0);
|
||||
|
||||
if (m_quicktool)
|
||||
return ink;
|
||||
|
@ -26,6 +26,7 @@ namespace app {
|
||||
class EditorDecorator;
|
||||
|
||||
namespace tools {
|
||||
class Ink;
|
||||
class Tool;
|
||||
}
|
||||
|
||||
@ -107,6 +108,9 @@ namespace app {
|
||||
// Returns true if this state accept the given quicktool.
|
||||
virtual bool acceptQuickTool(tools::Tool* tool) { return true; }
|
||||
|
||||
// Custom ink in this state.
|
||||
virtual tools::Ink* getStateInk() { return nullptr; }
|
||||
|
||||
private:
|
||||
DISABLE_COPYING(EditorState);
|
||||
};
|
||||
|
@ -11,10 +11,12 @@
|
||||
|
||||
#include "app/ui/editor/select_box_state.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/system.h"
|
||||
#include "ui/view.h"
|
||||
@ -27,6 +29,7 @@ SelectBoxState::SelectBoxState(SelectBoxDelegate* delegate, const gfx::Rect& rc,
|
||||
: m_delegate(delegate)
|
||||
, m_rulers(4)
|
||||
, m_movingRuler(-1)
|
||||
, m_selectingBox(false)
|
||||
, m_flags(flags)
|
||||
{
|
||||
setBoxBounds(rc);
|
||||
@ -66,29 +69,45 @@ bool SelectBoxState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
if (msg->left() || msg->right()) {
|
||||
m_movingRuler = -1;
|
||||
|
||||
for (int i=0; i<(int)m_rulers.size(); ++i) {
|
||||
if (touchRuler(editor, m_rulers[i], msg->position().x, msg->position().y)) {
|
||||
m_movingRuler = i;
|
||||
break;
|
||||
if (hasFlag(RULERS)) {
|
||||
for (int i=0; i<(int)m_rulers.size(); ++i) {
|
||||
if (touchRuler(editor, m_rulers[i], msg->position().x, msg->position().y)) {
|
||||
m_movingRuler = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFlag(QUICKBOX) && m_movingRuler == -1) {
|
||||
m_selectingBox = true;
|
||||
m_startingPos = editor->screenToEditor(msg->position());
|
||||
setBoxBounds(gfx::Rect(m_startingPos, gfx::Size(1, 1)));
|
||||
}
|
||||
|
||||
editor->captureMouse();
|
||||
return true;
|
||||
}
|
||||
|
||||
return StandbyState::onMouseDown(editor, msg);
|
||||
}
|
||||
|
||||
bool SelectBoxState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
m_movingRuler = -1;
|
||||
|
||||
if (m_selectingBox) {
|
||||
m_selectingBox = false;
|
||||
if (m_delegate)
|
||||
m_delegate->onQuickboxEnd(getBoxBounds());
|
||||
}
|
||||
|
||||
return StandbyState::onMouseUp(editor, msg);
|
||||
}
|
||||
|
||||
bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
if (m_movingRuler >= 0) {
|
||||
bool used = false;
|
||||
|
||||
if (hasFlag(RULERS) && m_movingRuler >= 0) {
|
||||
gfx::Point pt = editor->screenToEditor(msg->position());
|
||||
|
||||
switch (m_rulers[m_movingRuler].getOrientation()) {
|
||||
@ -101,44 +120,81 @@ bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
m_rulers[m_movingRuler].setPosition(pt.x);
|
||||
break;
|
||||
}
|
||||
used = true;
|
||||
}
|
||||
|
||||
if (hasFlag(QUICKBOX) && m_selectingBox) {
|
||||
gfx::Point p1 = m_startingPos;
|
||||
gfx::Point p2 = editor->screenToEditor(msg->position());
|
||||
|
||||
if (p2.x < p1.x) std::swap(p1.x, p2.x);
|
||||
if (p2.y < p1.y) std::swap(p1.y, p2.y);
|
||||
++p2.x;
|
||||
++p2.y;
|
||||
|
||||
setBoxBounds(gfx::Rect(p1, p2));
|
||||
used = true;
|
||||
}
|
||||
|
||||
if (used) {
|
||||
if (m_delegate)
|
||||
m_delegate->onChangeRectangle(getBoxBounds());
|
||||
|
||||
editor->invalidate();
|
||||
return true;
|
||||
}
|
||||
return StandbyState::onMouseMove(editor, msg);
|
||||
else
|
||||
return StandbyState::onMouseMove(editor, msg);
|
||||
}
|
||||
|
||||
bool SelectBoxState::onSetCursor(Editor* editor)
|
||||
{
|
||||
if (m_movingRuler >= 0) {
|
||||
switch (m_rulers[m_movingRuler].getOrientation()) {
|
||||
case Ruler::Horizontal: ui::set_mouse_cursor(kSizeNSCursor); return true;
|
||||
case Ruler::Vertical: ui::set_mouse_cursor(kSizeWECursor); return true;
|
||||
if (hasFlag(RULERS)) {
|
||||
if (m_movingRuler >= 0) {
|
||||
switch (m_rulers[m_movingRuler].getOrientation()) {
|
||||
case Ruler::Horizontal: ui::set_mouse_cursor(kSizeNSCursor); return true;
|
||||
case Ruler::Vertical: ui::set_mouse_cursor(kSizeWECursor); return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int x = ui::get_mouse_position().x;
|
||||
int y = ui::get_mouse_position().y;
|
||||
int x = ui::get_mouse_position().x;
|
||||
int y = ui::get_mouse_position().y;
|
||||
|
||||
for (Rulers::iterator it = m_rulers.begin(), end = m_rulers.end(); it != end; ++it) {
|
||||
if (touchRuler(editor, *it, x, y)) {
|
||||
switch (it->getOrientation()) {
|
||||
case Ruler::Horizontal:
|
||||
ui::set_mouse_cursor(kSizeNSCursor);
|
||||
return true;
|
||||
case Ruler::Vertical:
|
||||
ui::set_mouse_cursor(kSizeWECursor);
|
||||
return true;
|
||||
for (Rulers::iterator it = m_rulers.begin(), end = m_rulers.end(); it != end; ++it) {
|
||||
if (touchRuler(editor, *it, x, y)) {
|
||||
switch (it->getOrientation()) {
|
||||
case Ruler::Horizontal:
|
||||
ui::set_mouse_cursor(kSizeNSCursor);
|
||||
return true;
|
||||
case Ruler::Vertical:
|
||||
ui::set_mouse_cursor(kSizeWECursor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return StandbyState::onSetCursor(editor);
|
||||
}
|
||||
|
||||
bool SelectBoxState::requireBrushPreview()
|
||||
{
|
||||
if (hasFlag(QUICKBOX))
|
||||
return true;
|
||||
|
||||
// Returns false as it overrides default standby state behavior &
|
||||
// look. This state uses normal arrow cursors.
|
||||
return false;
|
||||
}
|
||||
|
||||
tools::Ink* SelectBoxState::getStateInk()
|
||||
{
|
||||
if (hasFlag(QUICKBOX))
|
||||
return App::instance()->getToolBox()->getInkById(
|
||||
tools::WellKnownInks::Selection);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SelectBoxState::preRenderDecorator(EditorPreRender* render)
|
||||
{
|
||||
// Without black shadow?
|
||||
|
@ -20,7 +20,14 @@ namespace app {
|
||||
class SelectBoxDelegate {
|
||||
public:
|
||||
virtual ~SelectBoxDelegate() { }
|
||||
virtual void onChangeRectangle(const gfx::Rect& rect) = 0;
|
||||
|
||||
// Called each time the selected box is modified (e.g. rulers are
|
||||
// moved).
|
||||
virtual void onChangeRectangle(const gfx::Rect& rect) { }
|
||||
|
||||
// Called only in QUICKBOX mode, when the user released the mouse
|
||||
// button.
|
||||
virtual void onQuickboxEnd(const gfx::Rect& rect) { }
|
||||
};
|
||||
|
||||
class SelectBoxState : public StandbyState
|
||||
@ -29,9 +36,10 @@ namespace app {
|
||||
|
||||
public:
|
||||
typedef int Flags;
|
||||
static const int RULERS = 1;
|
||||
static const int DARKOUTSIDE = 2;
|
||||
static const int GRID = 4;
|
||||
static const int RULERS = 1; // Draw rulers at each edge of the current box
|
||||
static const int DARKOUTSIDE = 2; // The outside of the box must be darker
|
||||
static const int GRID = 4; // Draw a grid
|
||||
static const int QUICKBOX = 8; // Select the box as in selection tool, drawing a boxu
|
||||
|
||||
SelectBoxState(SelectBoxDelegate* delegate,
|
||||
const gfx::Rect& rc,
|
||||
@ -48,10 +56,8 @@ namespace app {
|
||||
virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
|
||||
virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
|
||||
virtual bool onSetCursor(Editor* editor) override;
|
||||
|
||||
// Returns false as it overrides default standby state behavior &
|
||||
// look. This state uses normal arrow cursors.
|
||||
virtual bool requireBrushPreview() override { return false; }
|
||||
virtual bool requireBrushPreview() override;
|
||||
virtual tools::Ink* getStateInk() override;
|
||||
|
||||
// EditorDecorator overrides
|
||||
virtual void preRenderDecorator(EditorPreRender* render) override;
|
||||
@ -69,6 +75,8 @@ namespace app {
|
||||
SelectBoxDelegate* m_delegate;
|
||||
Rulers m_rulers;
|
||||
int m_movingRuler;
|
||||
bool m_selectingBox;
|
||||
gfx::Point m_startingPos;
|
||||
Flags m_flags;
|
||||
};
|
||||
|
||||
|
@ -53,11 +53,11 @@ static base::SharedPtr<Brush> special_brush;
|
||||
static base::SharedPtr<Brush> last_brush;
|
||||
static gfx::Point brush_origin;
|
||||
|
||||
void set_tool_loop_brush_image(doc::ImageRef& image,
|
||||
void set_tool_loop_brush_image(const doc::Image* image,
|
||||
const gfx::Point& origin)
|
||||
{
|
||||
special_brush.reset(new Brush());
|
||||
special_brush->setImage(image.get());
|
||||
special_brush->setImage(image);
|
||||
special_brush->setPatternOrigin(brush_origin = origin);
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace app {
|
||||
}
|
||||
|
||||
void set_tool_loop_brush_image(
|
||||
doc::ImageRef& image,
|
||||
const doc::Image* image,
|
||||
const gfx::Point& origin);
|
||||
bool is_tool_loop_brush_image();
|
||||
void discard_tool_loop_brush_image();
|
||||
|
@ -24,8 +24,13 @@ using namespace doc;
|
||||
|
||||
Image* new_image_from_mask(const Site& site)
|
||||
{
|
||||
const Sprite* srcSprite = site.sprite();
|
||||
const Mask* srcMask = static_cast<const app::Document*>(site.document())->mask();
|
||||
return new_image_from_mask(site, srcMask);
|
||||
}
|
||||
|
||||
doc::Image* new_image_from_mask(const doc::Site& site, const doc::Mask* srcMask)
|
||||
{
|
||||
const Sprite* srcSprite = site.sprite();
|
||||
const Image* srcMaskBitmap = srcMask->bitmap();
|
||||
const gfx::Rect& srcBounds = srcMask->bounds();
|
||||
int x, y, u, v, getx, gety;
|
||||
@ -38,7 +43,7 @@ Image* new_image_from_mask(const Site& site)
|
||||
|
||||
dst = Image::create(srcSprite->pixelFormat(), srcBounds.w, srcBounds.h);
|
||||
if (!dst)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
// Clear the new image
|
||||
dst->setMaskColor(src ? src->maskColor(): srcSprite->transparentColor());
|
||||
|
@ -11,12 +11,14 @@
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Mask;
|
||||
class Site;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
doc::Image* new_image_from_mask(const doc::Site& site);
|
||||
doc::Image* new_image_from_mask(const doc::Site& site, const doc::Mask* mask);
|
||||
|
||||
} // namespace app
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user