Now we can create brushes selecting the box directly (fix #154)

This commit is contained in:
David Capello 2015-04-27 11:31:48 -03:00
parent 215f594273
commit d9fb81e5d1
11 changed files with 180 additions and 60 deletions

View File

@ -439,6 +439,7 @@
<param name="orientation" value="vertical" />
</item>
<item command="MaskContent" text="Transfor&amp;m" />
<item command="NewBrush" text="New &amp;Brush" />
<separator />
<item command="ReplaceColor" text="R&amp;eplace Color..." />
<item command="InvertColor" text="&amp;Invert" />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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