From 0cf4dec9a249c3d0426737482ab2a426f5e09031 Mon Sep 17 00:00:00 2001 From: askmeaboutloom Date: Fri, 1 Nov 2024 01:55:58 +0100 Subject: [PATCH] Drag adjustment of fill and magic wand tolerance Similar to how the magic wand tool works in GIMP, clicking and dragging left and right will temporarily adjust the tolerance and show a preview of the effect thereof. Note that this is not how the fill tool works in GIMP, that just seems to have bogus behavior there when you drag it. Implements #1327. --- ChangeLog | 1 + src/desktop/toolwidgets/fillsettings.cpp | 29 +++++- src/desktop/toolwidgets/fillsettings.h | 3 + src/desktop/toolwidgets/selectionsettings.cpp | 29 +++++- src/desktop/toolwidgets/selectionsettings.h | 4 + src/libclient/tools/clickdetector.h | 38 +++++++- src/libclient/tools/floodfill.cpp | 90 +++++++++++++++---- src/libclient/tools/floodfill.h | 12 ++- src/libclient/tools/magicwand.cpp | 60 +++++++++++-- src/libclient/tools/magicwand.h | 9 ++ src/libclient/tools/toolcontroller.h | 4 +- 11 files changed, 246 insertions(+), 33 deletions(-) diff --git a/ChangeLog b/ChangeLog index c1482584b1..2c577b7bd7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -51,6 +51,7 @@ Unreleased Version 2.2.2-pre * Fix: The Delete Empty Annotations can now be undone without undoing what happened before it as well. * Feature: Empty annotations are now automatically deleted when you deselect them, to avoid the common clutter of stray empty annotations across the canvas. If you really did want an empty annotation, you can hit undo and it will return. * Server Feature: Setting a minimum protocol version of dp:4.24.0 now tells clients that they're using a Drawpile version too old for this server, rather than leaving them sitting at a session list where they can join no sessions. + * Feature: Allow clicking and dragging the fill and magic wand tools to adjust the tolerance. 2024-08-09 Version 2.2.2-beta.3 * Fix: Use more accurate timers for performance profiles if the platform supports it. diff --git a/src/desktop/toolwidgets/fillsettings.cpp b/src/desktop/toolwidgets/fillsettings.cpp index ab17e539c5..46504f137b 100644 --- a/src/desktop/toolwidgets/fillsettings.cpp +++ b/src/desktop/toolwidgets/fillsettings.cpp @@ -141,7 +141,7 @@ QWidget *FillSettings::createUiWidget(QWidget *parent) &FillSettings::updateSettings); connect( m_ui->tolerance, QOverload::of(&QSpinBox::valueChanged), this, - &FillSettings::updateSettings); + &FillSettings::updateTolerance); connect( m_ui->size, QOverload::of(&QSpinBox::valueChanged), this, &FillSettings::updateSettings); @@ -195,6 +195,10 @@ QWidget *FillSettings::createUiWidget(QWidget *parent) &FillSettings::setButtonState); setButtonState(false, false); + connect( + controller(), &ToolController::floodFillDragChanged, this, + &FillSettings::setDragState); + QWidget *disabledWidget = new QWidget; QVBoxLayout *disabledLayout = new QVBoxLayout(disabledWidget); @@ -259,7 +263,8 @@ void FillSettings::pushSettings() static_cast(controller()->getTool(Tool::FLOODFILL)); int size = m_ui->size->value(); tool->setParameters( - m_ui->tolerance->value() / qreal(m_ui->tolerance->maximum()), + m_toleranceBeforeDrag < 0 ? m_ui->tolerance->value() + : m_toleranceBeforeDrag, m_ui->expandShrink->effectiveValue(), m_ui->expandShrink->kernel(), m_ui->feather->value(), isSizeUnlimited(size) ? -1 : size, m_ui->opacity->value() / 100.0, m_ui->gap->value(), @@ -410,6 +415,13 @@ void FillSettings::stepAdjust1(bool increase) } } +void FillSettings::updateTolerance() +{ + if(m_toleranceBeforeDrag < 0) { + updateSettings(); + } +} + void FillSettings::updateSettings() { if(!m_updating) { @@ -481,6 +493,19 @@ void FillSettings::setButtonState(bool running, bool pending) m_ui->cancelButton->setEnabled(running || pending); } +void FillSettings::setDragState(bool dragging, int tolerance) +{ + if(dragging) { + if(m_toleranceBeforeDrag < 0) { + m_toleranceBeforeDrag = m_ui->tolerance->value(); + } + m_ui->tolerance->setValue(tolerance); + } else if(m_toleranceBeforeDrag >= 0) { + m_ui->tolerance->setValue(m_toleranceBeforeDrag); + m_toleranceBeforeDrag = -1; + } +} + void FillSettings::updateWidgets() { if(m_stack) { diff --git a/src/desktop/toolwidgets/fillsettings.h b/src/desktop/toolwidgets/fillsettings.h index 7023252715..320ff70791 100644 --- a/src/desktop/toolwidgets/fillsettings.h +++ b/src/desktop/toolwidgets/fillsettings.h @@ -62,6 +62,7 @@ public slots: QWidget *createUiWidget(QWidget *parent) override; private: + void updateTolerance(); void updateSettings(); void updateSize(); @@ -72,6 +73,7 @@ public slots: void selectBlendMode(int blendMode); void setButtonState(bool running, bool pending); + void setDragState(bool dragging, int tolerance); void updateWidgets(); QWidget *m_headerWidget = nullptr; @@ -84,6 +86,7 @@ public slots: QButtonGroup *m_areaGroup = nullptr; int m_previousMode; int m_previousEraseMode; + int m_toleranceBeforeDrag = -1; qreal m_quickAdjust1 = 0.0; bool m_featureAccess = true; bool m_haveSelection = false; diff --git a/src/desktop/toolwidgets/selectionsettings.cpp b/src/desktop/toolwidgets/selectionsettings.cpp index 15d632b68e..38b8f1522d 100644 --- a/src/desktop/toolwidgets/selectionsettings.cpp +++ b/src/desktop/toolwidgets/selectionsettings.cpp @@ -137,8 +137,7 @@ void SelectionSettings::pushSettings() int size = m_sizeSlider->value(); selectionParams.size = isSizeUnlimited(size) ? -1 : size; selectionParams.opacity = m_opacitySlider->value() / 100.0; - selectionParams.tolerance = - m_toleranceSlider->value() / qreal(m_toleranceSlider->maximum()); + selectionParams.tolerance = m_toleranceSlider->value(); selectionParams.expansion = m_expandShrink->effectiveValue(); selectionParams.featherRadius = m_featherSlider->value(); selectionParams.gap = m_closeGapsSlider->value(); @@ -268,7 +267,7 @@ QWidget *SelectionSettings::createUiWidget(QWidget *parent) m_toleranceSlider->setBlockUpdateSignalOnDrag(true); connect( m_toleranceSlider, QOverload::of(&KisSliderSpinBox::valueChanged), - this, &SelectionSettings::pushSettings); + this, &SelectionSettings::updateTolerance); magicWandLayout->addRow(m_toleranceSlider); m_expandShrink = new widgets::ExpandShrinkSpinner; @@ -420,6 +419,10 @@ QWidget *SelectionSettings::createUiWidget(QWidget *parent) setActiveTool(tools::Tool::SELECTION); updateSize(m_sizeSlider->value()); + connect( + controller(), &ToolController::magicWandDragChanged, this, + &SelectionSettings::setDragState); + return widget; } @@ -433,6 +436,13 @@ void SelectionSettings::updateSize(int size) emit pixelSizeChanged(calculatePixelSize(size, unlimited)); } +void SelectionSettings::updateTolerance() +{ + if(m_toleranceBeforeDrag < 0) { + pushSettings(); + } +} + bool SelectionSettings::isSizeUnlimited(int size) { return size >= props::size.max; @@ -443,4 +453,17 @@ int SelectionSettings::calculatePixelSize(int size, bool unlimited) return unlimited ? 0 : size * 2 + 1; } +void SelectionSettings::setDragState(bool dragging, int tolerance) +{ + if(dragging) { + if(m_toleranceBeforeDrag < 0) { + m_toleranceBeforeDrag = m_toleranceSlider->value(); + } + m_toleranceSlider->setValue(tolerance); + } else if(m_toleranceBeforeDrag >= 0) { + m_toleranceSlider->setValue(m_toleranceBeforeDrag); + m_toleranceBeforeDrag = -1; + } +} + } diff --git a/src/desktop/toolwidgets/selectionsettings.h b/src/desktop/toolwidgets/selectionsettings.h index 5d3fbdc1ad..1278e15913 100644 --- a/src/desktop/toolwidgets/selectionsettings.h +++ b/src/desktop/toolwidgets/selectionsettings.h @@ -60,9 +60,12 @@ public slots: enum Area { Continuous, Similar }; void updateSize(int size); + void updateTolerance(); static bool isSizeUnlimited(int size); static int calculatePixelSize(int size, bool unlimited); + void setDragState(bool dragging, int tolerance); + QWidget *m_headerWidget = nullptr; QButtonGroup *m_headerGroup = nullptr; QWidget *m_selectionContainer = nullptr; @@ -77,6 +80,7 @@ public slots: QButtonGroup *m_sourceGroup = nullptr; QButtonGroup *m_areaGroup = nullptr; QPushButton *m_startTransformButton = nullptr; + int m_toleranceBeforeDrag = -1; bool m_isMagicWand = false; }; diff --git a/src/libclient/tools/clickdetector.h b/src/libclient/tools/clickdetector.h index 6dac45372c..3d248af372 100644 --- a/src/libclient/tools/clickdetector.h +++ b/src/libclient/tools/clickdetector.h @@ -10,7 +10,11 @@ namespace tools { +class DragDetector; + class ClickDetector final { + friend class DragDetector; + public: void begin(const QPointF &viewPos, DeviceType deviceType) { @@ -43,8 +47,8 @@ class ClickDetector final { m_clicks = 0; } - bool isClick() { return m_clicks != 0; } - int clicks() { return m_clicks; } + bool isClick() const { return m_clicks != 0; } + int clicks() const { return m_clicks; } private: static constexpr int CLICK_TIME = 150; @@ -56,7 +60,7 @@ class ClickDetector final { QGuiApplication::styleHints()->mouseDoubleClickInterval()); } - qreal getClickDistance(DeviceType deviceType) + static qreal getClickDistance(DeviceType deviceType) { int distance; switch(deviceType) { @@ -84,6 +88,34 @@ class ClickDetector final { unsigned int m_clicks = 0; }; +class DragDetector final { +public: + void begin(const QPointF &viewPos, DeviceType deviceType) + { + m_clickDistance = ClickDetector::getClickDistance(deviceType); + m_timer.setRemainingTime(ClickDetector::CLICK_TIME); + m_startViewPos = viewPos; + m_dragThresholdHit = false; + } + + void motion(const QPointF &viewPos) + { + if(!m_dragThresholdHit && !m_timer.hasExpired()) { + if(QLineF(m_startViewPos, viewPos).length() > m_clickDistance) { + m_dragThresholdHit = true; + } + } + } + + bool isDrag() const { return m_dragThresholdHit || m_timer.hasExpired(); } + +private: + QDeadlineTimer m_timer; + qreal m_clickDistance = 0.0; + QPointF m_startViewPos; + bool m_dragThresholdHit = false; +}; + } #endif diff --git a/src/libclient/tools/floodfill.cpp b/src/libclient/tools/floodfill.cpp index 3bd7555615..0875aac32a 100644 --- a/src/libclient/tools/floodfill.cpp +++ b/src/libclient/tools/floodfill.cpp @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include "libclient/tools/floodfill.h" -#include "libclient/canvas/blendmodes.h" +extern "C" { +#include +} #include "libclient/canvas/canvasmodel.h" #include "libclient/canvas/layerlist.h" #include "libclient/canvas/paintengine.h" #include "libclient/net/client.h" +#include "libclient/tools/floodfill.h" #include "libclient/tools/toolcontroller.h" #include #include @@ -21,7 +23,7 @@ class FloodFill::Task final : public ToolController::Task { int sourceLayerId, int size, int gap, int expansion, DP_FloodFillKernel kernel, int featherRadius, Area area, DP_ViewMode viewMode, int activeLayerId, int activeFrameIndex, - bool editable) + bool editable, bool dragging) : m_tool(tool) , m_cancel(cancel) , m_canvasState(canvasState) @@ -40,6 +42,7 @@ class FloodFill::Task final : public ToolController::Task { , m_activeLayerId(activeLayerId) , m_activeFrameIndex(activeFrameIndex) , m_editable(editable) + , m_dragging(dragging) { } @@ -60,9 +63,9 @@ class FloodFill::Task final : public ToolController::Task { m_contextId, canvas::CanvasModel::MAIN_SELECTION_ID, m_point.x(), m_point.y(), m_fillColor, m_tolerance, m_sourceLayerId, size, continuous ? m_gap : 0, m_expansion, - m_kernel, m_featherRadius, false, continuous, !m_editable, - m_viewMode, m_activeLayerId, m_activeFrameIndex, m_cancel, - m_img, m_x, m_y); + m_kernel, m_featherRadius, false, continuous, + !m_editable && !m_dragging, m_viewMode, m_activeLayerId, + m_activeFrameIndex, m_cancel, m_img, m_x, m_y); } if(m_result != DP_FLOOD_FILL_SUCCESS && m_result != DP_FLOOD_FILL_CANCELLED) { @@ -104,6 +107,7 @@ class FloodFill::Task final : public ToolController::Task { int m_y; QString m_error; const bool m_editable; + const bool m_dragging; }; FloodFill::FloodFill(ToolController &owner) @@ -123,6 +127,7 @@ FloodFill::FloodFill(ToolController &owner) void FloodFill::begin(const BeginParams ¶ms) { + stopDragging(); if(params.right) { cancelMultipart(); } else if(!m_running) { @@ -132,6 +137,7 @@ void FloodFill::begin(const BeginParams ¶ms) flushPending(); } if(shouldFill) { + m_dragDetector.begin(params.viewPos, params.deviceType); fillAt(params.point, m_owner.activeLayer(), m_editableFills); } } @@ -139,10 +145,40 @@ void FloodFill::begin(const BeginParams ¶ms) void FloodFill::motion(const MotionParams ¶ms) { - Q_UNUSED(params); + m_dragDetector.motion(params.viewPos); + if(m_dragDetector.isDrag()) { + if(m_dragging) { + qreal delta = + qRound((params.viewPos.x() - m_dragPrevPoint.x()) / 2.0); + m_dragPrevPoint = params.viewPos; + + int prevDragTolerance = qRound(m_dragTolerance); + m_dragTolerance = qBound(0.0, m_dragTolerance + delta, 255.0); + int dragTolerance = qRound(m_dragTolerance); + + if(dragTolerance != prevDragTolerance) { + if(m_running) { + m_repeat = true; + m_cancel = true; + } else if(havePending()) { + fillAt(m_lastPoint, lastActiveLayerId(), m_pendingEditable); + } + emit m_owner.floodFillDragChanged(true, dragTolerance); + } + } else { + m_dragging = true; + m_dragPrevPoint = params.viewPos; + m_dragTolerance = m_tolerance; + emit m_owner.floodFillDragChanged(true, m_tolerance); + } + updateCursor(); + } } -void FloodFill::end(const EndParams &) {} +void FloodFill::end(const EndParams &) +{ + stopDragging(); +} bool FloodFill::isMultipart() const { @@ -187,7 +223,7 @@ void FloodFill::setForegroundColor(const QColor &color) } void FloodFill::setParameters( - qreal tolerance, int expansion, int kernel, int featherRadius, int size, + int tolerance, int expansion, int kernel, int featherRadius, int size, qreal opacity, int gap, Source source, int blendMode, Area area, bool editableFills, bool confirmFills) { @@ -200,9 +236,7 @@ void FloodFill::setParameters( if(confirmFills != m_confirmFills) { m_confirmFills = confirmFills; - if(havePending()) { - setCursor(confirmFills ? m_confirmCursor : m_pendingCursor); - } + updateCursor(); } if(needsUpdate) { @@ -232,6 +266,15 @@ int FloodFill::lastActiveLayerId() const : m_owner.activeLayer(); } +void FloodFill::stopDragging() +{ + if(m_dragging) { + m_dragging = false; + updateCursor(); + emit m_owner.floodFillDragChanged(false, 0); + } +} + void FloodFill::fillAt(const QPointF &point, int activeLayerId, bool editable) { m_repeat = false; @@ -275,10 +318,12 @@ void FloodFill::fillAt(const QPointF &point, int activeLayerId, bool editable) QCoreApplication::translate("FillSettings", "Filling…")); m_owner.executeAsync(new Task( this, m_cancel, paintEngine->viewCanvasState(), - canvas->localUserId(), point, fillColor, m_tolerance, layerId, - m_size, m_gap, m_expansion, DP_FloodFillKernel(m_kernel), + canvas->localUserId(), point, fillColor, + (m_dragging ? qRound(m_dragTolerance) : m_tolerance) / 255.0, + layerId, m_size, m_gap, m_expansion, DP_FloodFillKernel(m_kernel), m_featherRadius, m_area, paintEngine->viewMode(), - paintEngine->viewLayer(), paintEngine->viewFrame(), editable)); + paintEngine->viewLayer(), paintEngine->viewFrame(), editable, + m_dragging)); } } @@ -308,7 +353,7 @@ void FloodFill::floodFillFinished(Task *task) m_pendingArea = task->area(); m_pendingColor = task->color(); m_pendingEditable = task->isEditable(); - setCursor(m_confirmFills ? m_confirmCursor : m_pendingCursor); + updateCursor(); } } else if(result != DP_FLOOD_FILL_CANCELLED) { qWarning("Flood fill failed: %s", qUtf8Printable(task->error())); @@ -415,7 +460,7 @@ void FloodFill::disposePending() m_pendingImage = QImage(); previewPending(); setHandlesRightClick(false); - setCursor(m_bucketCursor); + updateCursor(); emitFloodFillStateChanged(); } } @@ -443,6 +488,17 @@ void FloodFill::adjustPendingImage(bool adjustColor, bool adjustOpacity) } } +void FloodFill::updateCursor() +{ + if(m_dragging) { + setCursor(Qt::SplitHCursor); + } else if(havePending()) { + setCursor(m_confirmFills ? m_confirmCursor : m_pendingCursor); + } else { + setCursor(m_bucketCursor); + } +} + void FloodFill::emitFloodFillStateChanged() { emit m_owner.floodFillStateChanged(m_running, havePending()); diff --git a/src/libclient/tools/floodfill.h b/src/libclient/tools/floodfill.h index e42be9abee..70e531eaeb 100644 --- a/src/libclient/tools/floodfill.h +++ b/src/libclient/tools/floodfill.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #ifndef TOOLS_FLOODFILL_H #define TOOLS_FLOODFILL_H +#include "libclient/tools/clickdetector.h" #include "libclient/tools/tool.h" #include #include @@ -39,7 +40,7 @@ class FloodFill final : public Tool { void setForegroundColor(const QColor &color) override; void setParameters( - qreal tolerance, int expansion, int kernel, int featherRadius, int size, + int tolerance, int expansion, int kernel, int featherRadius, int size, qreal opacity, int gap, Source source, int blendMode, Area area, bool editableFills, bool confirmFills); @@ -49,6 +50,7 @@ class FloodFill final : public Tool { int lastActiveLayerId() const; + void stopDragging(); void fillAt(const QPointF &point, int activeLayerId, bool editable); void repeatFill(); void floodFillFinished(Task *task); @@ -61,9 +63,11 @@ class FloodFill final : public Tool { void adjustPendingImage(bool adjustColor, bool adjustOpacity); + void updateCursor(); + void emitFloodFillStateChanged(); - qreal m_tolerance = 0.01; + int m_tolerance = 0; int m_expansion = 0; int m_kernel; int m_featherRadius = 0; @@ -78,8 +82,11 @@ class FloodFill final : public Tool { bool m_running = false; bool m_repeat = false; bool m_pendingEditable = false; + bool m_dragging = false; QAtomicInt m_cancel = false; QPointF m_lastPoint; + QPointF m_dragPrevPoint; + qreal m_dragTolerance = 0.0; int m_lastActiveLayerId = 0; QImage m_pendingImage; QPoint m_pendingPos; @@ -91,6 +98,7 @@ class FloodFill final : public Tool { QCursor m_bucketCursor; QCursor m_pendingCursor; QCursor m_confirmCursor; + DragDetector m_dragDetector; }; } diff --git a/src/libclient/tools/magicwand.cpp b/src/libclient/tools/magicwand.cpp index 63a7be4621..309ae930b4 100644 --- a/src/libclient/tools/magicwand.cpp +++ b/src/libclient/tools/magicwand.cpp @@ -88,27 +88,67 @@ MagicWandTool::MagicWandTool(ToolController &owner) : Tool( owner, MAGICWAND, QCursor(QPixmap(":cursors/magicwand.png"), 2, 2), true, false, false, false, false) + , m_wandCursor(cursor()) { } void MagicWandTool::begin(const BeginParams ¶ms) { + stopDragging(); if(params.right) { + m_held = false; cancelMultipart(); } else if(!m_running) { if(havePending()) { flushPending(); } + m_held = true; + m_dragDetector.begin(params.viewPos, params.deviceType); fillAt(params.point, params.constrain, params.center); } } void MagicWandTool::motion(const MotionParams ¶ms) { - Q_UNUSED(params); + m_dragDetector.motion(params.viewPos); + if(m_dragDetector.isDrag()) { + if(m_dragging) { + qreal delta = + qRound((params.viewPos.x() - m_dragPrevPoint.x()) / 2.0); + m_dragPrevPoint = params.viewPos; + + int prevDragTolerance = qRound(m_dragTolerance); + m_dragTolerance = qBound(0.0, m_dragTolerance + delta, 255.0); + int dragTolerance = qRound(m_dragTolerance); + + if(dragTolerance != prevDragTolerance) { + if(m_running) { + m_repeat = true; + m_cancel = true; + } else if(havePending()) { + fillAt(m_lastPoint, params.constrain, params.center); + } + emit m_owner.magicWandDragChanged(true, dragTolerance); + } + } else { + m_dragging = true; + m_dragPrevPoint = params.viewPos; + int tolerance = m_owner.selectionParams().tolerance; + m_dragTolerance = tolerance; + setCursor(Qt::SplitHCursor); + emit m_owner.magicWandDragChanged(true, tolerance); + } + } } -void MagicWandTool::end(const EndParams &) {} +void MagicWandTool::end(const EndParams &) +{ + m_held = false; + stopDragging(); + if(!m_running && !m_repeat) { + flushPending(); + } +} bool MagicWandTool::isMultipart() const { @@ -162,6 +202,15 @@ void MagicWandTool::updateParameters() } } +void MagicWandTool::stopDragging() +{ + if(m_dragging) { + m_dragging = false; + setCursor(m_wandCursor); + emit m_owner.magicWandDragChanged(false, 0); + } +} + void MagicWandTool::fillAt(const QPointF &point, bool constrain, bool center) { m_repeat = false; @@ -199,8 +248,9 @@ void MagicWandTool::fillAt(const QPointF &point, bool constrain, bool center) canvas::PaintEngine *paintEngine = canvas->paintEngine(); m_owner.executeAsync(new Task( this, m_cancel, paintEngine->viewCanvasState(), point, - selectionParams.size, selectionParams.tolerance, layerId, - selectionParams.gap, selectionParams.expansion, + selectionParams.size, + (m_dragging ? m_dragTolerance : selectionParams.tolerance) / 255.0, + layerId, selectionParams.gap, selectionParams.expansion, DP_FloodFillKernel(selectionParams.kernel), selectionParams.featherRadius, selectionParams.continuous, activeLayerId, paintEngine->viewMode(), paintEngine->viewLayer(), @@ -239,7 +289,7 @@ void MagicWandTool::floodFillFinished(Task *task) setHandlesRightClick(havePending()); if(m_repeat) { fillAt(m_lastPoint, m_lastConstrain, m_lastCenter); - } else if(!EDITABLE) { + } else if(!m_held) { flushPending(); } } diff --git a/src/libclient/tools/magicwand.h b/src/libclient/tools/magicwand.h index 8bb54f1ca2..7f9e328019 100644 --- a/src/libclient/tools/magicwand.h +++ b/src/libclient/tools/magicwand.h @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later #ifndef LIBCLIENT_TOOLS_MAGICWAND_H #define LIBCLIENT_TOOLS_MAGICWAND_H +#include "libclient/tools/clickdetector.h" #include "libclient/tools/tool.h" #include "libclient/tools/toolcontroller.h" #include #include +#include namespace tools { @@ -33,6 +35,7 @@ class MagicWandTool final : public Tool { class Task; friend Task; + void stopDragging(); void fillAt(const QPointF &point, bool constrain, bool center); void repeatFill(); void floodFillFinished(Task *task); @@ -47,13 +50,19 @@ class MagicWandTool final : public Tool { int m_op = -1; bool m_running = false; bool m_repeat = false; + bool m_held = false; + bool m_dragging = false; QAtomicInt m_cancel; QPointF m_lastPoint; + QPointF m_dragPrevPoint; + qreal m_dragTolerance = 0.0; bool m_lastConstrain = false; bool m_lastCenter = false; ToolController::SelectionParams m_pendingParams; QImage m_pendingImage; QPoint m_pendingPos; + QCursor m_wandCursor; + DragDetector m_dragDetector; }; } diff --git a/src/libclient/tools/toolcontroller.h b/src/libclient/tools/toolcontroller.h index 40fb176989..e0381f08c7 100644 --- a/src/libclient/tools/toolcontroller.h +++ b/src/libclient/tools/toolcontroller.h @@ -53,7 +53,7 @@ class ToolController final : public QObject { int defaultOp = 0; int size = -1; qreal opacity = 1.0; - qreal tolerance = 0.0; + int tolerance = 0; int expansion = 0; int featherRadius = 0; int gap = 0; @@ -233,6 +233,8 @@ public slots: void deleteAnnotationRequested(int annotationId); void floodFillStateChanged(bool running, bool pending); + void floodFillDragChanged(bool dragging, int tolerance); + void magicWandDragChanged(bool dragging, int tolerance); void toolStateChanged(int state); void asyncExecutionFinished(Task *task);