mirror of https://github.com/aseprite/aseprite.git
Implement tool loop for selection tools
This commit is contained in:
parent
366751d1f2
commit
23d4543272
|
@ -195,6 +195,7 @@ public:
|
|||
const MaskBoundaries& maskBoundaries() const { return m_maskBoundaries; }
|
||||
|
||||
MaskBoundaries& maskBoundaries() { return m_maskBoundaries; }
|
||||
void setMaskBoundaries(const MaskBoundaries& segs) { m_maskBoundaries = segs; }
|
||||
|
||||
bool hasMaskBoundaries() const { return !m_maskBoundaries.isEmpty(); }
|
||||
|
||||
|
|
|
@ -292,8 +292,15 @@ public:
|
|||
m_maxBounds |= rc;
|
||||
else {
|
||||
rc &= loop->getDstImage()->bounds();
|
||||
for (int v = rc.y; v < rc.y2(); ++v)
|
||||
BaseInk::inkHline(rc.x, v, rc.x2() - 1, loop);
|
||||
if (loop->isSelectionToolLoop())
|
||||
loop->addSelectionToolPoint(rc);
|
||||
|
||||
// NOTE: this condition is here for drawing mode switches, remove/rework after testing
|
||||
if (!loop->isSelectionToolLoop() ||
|
||||
(loop->getPointShape()->isFloodFill() || !loop->getPreviewFilled())) {
|
||||
for (int v = rc.y; v < rc.y2(); ++v)
|
||||
BaseInk::inkHline(rc.x, v, rc.x2() - 1, loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -457,8 +464,15 @@ public:
|
|||
}
|
||||
else {
|
||||
rc &= loop->getDstImage()->bounds();
|
||||
for (int v = rc.y; v < rc.y2(); ++v)
|
||||
BaseInk::inkHline(rc.x, v, rc.x2() - 1, loop);
|
||||
if (loop->isSelectionToolLoop())
|
||||
loop->addSelectionToolPoint(rc);
|
||||
|
||||
// NOTE: this condition is here for drawing mode switches, remove/rework after testing
|
||||
if (!loop->isSelectionToolLoop() ||
|
||||
(loop->getPointShape()->isFloodFill() || !loop->getPreviewFilled())) {
|
||||
for (int v = rc.y; v < rc.y2(); ++v)
|
||||
BaseInk::inkHline(rc.x, v, rc.x2() - 1, loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -264,6 +264,11 @@ public:
|
|||
virtual void onSliceRect(const gfx::Rect& bounds) = 0;
|
||||
|
||||
virtual const app::TiledModeHelper& getTiledModeHelper() = 0;
|
||||
|
||||
// Used by selection tools
|
||||
virtual bool isSelectionToolLoop() const = 0;
|
||||
virtual void addSelectionToolPoint(const gfx::Rect& rc) = 0;
|
||||
virtual void clearSelectionToolMask(const bool finalStep) = 0;
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -271,6 +271,8 @@ void ToolLoopManager::doLoopStep(bool lastStep)
|
|||
}
|
||||
|
||||
m_toolLoop->validateDstImage(m_dirtyArea);
|
||||
if (!lastStep && m_toolLoop->isSelectionToolLoop())
|
||||
m_toolLoop->clearSelectionToolMask(false);
|
||||
|
||||
// Join or fill user points
|
||||
if (fillStrokes)
|
||||
|
@ -341,6 +343,13 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
|
|||
m_dirtyArea.createUnion(m_dirtyArea, Region(r1.createUnion(r2)));
|
||||
}
|
||||
|
||||
// This ensures the Selection Tool Mask doesn't leave a 'trail' behind
|
||||
if (m_toolLoop->isSelectionToolLoop()) {
|
||||
auto bounds = m_dirtyArea.bounds();
|
||||
bounds.enlarge(1);
|
||||
m_dirtyArea |= gfx::Region(bounds);
|
||||
}
|
||||
|
||||
// Merge new dirty area with the previous one (for tools like line
|
||||
// or rectangle it's needed to redraw the previous position and
|
||||
// the new one)
|
||||
|
|
|
@ -219,6 +219,8 @@ bool DrawingState::onMouseUp(Editor* editor, MouseMessage* msg)
|
|||
if (m_toolLoopManager->releaseButton(m_lastPointer))
|
||||
return true;
|
||||
}
|
||||
if (m_toolLoop->isSelectionToolLoop())
|
||||
m_toolLoop->clearSelectionToolMask(true);
|
||||
|
||||
destroyLoop(editor);
|
||||
|
||||
|
@ -403,6 +405,9 @@ void DrawingState::destroyLoopIfCanceled(Editor* editor)
|
|||
{
|
||||
// Cancel drawing loop
|
||||
if (m_toolLoopManager->isCanceled()) {
|
||||
if (m_toolLoop->isSelectionToolLoop())
|
||||
m_toolLoop->clearSelectionToolMask(true);
|
||||
|
||||
destroyLoop(editor);
|
||||
|
||||
// Change to standby state
|
||||
|
|
|
@ -207,6 +207,7 @@ Editor::Editor(Doc* document, EditorFlags flags, EditorStatePtr state)
|
|||
m_symmetryModeConn = Preferences::instance().symmetryMode.enabled.AfterChange.connect(
|
||||
[this] { invalidateIfActive(); });
|
||||
m_showExtrasConn = m_docPref.show.AfterChange.connect([this] { onShowExtrasChange(); });
|
||||
m_selectionToolMask = std::unique_ptr<Mask>(new Mask());
|
||||
|
||||
m_document->add_observer(this);
|
||||
|
||||
|
@ -1031,8 +1032,20 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
|||
}
|
||||
|
||||
// Draw the mask
|
||||
if (m_document->hasMaskBoundaries())
|
||||
drawMask(g);
|
||||
const bool haveSegs = m_document->hasMaskBoundaries();
|
||||
if (haveSegs)
|
||||
drawMask(g, MaskIndex::Document);
|
||||
|
||||
if (!m_selectionToolMask->isEmpty()) {
|
||||
const auto segs = m_document->maskBoundaries();
|
||||
m_document->generateMaskBoundaries(m_selectionToolMask.get());
|
||||
drawMask(g, MaskIndex::SelectionTool);
|
||||
|
||||
if (haveSegs)
|
||||
m_document->setMaskBoundaries(segs);
|
||||
else
|
||||
m_document->destroyMaskBoundaries();
|
||||
}
|
||||
|
||||
// Post-render decorator.
|
||||
if ((m_flags & kShowDecorators) && m_decorator) {
|
||||
|
@ -1067,9 +1080,10 @@ void Editor::drawSpriteClipped(const gfx::Region& updateRegion)
|
|||
* regenerate boundaries, use the sprite_generate_mask_boundaries()
|
||||
* routine.
|
||||
*/
|
||||
void Editor::drawMask(Graphics* g)
|
||||
void Editor::drawMask(Graphics* g, const MaskIndex index)
|
||||
{
|
||||
if ((m_flags & kShowMask) == 0 || !m_docPref.show.selectionEdges())
|
||||
if (index == MaskIndex::Document &&
|
||||
((m_flags & kShowMask) == 0 || !m_docPref.show.selectionEdges()))
|
||||
return;
|
||||
|
||||
ASSERT(m_document->hasMaskBoundaries());
|
||||
|
@ -1084,10 +1098,25 @@ void Editor::drawMask(Graphics* g)
|
|||
|
||||
ui::Paint paint;
|
||||
paint.style(ui::Paint::Stroke);
|
||||
set_checkered_paint_mode(paint,
|
||||
m_antsOffset,
|
||||
gfx::rgba(0, 0, 0, 255),
|
||||
gfx::rgba(255, 255, 255, 255));
|
||||
if (index == MaskIndex::Document) {
|
||||
set_checkered_paint_mode(paint,
|
||||
m_antsOffset,
|
||||
gfx::rgba(0, 0, 0, 255),
|
||||
gfx::rgba(255, 255, 255, 255));
|
||||
}
|
||||
else {
|
||||
// NOTE: this condition is here for drawing mode switches, remove/rework after testing
|
||||
if ((int(getToolLoopModifiers()) & (int(tools::ToolLoopModifiers::kSubtractSelection) |
|
||||
int(tools::ToolLoopModifiers::kIntersectSelection))) != 0) {
|
||||
set_checkered_paint_mode(paint,
|
||||
m_antsOffset,
|
||||
gfx::rgba(0, 32, 64, 255),
|
||||
gfx::rgba(0, 128, 255, 255));
|
||||
}
|
||||
else {
|
||||
paint.color(gfx::rgba(0, 0, 0, 255));
|
||||
}
|
||||
}
|
||||
|
||||
// We translate the path instead of applying a matrix to the
|
||||
// ui::Graphics so the "checkered" pattern is not scaled too.
|
||||
|
@ -1099,10 +1128,12 @@ void Editor::drawMask(Graphics* g)
|
|||
|
||||
void Editor::drawMaskSafe()
|
||||
{
|
||||
if ((m_flags & kShowMask) == 0)
|
||||
if (((m_flags & kShowMask) == 0 && !m_selectionToolMask->isEmpty()) ||
|
||||
!(isVisible() && m_document))
|
||||
return;
|
||||
|
||||
if (isVisible() && m_document && m_document->hasMaskBoundaries()) {
|
||||
const bool haveSegs = m_document->hasMaskBoundaries();
|
||||
if (haveSegs || !m_selectionToolMask->isEmpty()) {
|
||||
Region region;
|
||||
getDrawableRegion(region, kCutTopWindows);
|
||||
region.offset(-bounds().origin());
|
||||
|
@ -1110,10 +1141,27 @@ void Editor::drawMaskSafe()
|
|||
HideBrushPreview hide(m_brushPreview);
|
||||
GraphicsPtr g = getGraphics(clientBounds());
|
||||
|
||||
for (const gfx::Rect& rc : region) {
|
||||
IntersectClip clip(g.get(), rc);
|
||||
if (clip)
|
||||
drawMask(g.get());
|
||||
if (haveSegs) {
|
||||
for (const gfx::Rect& rc : region) {
|
||||
IntersectClip clip(g.get(), rc);
|
||||
if (clip)
|
||||
drawMask(g.get(), MaskIndex::Document);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_selectionToolMask->isEmpty()) {
|
||||
const auto segs = m_document->maskBoundaries();
|
||||
m_document->generateMaskBoundaries(m_selectionToolMask.get());
|
||||
for (const gfx::Rect& rc : region) {
|
||||
IntersectClip clip(g.get(), rc);
|
||||
if (clip)
|
||||
drawMask(g.get(), MaskIndex::SelectionTool);
|
||||
}
|
||||
|
||||
if (haveSegs)
|
||||
m_document->setMaskBoundaries(segs);
|
||||
else
|
||||
m_document->destroyMaskBoundaries();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2395,7 +2443,10 @@ void Editor::onPaint(ui::PaintEvent& ev)
|
|||
|
||||
// Draw the mask boundaries
|
||||
if (m_document->hasMaskBoundaries()) {
|
||||
drawMask(g);
|
||||
drawMask(g, MaskIndex::Document);
|
||||
m_antsTimer.start();
|
||||
}
|
||||
else if (!m_selectionToolMask->isEmpty()) {
|
||||
m_antsTimer.start();
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -318,6 +318,7 @@ public:
|
|||
void showUnhandledException(const std::exception& ex, const ui::Message* msg);
|
||||
|
||||
static void registerCommands();
|
||||
Mask* getSelectionToolMask() { return m_selectionToolMask.get(); }
|
||||
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
|
@ -361,7 +362,8 @@ private:
|
|||
void drawBackground(ui::Graphics* g);
|
||||
void drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc);
|
||||
void drawMaskSafe();
|
||||
void drawMask(ui::Graphics* g);
|
||||
enum MaskIndex { Document, SelectionTool, Count };
|
||||
void drawMask(ui::Graphics* g, MaskIndex index);
|
||||
void drawGrid(ui::Graphics* g,
|
||||
const gfx::Rect& spriteBounds,
|
||||
const gfx::Rect& gridBounds,
|
||||
|
@ -511,6 +513,9 @@ private:
|
|||
// same document can show the same preview image/stroke being drawn
|
||||
// (search for Render::setPreviewImage()).
|
||||
static std::unique_ptr<EditorRender> m_renderEngine;
|
||||
|
||||
// Used for selection tool feedback
|
||||
std::unique_ptr<Mask> m_selectionToolMask;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -458,8 +458,10 @@ bool StandbyState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos)
|
|||
return StateWithWheelBehavior::onSetCursor(editor, mouseScreenPos);
|
||||
}
|
||||
|
||||
static bool selectionToolLoopEnabled = true;
|
||||
bool StandbyState::onKeyDown(Editor* editor, KeyMessage* msg)
|
||||
{
|
||||
selectionToolLoopEnabled = true;
|
||||
if (Preferences::instance().editor.straightLinePreview() &&
|
||||
checkStartDrawingStraightLine(editor, nullptr, nullptr))
|
||||
return false;
|
||||
|
@ -470,6 +472,8 @@ bool StandbyState::onKeyDown(Editor* editor, KeyMessage* msg)
|
|||
// in a selection-like tool
|
||||
if (keys.size() == 1 && keys[0]->wheelAction() == WheelAction::BrushSize &&
|
||||
editor->getCurrentEditorInk()->isSelection()) {
|
||||
// TEST: disable new tool loop implementation for selection tools
|
||||
selectionToolLoopEnabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -619,7 +623,8 @@ DrawingState* StandbyState::startDrawingState(Editor* editor,
|
|||
UIContext::instance(),
|
||||
pointer.button(),
|
||||
(drawingType == DrawingType::LineFreehand),
|
||||
(drawingType == DrawingType::SelectTiles));
|
||||
(drawingType == DrawingType::SelectTiles),
|
||||
selectionToolLoopEnabled);
|
||||
if (!toolLoop)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -426,6 +426,11 @@ public:
|
|||
|
||||
const app::TiledModeHelper& getTiledModeHelper() override { return m_tiledModeHelper; }
|
||||
|
||||
// Used by selection tools
|
||||
bool isSelectionToolLoop() const override { return false; }
|
||||
void addSelectionToolPoint(const gfx::Rect& rc) override {};
|
||||
void clearSelectionToolMask(const bool finalStep) override {};
|
||||
|
||||
protected:
|
||||
void updateAllVisibleRegion()
|
||||
{
|
||||
|
@ -444,16 +449,12 @@ protected:
|
|||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// For drawing
|
||||
// Common properties between drawing/selection ToolLoop impl
|
||||
|
||||
class ToolLoopImpl final : public ToolLoopBase,
|
||||
public EditorObserver {
|
||||
class PaintToolLoopBase : public ToolLoopBase,
|
||||
public EditorObserver {
|
||||
protected:
|
||||
Context* m_context;
|
||||
bool m_filled;
|
||||
bool m_previewFilled;
|
||||
int m_sprayWidth;
|
||||
int m_spraySpeed;
|
||||
bool m_useMask;
|
||||
Mask* m_mask;
|
||||
gfx::Point m_maskOrigin;
|
||||
bool m_internalCancel = false;
|
||||
|
@ -463,12 +464,12 @@ class ToolLoopImpl final : public ToolLoopBase,
|
|||
bool m_saveLastPoint;
|
||||
|
||||
public:
|
||||
ToolLoopImpl(Editor* editor,
|
||||
Site& site,
|
||||
const doc::Grid& grid,
|
||||
Context* context,
|
||||
ToolLoopParams& params,
|
||||
const bool saveLastPoint)
|
||||
PaintToolLoopBase(Editor* editor,
|
||||
Site& site,
|
||||
const doc::Grid& grid,
|
||||
Context* context,
|
||||
ToolLoopParams& params,
|
||||
const bool saveLastPoint)
|
||||
: ToolLoopBase(editor, site, grid, params)
|
||||
, m_context(context)
|
||||
, m_tx(Tx::DontLockDoc,
|
||||
|
@ -481,6 +482,22 @@ public:
|
|||
ModifyDocument))
|
||||
, m_floodfillSrcImage(nullptr)
|
||||
, m_saveLastPoint(saveLastPoint)
|
||||
{
|
||||
}
|
||||
|
||||
~PaintToolLoopBase()
|
||||
{
|
||||
if (m_editor)
|
||||
m_editor->remove_observer(this);
|
||||
|
||||
// getSrcImage() is a virtual member function but ToolLoopImpl is
|
||||
// marked as final to avoid not calling a derived version from
|
||||
// this destructor.
|
||||
if (m_floodfillSrcImage != getSrcImage())
|
||||
delete m_floodfillSrcImage;
|
||||
}
|
||||
|
||||
void constructor_prologue()
|
||||
{
|
||||
if (m_pointShape->isFloodFill()) {
|
||||
if (m_tilesMode) {
|
||||
|
@ -503,55 +520,10 @@ public:
|
|||
m_floodfillSrcImage = render::rasterize_with_sprite_bounds(cel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 'isSelectionPreview = true' if the intention is to show a preview
|
||||
// of Selection tools or Slice tool.
|
||||
const bool isSelectionPreview = m_ink->isSelection() || m_ink->isSlice();
|
||||
m_expandCelCanvas.reset(new ExpandCelCanvas(
|
||||
site,
|
||||
m_layer,
|
||||
m_docPref.tiled.mode(),
|
||||
m_tx,
|
||||
ExpandCelCanvas::Flags(
|
||||
ExpandCelCanvas::NeedsSource |
|
||||
(m_layer->isTilemap() && (!m_tilesMode || isSelectionPreview) ?
|
||||
ExpandCelCanvas::PixelsBounds :
|
||||
ExpandCelCanvas::None) |
|
||||
(m_layer->isTilemap() && site.tilemapMode() == TilemapMode::Pixels &&
|
||||
site.tilesetMode() == TilesetMode::Manual && !isSelectionPreview ?
|
||||
ExpandCelCanvas::TilesetPreview :
|
||||
ExpandCelCanvas::None) |
|
||||
(isSelectionPreview ? ExpandCelCanvas::SelectionPreview : ExpandCelCanvas::None))));
|
||||
|
||||
if (!m_floodfillSrcImage)
|
||||
m_floodfillSrcImage = const_cast<Image*>(getSrcImage());
|
||||
|
||||
// Settings
|
||||
switch (m_tool->getFill(m_button)) {
|
||||
case tools::FillNone: m_filled = false; break;
|
||||
case tools::FillAlways: m_filled = true; break;
|
||||
case tools::FillOptional: m_filled = m_toolPref.filled(); break;
|
||||
}
|
||||
|
||||
m_previewFilled = m_toolPref.filledPreview();
|
||||
m_sprayWidth = m_toolPref.spray.width();
|
||||
m_spraySpeed = m_toolPref.spray.speed();
|
||||
|
||||
if (isSelectionPreview) {
|
||||
m_useMask = false;
|
||||
}
|
||||
else {
|
||||
m_useMask = m_document->isMaskVisible();
|
||||
}
|
||||
|
||||
// Start with an empty mask if the user is selecting with "default selection mode"
|
||||
if (isSelectionPreview &&
|
||||
(!m_document->isMaskVisible() ||
|
||||
(int(getModifiers()) & int(tools::ToolLoopModifiers::kReplaceSelection)))) {
|
||||
Mask emptyMask;
|
||||
m_tx(new cmd::SetMask(m_document, &emptyMask));
|
||||
}
|
||||
|
||||
void constructor_epilogue()
|
||||
{
|
||||
// Setup the new grid of ExpandCelCanvas which can be displaced to
|
||||
// match the new temporal cel position (m_celOrigin).
|
||||
m_grid = m_expandCelCanvas->getGrid();
|
||||
|
@ -566,18 +538,6 @@ public:
|
|||
m_editor->add_observer(this);
|
||||
}
|
||||
|
||||
~ToolLoopImpl()
|
||||
{
|
||||
if (m_editor)
|
||||
m_editor->remove_observer(this);
|
||||
|
||||
// getSrcImage() is a virtual member function but ToolLoopImpl is
|
||||
// marked as final to avoid not calling a derived version from
|
||||
// this destructor.
|
||||
if (m_floodfillSrcImage != getSrcImage())
|
||||
delete m_floodfillSrcImage;
|
||||
}
|
||||
|
||||
// IToolLoop interface
|
||||
bool needsCelCoordinates() override
|
||||
{
|
||||
|
@ -591,50 +551,6 @@ public:
|
|||
return ToolLoopBase::needsCelCoordinates();
|
||||
}
|
||||
|
||||
void commit() override
|
||||
{
|
||||
bool redraw = false;
|
||||
|
||||
if (!m_internalCancel) {
|
||||
// Freehand changes the last point
|
||||
if (m_saveLastPoint) {
|
||||
m_tx(new cmd::SetLastPoint(m_document, getController()->getLastPoint().toPoint()));
|
||||
}
|
||||
|
||||
// Paint ink
|
||||
if (m_ink->isPaint()) {
|
||||
try {
|
||||
ContextReader reader(m_context, 500);
|
||||
ContextWriter writer(reader);
|
||||
m_expandCelCanvas->commit();
|
||||
}
|
||||
catch (const LockedDocException& ex) {
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
// Selection ink
|
||||
else if (m_ink->isSelection()) {
|
||||
redraw = true;
|
||||
|
||||
// Show selection edges
|
||||
if (Preferences::instance().selection.autoShowSelectionEdges())
|
||||
m_docPref.show.selectionEdges(true);
|
||||
}
|
||||
// Slice ink
|
||||
else if (m_ink->isSlice()) {
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
m_tx.commit();
|
||||
}
|
||||
else {
|
||||
rollback();
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
update_screen_for_document(m_document);
|
||||
}
|
||||
|
||||
void rollback() override
|
||||
{
|
||||
try {
|
||||
|
@ -675,14 +591,9 @@ public:
|
|||
m_expandCelCanvas->copyValidDestToSourceCanvas(rgn);
|
||||
}
|
||||
|
||||
bool useMask() override { return m_useMask; }
|
||||
Mask* getMask() override { return m_mask; }
|
||||
void setMask(Mask* newMask) override { m_tx(new cmd::SetMask(m_document, newMask)); }
|
||||
gfx::Point getMaskOrigin() override { return m_maskOrigin; }
|
||||
bool getFilled() override { return m_filled; }
|
||||
bool getPreviewFilled() override { return m_previewFilled; }
|
||||
int getSprayWidth() override { return m_sprayWidth; }
|
||||
int getSpraySpeed() override { return m_spraySpeed; }
|
||||
|
||||
void onSliceRect(const gfx::Rect& bounds) override
|
||||
{
|
||||
|
@ -730,6 +641,228 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// For drawing
|
||||
|
||||
class ToolLoopImpl final : public PaintToolLoopBase {
|
||||
public:
|
||||
ToolLoopImpl(Editor* editor,
|
||||
Site& site,
|
||||
const doc::Grid& grid,
|
||||
Context* context,
|
||||
ToolLoopParams& params,
|
||||
const bool saveLastPoint)
|
||||
: PaintToolLoopBase(editor, site, grid, context, params, saveLastPoint)
|
||||
{
|
||||
constructor_prologue();
|
||||
|
||||
// 'isSelectionPreview = true' if the intention is to show a preview
|
||||
// of Selection tools or Slice tool.
|
||||
const bool isSelectionPreview = m_ink->isSelection() || m_ink->isSlice();
|
||||
m_expandCelCanvas.reset(new ExpandCelCanvas(
|
||||
site,
|
||||
m_layer,
|
||||
m_docPref.tiled.mode(),
|
||||
m_tx,
|
||||
ExpandCelCanvas::Flags(
|
||||
ExpandCelCanvas::NeedsSource |
|
||||
(m_layer->isTilemap() && (!m_tilesMode || isSelectionPreview) ?
|
||||
ExpandCelCanvas::PixelsBounds :
|
||||
ExpandCelCanvas::None) |
|
||||
(m_layer->isTilemap() && site.tilemapMode() == TilemapMode::Pixels &&
|
||||
site.tilesetMode() == TilesetMode::Manual && !isSelectionPreview ?
|
||||
ExpandCelCanvas::TilesetPreview :
|
||||
ExpandCelCanvas::None) |
|
||||
(isSelectionPreview ? ExpandCelCanvas::SelectionPreview : ExpandCelCanvas::None))));
|
||||
|
||||
if (!m_floodfillSrcImage)
|
||||
m_floodfillSrcImage = const_cast<Image*>(getSrcImage());
|
||||
|
||||
// Settings
|
||||
switch (m_tool->getFill(m_button)) {
|
||||
case tools::FillNone: m_filled = false; break;
|
||||
case tools::FillAlways: m_filled = true; break;
|
||||
case tools::FillOptional: m_filled = m_toolPref.filled(); break;
|
||||
}
|
||||
|
||||
m_previewFilled = m_toolPref.filledPreview();
|
||||
m_sprayWidth = m_toolPref.spray.width();
|
||||
m_spraySpeed = m_toolPref.spray.speed();
|
||||
|
||||
if (isSelectionPreview) {
|
||||
m_useMask = false;
|
||||
}
|
||||
else {
|
||||
m_useMask = m_document->isMaskVisible();
|
||||
}
|
||||
|
||||
// Start with an empty mask if the user is selecting with "default selection mode"
|
||||
if (isSelectionPreview &&
|
||||
(!m_document->isMaskVisible() ||
|
||||
(int(getModifiers()) & int(tools::ToolLoopModifiers::kReplaceSelection)) != 0)) {
|
||||
Mask emptyMask;
|
||||
m_tx(new cmd::SetMask(m_document, &emptyMask));
|
||||
}
|
||||
|
||||
constructor_epilogue();
|
||||
}
|
||||
|
||||
// IToolLoop interface
|
||||
void commit() override
|
||||
{
|
||||
bool redraw = false;
|
||||
|
||||
if (!m_internalCancel) {
|
||||
// Freehand changes the last point
|
||||
if (m_saveLastPoint) {
|
||||
m_tx(new cmd::SetLastPoint(m_document, getController()->getLastPoint().toPoint()));
|
||||
}
|
||||
|
||||
// Paint ink
|
||||
if (m_ink->isPaint()) {
|
||||
try {
|
||||
ContextReader reader(m_context, 500);
|
||||
ContextWriter writer(reader);
|
||||
m_expandCelCanvas->commit();
|
||||
}
|
||||
catch (const LockedDocException& ex) {
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
// Selection ink
|
||||
else if (m_ink->isSelection()) {
|
||||
redraw = true;
|
||||
|
||||
// Show selection edges
|
||||
if (Preferences::instance().selection.autoShowSelectionEdges())
|
||||
m_docPref.show.selectionEdges(true);
|
||||
}
|
||||
// Slice ink
|
||||
else if (m_ink->isSlice()) {
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
m_tx.commit();
|
||||
}
|
||||
else {
|
||||
rollback();
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
update_screen_for_document(m_document);
|
||||
}
|
||||
|
||||
bool useMask() override { return m_useMask; }
|
||||
bool getFilled() override { return m_filled; }
|
||||
bool getPreviewFilled() override { return m_previewFilled; }
|
||||
int getSprayWidth() override { return m_sprayWidth; }
|
||||
int getSpraySpeed() override { return m_spraySpeed; }
|
||||
|
||||
private:
|
||||
bool m_filled;
|
||||
bool m_previewFilled;
|
||||
int m_sprayWidth;
|
||||
int m_spraySpeed;
|
||||
bool m_useMask;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// For selection tools
|
||||
|
||||
class SelectionToolLoopImpl final : public PaintToolLoopBase {
|
||||
public:
|
||||
SelectionToolLoopImpl(Editor* editor,
|
||||
Site& site,
|
||||
const doc::Grid& grid,
|
||||
Context* context,
|
||||
ToolLoopParams& params,
|
||||
const bool saveLastPoint)
|
||||
: PaintToolLoopBase(editor, site, grid, context, params, saveLastPoint)
|
||||
{
|
||||
constructor_prologue();
|
||||
|
||||
// 'isSelectionPreview = true' if the intention is to show a preview
|
||||
// of Selection tools or Slice tool.
|
||||
m_expandCelCanvas.reset(new ExpandCelCanvas(
|
||||
site,
|
||||
m_layer,
|
||||
m_docPref.tiled.mode(),
|
||||
m_tx,
|
||||
ExpandCelCanvas::Flags(
|
||||
ExpandCelCanvas::NeedsSource |
|
||||
(m_layer->isTilemap() ? ExpandCelCanvas::PixelsBounds : ExpandCelCanvas::None) |
|
||||
ExpandCelCanvas::SelectionPreview)));
|
||||
|
||||
if (!m_floodfillSrcImage)
|
||||
m_floodfillSrcImage = const_cast<Image*>(getSrcImage());
|
||||
|
||||
// Start with an empty mask if the user is selecting with "default selection mode"
|
||||
if (!m_document->isMaskVisible() ||
|
||||
(int(getModifiers()) & int(tools::ToolLoopModifiers::kReplaceSelection)) != 0) {
|
||||
Mask emptyMask;
|
||||
m_tx(new cmd::SetMask(m_document, &emptyMask));
|
||||
}
|
||||
|
||||
constructor_epilogue();
|
||||
}
|
||||
|
||||
// For drawing the selection to second mask
|
||||
bool isSelectionToolLoop() const override { return true; }
|
||||
void addSelectionToolPoint(const gfx::Rect& rc) override
|
||||
{
|
||||
if (rc.w >= 1 && rc.h >= 1)
|
||||
m_editor->getSelectionToolMask()->add(rc);
|
||||
}
|
||||
|
||||
void clearSelectionToolMask(const bool finalStep) override
|
||||
{
|
||||
if (finalStep || getTracePolicy() == tools::TracePolicy::Last)
|
||||
m_editor->getSelectionToolMask()->clear();
|
||||
}
|
||||
|
||||
// IToolLoop interface
|
||||
void commit() override
|
||||
{
|
||||
bool redraw = false;
|
||||
|
||||
if (!m_internalCancel) {
|
||||
// Freehand changes the last point
|
||||
if (m_saveLastPoint) {
|
||||
m_tx(new cmd::SetLastPoint(m_document, getController()->getLastPoint().toPoint()));
|
||||
}
|
||||
|
||||
// Selection ink
|
||||
redraw = true;
|
||||
if (m_ink->isSelection() && Preferences::instance().selection.autoShowSelectionEdges()) {
|
||||
// Show selection edges
|
||||
m_docPref.show.selectionEdges(true);
|
||||
}
|
||||
|
||||
m_tx.commit();
|
||||
}
|
||||
else {
|
||||
rollback();
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
update_screen_for_document(m_document);
|
||||
}
|
||||
|
||||
bool useMask() override { return false; }
|
||||
bool getFilled() override { return true; }
|
||||
bool getPreviewFilled() override
|
||||
{
|
||||
// NOTE: this condition is here for drawing mode switches, remove/rework after testing
|
||||
if ((int(getModifiers()) & (int(tools::ToolLoopModifiers::kAddSelection) |
|
||||
int(tools::ToolLoopModifiers::kIntersectSelection))) != 0) {
|
||||
return getTracePolicy() == tools::TracePolicy::Last;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int getSprayWidth() override { return 0; }
|
||||
int getSpraySpeed() override { return 0; }
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// For user UI painting
|
||||
|
||||
|
@ -745,7 +878,8 @@ tools::ToolLoop* create_tool_loop(Editor* editor,
|
|||
Context* context,
|
||||
const tools::Pointer::Button button,
|
||||
const bool convertLineToFreehand,
|
||||
const bool selectTiles)
|
||||
const bool selectTiles,
|
||||
const bool selectionToolLoopEnabled)
|
||||
{
|
||||
Site site = editor->getSite();
|
||||
doc::Grid grid = site.grid();
|
||||
|
@ -850,8 +984,15 @@ tools::ToolLoop* create_tool_loop(Editor* editor,
|
|||
fill_toolloop_params_from_tool_preferences(params);
|
||||
|
||||
ASSERT(context->activeDocument() == editor->document());
|
||||
auto toolLoop = new ToolLoopImpl(editor, site, grid, context, params, saveLastPoint);
|
||||
if (selectionToolLoopEnabled && (params.ink->isSelection() || params.ink->isSlice())) {
|
||||
auto* toolLoop =
|
||||
new SelectionToolLoopImpl(editor, site, grid, context, params, saveLastPoint);
|
||||
if (selectTiles)
|
||||
toolLoop->forceSnapToTiles();
|
||||
|
||||
return toolLoop;
|
||||
}
|
||||
auto* toolLoop = new ToolLoopImpl(editor, site, grid, context, params, saveLastPoint);
|
||||
if (selectTiles)
|
||||
toolLoop->forceSnapToTiles();
|
||||
|
||||
|
|
|
@ -58,7 +58,8 @@ tools::ToolLoop* create_tool_loop(Editor* editor,
|
|||
Context* context,
|
||||
const tools::Pointer::Button button,
|
||||
const bool convertLineToFreehand,
|
||||
const bool selectTiles);
|
||||
const bool selectTiles,
|
||||
const bool selectionToolLoopEnabled);
|
||||
|
||||
tools::ToolLoop* create_tool_loop_preview(Editor* editor,
|
||||
const doc::BrushRef& brush,
|
||||
|
|
Loading…
Reference in New Issue