Implement tool loop for selection tools

This commit is contained in:
Liebranca 2025-03-27 09:39:01 -03:00 committed by David Capello
parent 366751d1f2
commit 23d4543272
10 changed files with 384 additions and 147 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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