diff --git a/app/src/generalpage.cpp b/app/src/generalpage.cpp index ff0406835..d6403eb3f 100644 --- a/app/src/generalpage.cpp +++ b/app/src/generalpage.cpp @@ -103,7 +103,7 @@ GeneralPage::GeneralPage() : ui(new Ui::GeneralPage) connect(ui->antialiasingBox, &QCheckBox::stateChanged, this, &GeneralPage::antiAliasCheckboxStateChanged); connect(ui->curveSmoothingLevel, &QSlider::valueChanged, this, &GeneralPage::curveSmoothingChanged); connect(ui->highResBox, &QCheckBox::stateChanged, this, &GeneralPage::highResCheckboxStateChanged); - connect(ui->dottedCursorBox, &QCheckBox::stateChanged, this, &GeneralPage::dottedCursorCheckboxStateChanged); + connect(ui->canvasCursorBox, &QCheckBox::stateChanged, this, &GeneralPage::canvasCursorCheckboxStateChanged); connect(ui->gridSizeInputW, spinValueChanged, this, &GeneralPage::gridWidthChanged); connect(ui->gridSizeInputH, spinValueChanged, this, &GeneralPage::gridHeightChanged); connect(ui->actionSafeCheckBox, &QCheckBox::stateChanged, this, &GeneralPage::actionSafeCheckBoxStateChanged); @@ -141,8 +141,8 @@ void GeneralPage::updateValues() ui->toolCursorsBox->setChecked(mManager->isOn(SETTING::TOOL_CURSOR)); QSignalBlocker b5(ui->antialiasingBox); ui->antialiasingBox->setChecked(mManager->isOn(SETTING::ANTIALIAS)); - QSignalBlocker b6(ui->dottedCursorBox); - ui->dottedCursorBox->setChecked(mManager->isOn(SETTING::DOTTED_CURSOR)); + QSignalBlocker b6(ui->canvasCursorBox); + ui->canvasCursorBox->setChecked(mManager->isOn(SETTING::CANVAS_CURSOR)); QSignalBlocker b7(ui->gridSizeInputW); ui->gridSizeInputW->setValue(mManager->getInt(SETTING::GRID_SIZE_W)); QSignalBlocker b11(ui->gridSizeInputH); @@ -233,9 +233,9 @@ void GeneralPage::toolCursorsCheckboxStateChanged(int b) mManager->set(SETTING::TOOL_CURSOR, b != Qt::Unchecked); } -void GeneralPage::dottedCursorCheckboxStateChanged(int b) +void GeneralPage::canvasCursorCheckboxStateChanged(int b) { - mManager->set(SETTING::DOTTED_CURSOR, b != Qt::Unchecked); + mManager->set(SETTING::CANVAS_CURSOR, b != Qt::Unchecked); } void GeneralPage::gridWidthChanged(int value) diff --git a/app/src/generalpage.h b/app/src/generalpage.h index 239e6b8b9..0767090e0 100644 --- a/app/src/generalpage.h +++ b/app/src/generalpage.h @@ -51,7 +51,7 @@ private slots: void shadowsCheckboxStateChanged(int b); void antiAliasCheckboxStateChanged(int b); void toolCursorsCheckboxStateChanged(int b); - void dottedCursorCheckboxStateChanged(int b); + void canvasCursorCheckboxStateChanged(int b); void highResCheckboxStateChanged(int b); void gridCheckBoxStateChanged(int b); void curveSmoothingChanged(int value); diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index da3d6ee2b..480ff2e38 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -999,7 +999,6 @@ void MainWindow2::preferences() { clearKeyboardShortcuts(); setupKeyboardShortcuts(); - ui->scribbleArea->updateCanvasCursor(); mPrefDialog = nullptr; }); diff --git a/app/src/tooloptionwidget.cpp b/app/src/tooloptionwidget.cpp index 5c51c4206..64a45ca6e 100644 --- a/app/src/tooloptionwidget.cpp +++ b/app/src/tooloptionwidget.cpp @@ -27,6 +27,7 @@ GNU General Public License for more details. #include "util.h" #include "layer.h" #include "layermanager.h" +#include "stroketool.h" #include "toolmanager.h" ToolOptionWidget::ToolOptionWidget(QWidget* parent) : BaseDockWidget(parent) @@ -53,11 +54,11 @@ void ToolOptionWidget::initUI() QSettings settings(PENCIL2D, PENCIL2D); - ui->sizeSlider->init(tr("Width"), SpinSlider::EXPONENT, SpinSlider::INTEGER, 1, 200); + ui->sizeSlider->init(tr("Width"), SpinSlider::EXPONENT, SpinSlider::INTEGER, StrokeTool::WIDTH_MIN, StrokeTool::WIDTH_MAX); ui->sizeSlider->setValue(settings.value("brushWidth", "3").toDouble()); ui->brushSpinBox->setValue(settings.value("brushWidth", "3").toDouble()); - ui->featherSlider->init(tr("Feather"), SpinSlider::LOG, SpinSlider::INTEGER, 1, 99); + ui->featherSlider->init(tr("Feather"), SpinSlider::LOG, SpinSlider::INTEGER, StrokeTool::FEATHER_MIN, StrokeTool::FEATHER_MAX); ui->featherSlider->setValue(settings.value("brushFeather", "5").toDouble()); ui->featherSpinBox->setValue(settings.value("brushFeather", "5").toDouble()); } diff --git a/app/ui/generalpage.ui b/app/ui/generalpage.ui index 5586b0b6b..14229078f 100644 --- a/app/ui/generalpage.ui +++ b/app/ui/generalpage.ui @@ -115,9 +115,9 @@ - + - Dotted Cursor + Canvas Cursor @@ -515,7 +515,7 @@ windowOpacityLevel shadowsBox toolCursorsBox - dottedCursorBox + canvasCursorBox checkerBackgroundButton whiteBackgroundButton greyBackgroundButton diff --git a/core_lib/core_lib.pro b/core_lib/core_lib.pro index d1725c897..4b0642ecf 100644 --- a/core_lib/core_lib.pro +++ b/core_lib/core_lib.pro @@ -26,6 +26,7 @@ INCLUDEPATH += src \ PRECOMPILED_HEADER = src/corelib-pch.h HEADERS += \ + src/canvascursorpainter.h \ src/corelib-pch.h \ src/graphics/bitmap/bitmapbucket.h \ src/graphics/bitmap/bitmapimage.h \ @@ -115,6 +116,7 @@ HEADERS += \ SOURCES += src/graphics/bitmap/bitmapimage.cpp \ + src/canvascursorpainter.cpp \ src/graphics/bitmap/bitmapbucket.cpp \ src/graphics/bitmap/tile.cpp \ src/graphics/bitmap/tiledbuffer.cpp \ diff --git a/core_lib/src/canvascursorpainter.cpp b/core_lib/src/canvascursorpainter.cpp new file mode 100644 index 000000000..3118d7e55 --- /dev/null +++ b/core_lib/src/canvascursorpainter.cpp @@ -0,0 +1,97 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ +#include "canvascursorpainter.h" + +#include + +CanvasCursorPainter::CanvasCursorPainter() +{ + setupPen(); +} + +void CanvasCursorPainter::setupPen() +{ + mCursorPen = QPen(Qt::gray); + mCursorPen.setWidthF(1); + mCursorPen.setCosmetic(true); +} + +void CanvasCursorPainter::paint(QPainter& painter, const QRect& blitRect) +{ + if (mOptions.isAdjusting || mOptions.showCursor) { + if (mOptions.useFeather) { + paintFeatherCursor(painter, blitRect, mOptions.widthRect, mOptions.featherRect); + } + paintWidthCursor(painter, blitRect, mOptions.widthRect); + mIsDirty = true; + } +} + +void CanvasCursorPainter::preparePainter(const CanvasCursorPainterOptions& painterOptions, const QTransform& viewTransform) +{ + mOptions = painterOptions; + if (mOptions.isAdjusting || mOptions.showCursor) { + mOptions.widthRect = viewTransform.mapRect(mOptions.widthRect); + mOptions.featherRect = viewTransform.mapRect(mOptions.featherRect); + } +} + +void CanvasCursorPainter::paintFeatherCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds, const QRectF& featherCircleBounds) +{ + // When the circles are too close to each other, the rendering will appear dotted or almost + // invisible at certain zoom levels. + if (widthCircleBounds.width() - featherCircleBounds.width() <= 1) { + return; + } + + painter.save(); + + painter.setClipRect(blitRect); + painter.setPen(mCursorPen); + painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination); + painter.drawEllipse(featherCircleBounds); + + painter.restore(); +} + +void CanvasCursorPainter::paintWidthCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds) +{ + painter.save(); + + painter.setClipRect(blitRect); + painter.setPen(mCursorPen); + + painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination); + + // Only draw the cross when the width is bigger than the cross itself + if (widthCircleBounds.width() > 8) { + const QPointF& pos = widthCircleBounds.center(); + painter.drawLine(QPointF(pos.x() - 2, pos.y()), QPointF(pos.x() + 2, pos.y())); + painter.drawLine(QPointF(pos.x(), pos.y() - 2), QPointF(pos.x(), pos.y() + 2)); + } + + painter.drawEllipse(widthCircleBounds); + painter.restore(); + + mDirtyRect = widthCircleBounds.toAlignedRect(); +} + +void CanvasCursorPainter::clearDirty() +{ + mDirtyRect = QRect(); + mIsDirty = false; +} diff --git a/core_lib/src/canvascursorpainter.h b/core_lib/src/canvascursorpainter.h new file mode 100644 index 000000000..9d8ad7412 --- /dev/null +++ b/core_lib/src/canvascursorpainter.h @@ -0,0 +1,61 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ +#ifndef CANVASCURSORPAINTER_H +#define CANVASCURSORPAINTER_H + +#include + +class QPainter; + +struct CanvasCursorPainterOptions +{ + QRectF widthRect; + QRectF featherRect; + bool isAdjusting; + bool useFeather = false; + bool showCursor = false; +}; + +class CanvasCursorPainter +{ + +public: + CanvasCursorPainter(); + void paint(QPainter& painter, const QRect& blitRect); + + void preparePainter(const CanvasCursorPainterOptions& painterOptions, const QTransform& viewTransform); + + const QRect dirtyRect() { return mDirtyRect; } + bool isDirty() const { return mIsDirty; } + void clearDirty(); + +private: + + void setupPen(); + + /// @brief precision circular cursor: used for drawing a cursor on the canvas. + void paintWidthCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds); + void paintFeatherCursor(QPainter& painter, const QRect& blitRect, const QRectF& widthCircleBounds, const QRectF& featherCircleBounds); + + CanvasCursorPainterOptions mOptions; + QRect mDirtyRect; + bool mIsDirty = false; + + QPen mCursorPen; +}; + +#endif // CANVASCURSORPAINTER_H diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 5d4929e64..1009625e0 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -48,16 +48,15 @@ Tile* TiledBuffer::getTileFromIndex(const TileIndex& tileIndex) return selectedTile; } -void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { +void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { const QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth); const float tileSize = UNIFORM_TILE_SIZE; - const int width = qMax(brushCursorWidth,brushWidth); // Gather the number of tiles that fits the size of the brush width - const int xLeft = qFloor((qFloor(point.x() - width)) / tileSize); - const int xRight = qFloor((qFloor(point.x() + width)) / tileSize); - const int yTop = qFloor(qFloor(point.y() - width) / tileSize); - const int yBottom = qFloor(qFloor(point.y() + width) / tileSize); + const int xLeft = qFloor((qFloor(point.x() - brushWidth)) / tileSize); + const int xRight = qFloor((qFloor(point.x() + brushWidth)) / tileSize); + const int yTop = qFloor(qFloor(point.y() - brushWidth) / tileSize); + const int yBottom = qFloor(qFloor(point.y() + brushWidth) / tileSize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { @@ -108,11 +107,10 @@ void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPain } -void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, +void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { - const int pathWidth = pen.width(); - const int width = (qMax(pathWidth,cursorWidth) + 1); + const int width = pen.width();; const float tileSize = UNIFORM_TILE_SIZE; const QRectF pathRect = path.boundingRect(); diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 3e29cfed8..642492666 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -59,9 +59,9 @@ class TiledBuffer: public QObject bool isValid() const { return !mTiles.isEmpty(); } /** Draws a brush with the specified parameters to the tiled buffer */ - void drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); + void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); /** Draws a path with the specified parameters to the tiled buffer */ - void drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, + void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); /** Draws a image with the specified parameters to the tiled buffer */ void drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index d39359c02..143983cb0 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -45,7 +45,9 @@ GNU General Public License for more details. #include "selectionmanager.h" #include "overlaymanager.h" -ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), mCanvasPainter(mCanvas), mCameraPainter(mCanvas) +ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), + mCanvasPainter(mCanvas), + mCameraPainter(mCanvas) { setObjectName("ScribbleArea"); @@ -85,7 +87,6 @@ bool ScribbleArea::init() const int curveSmoothingLevel = mPrefs->getInt(SETTING::CURVE_SMOOTHING); mCurveSmoothingLevel = curveSmoothingLevel / 20.0; // default value is 1.0 - mQuickSizing = mPrefs->isOn(SETTING::QUICK_SIZING); mMakeInvisible = false; mMultiLayerOnionSkin = mPrefs->isOn(SETTING::MULTILAYER_ONION); @@ -94,8 +95,6 @@ bool ScribbleArea::init() mDeltaFactor = mEditor->preference()->isOn(SETTING::INVERT_SCROLL_ZOOM_DIRECTION) ? -1 : 1; - updateCanvasCursor(); - setMouseTracking(true); // reacts to mouse move events, even if the button is not pressed #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) setTabletTracking(true); // tablet tracking first added in 5.9 @@ -151,9 +150,6 @@ void ScribbleArea::settingUpdated(SETTING setting) case SETTING::ONION_WHILE_PLAYBACK: invalidateAllCache(); break; - case SETTING::QUICK_SIZING: - mQuickSizing = mPrefs->isOn(SETTING::QUICK_SIZING); - break; case SETTING::MULTILAYER_ONION: mMultiLayerOnionSkin = mPrefs->isOn(SETTING::MULTILAYER_ONION); invalidateAllCache(); @@ -174,7 +170,6 @@ void ScribbleArea::settingUpdated(SETTING setting) void ScribbleArea::updateToolCursor() { setCursor(currentTool()->cursor()); - updateCanvasCursor(); } void ScribbleArea::setCurveSmoothing(int newSmoothingLevel) @@ -388,11 +383,15 @@ void ScribbleArea::onObjectLoaded() bool ScribbleArea::event(QEvent *event) { + bool processed = false; if (event->type() == QEvent::WindowDeactivate) { editor()->tools()->clearTemporaryTool(); + processed = true; } - return QWidget::event(event); + + processed = currentTool()->event(event) || processed; + return QWidget::event(event) || processed; } /************************************************************************/ @@ -569,7 +568,6 @@ void ScribbleArea::wheelEvent(QWheelEvent* event) const qreal newScale = currentScale * std::pow(100, (delta * mDeltaFactor) / (12.0 * 120)); mEditor->view()->scaleAtOffset(newScale, offset); } - updateCanvasCursor(); event->accept(); } @@ -590,7 +588,7 @@ void ScribbleArea::tabletEvent(QTabletEvent *e) editor()->tools()->tabletRestorePrevTool(); } - if (event.eventType() == QTabletEvent::TabletPress) + if (event.eventType() == PointerEvent::Press) { event.accept(); mStrokeManager->pointerPressEvent(&event); @@ -616,7 +614,7 @@ void ScribbleArea::tabletEvent(QTabletEvent *e) } mTabletInUse = event.isAccepted(); } - else if (event.eventType() == QTabletEvent::TabletMove) + else if (event.eventType() == PointerEvent::Move) { if (!(event.buttons() & (Qt::LeftButton | Qt::RightButton)) || mTabletInUse) { @@ -624,7 +622,7 @@ void ScribbleArea::tabletEvent(QTabletEvent *e) pointerMoveEvent(&event); } } - else if (event.eventType() == QTabletEvent::TabletRelease) + else if (event.eventType() == PointerEvent::Release) { mTabletReleaseMillisAgo = 0; mMouseFilterTimer->start(); @@ -673,18 +671,7 @@ void ScribbleArea::pointerPressEvent(PointerEvent* event) editor()->tools()->setTemporaryTool(HAND, event->buttons())) { currentTool()->pointerPressEvent(event); - } - - const bool isPressed = event->buttons() & Qt::LeftButton; - if (isPressed && mQuickSizing) - { - if (currentTool()->startAdjusting(event->modifiers(), 1)) - { - return; - } - } - - if (event->button() == Qt::LeftButton) + } else if (event->button() == Qt::LeftButton) { currentTool()->pointerPressEvent(event); } @@ -692,31 +679,11 @@ void ScribbleArea::pointerPressEvent(PointerEvent* event) void ScribbleArea::pointerMoveEvent(PointerEvent* event) { - updateCanvasCursor(); - - if (event->buttons() & (Qt::LeftButton | Qt::RightButton)) - { - - // --- use SHIFT + drag to resize WIDTH / use CTRL + drag to resize FEATHER --- - if (currentTool()->isAdjusting()) - { - currentTool()->adjustCursor(event->modifiers()); - return; - } - } - currentTool()->pointerMoveEvent(event); } void ScribbleArea::pointerReleaseEvent(PointerEvent* event) { - if (currentTool()->isAdjusting()) - { - currentTool()->stopAdjusting(); - mEditor->tools()->setWidth(static_cast(currentTool()->properties.width)); - return; // [SHIFT]+drag OR [CTRL]+drag - } - currentTool()->pointerReleaseEvent(event); editor()->tools()->tryClearTemporaryTool(event->button()); @@ -878,58 +845,6 @@ void ScribbleArea::clearDrawingBuffer() mTiledBuffer.clear(); } -void ScribbleArea::paintCanvasCursor(QPainter& painter) -{ - QTransform view = mEditor->view()->getView(); - QPointF mousePos = currentTool()->isAdjusting() ? currentTool()->getCurrentPressPoint() : currentTool()->getCurrentPoint(); - int centerCal = mCursorImg.width() / 2; - - mTransformedCursorPos = view.map(mousePos); - - // reset matrix - view.reset(); - - painter.setTransform(view); - mCursorCenterPos.setX(centerCal); - mCursorCenterPos.setY(centerCal); - - painter.drawPixmap(QPoint(static_cast(mTransformedCursorPos.x() - mCursorCenterPos.x()), - static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())), - mCursorImg); - - mCursorCenterPos.setX(centerCal); - mCursorCenterPos.setY(centerCal); -} - -void ScribbleArea::updateCanvasCursor() -{ - float scalingFac = mEditor->view()->scaling(); - qreal brushWidth = currentTool()->properties.width; - qreal brushFeather = currentTool()->properties.feather; - if (currentTool()->isAdjusting()) - { - mCursorImg = currentTool()->quickSizeCursor(scalingFac); - } - else if (mEditor->preference()->isOn(SETTING::DOTTED_CURSOR)) - { - bool useFeather = currentTool()->properties.useFeather; - mCursorImg = currentTool()->canvasCursor(static_cast(brushWidth), static_cast(brushFeather), useFeather, scalingFac, width()); - } - else - { - mCursorImg = QPixmap(); // if the above does not comply, deallocate image - } - - // When we're using a tool, the TiledBuffer will take care of this; - // we don't want to cause needless updates - if (!currentTool()->isActive()) { - // update cursor rect - QPoint translatedPos(static_cast(mTransformedCursorPos.x() - mCursorCenterPos.x()), - static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())); - update(mCursorImg.rect().adjusted(-1, -1, 1, 1).translated(translatedPos)); - } -} - void ScribbleArea::handleDrawingOnEmptyFrame() { auto layer = mEditor->layers()->currentLayer(); @@ -1046,7 +961,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) painter.setClipRect(event->rect()); painter.drawPixmap(QPointF(), mCanvas); - currentTool()->paint(painter); + currentTool()->paint(painter, event->rect()); if (!editor()->playback()->isPlaying()) // we don't need to display the following when the animation is playing { @@ -1123,8 +1038,6 @@ void ScribbleArea::paintEvent(QPaintEvent* event) } } - paintCanvasCursor(painter); - mOverlayPainter.paint(painter, rect()); // paints the selection outline @@ -1271,13 +1184,13 @@ void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) { - mTiledBuffer.drawPath(mEditor->view()->mapScreenToCanvas(path), mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); + mTiledBuffer.drawPath(mEditor->view()->mapScreenToCanvas(path), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); } void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA) { // We use Source as opposed to SourceOver here to avoid the dabs being added on top of each other - mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_Source, useAA); + mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_Source, useAA); } void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) @@ -1298,7 +1211,7 @@ void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, { brush = QBrush(fillColor, Qt::SolidPattern); } - mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, brush, compMode, useAA); + mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, compMode, useAA); } void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) @@ -1314,7 +1227,7 @@ void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) blitRect.extend(updateRect); mTiledBuffer.clear(); - mTiledBuffer.drawPath(path, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); + mTiledBuffer.drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); // And update only the affected area update(blitRect.adjusted(-1, -1, 1, 1)); diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index c31ffca13..277f29d7d 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -174,7 +174,6 @@ public slots: void liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal offset_, qreal opacity_); void paintBitmapBuffer(); - void paintCanvasCursor(QPainter& painter); void clearDrawingBuffer(); void setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset); @@ -182,16 +181,11 @@ public slots: void pointerMoveEvent(PointerEvent*); void pointerReleaseEvent(PointerEvent*); - void updateCanvasCursor(); - /// Call this when starting to use a paint tool. Checks whether we are drawing /// on an empty frame, and if so, takes action according to use preference. void handleDrawingOnEmptyFrame(); TiledBuffer mTiledBuffer; - - QPixmap mCursorImg; - private: /** Invalidate the layer pixmap and camera painter caches. @@ -227,7 +221,6 @@ public slots: Editor* mEditor = nullptr; - bool mQuickSizing = true; LayerVisibility mLayerVisibility = LayerVisibility::ALL; bool mMakeInvisible = false; qreal mCurveSmoothingLevel = 0.0; @@ -258,9 +251,6 @@ public slots: QTimer* mMouseFilterTimer = nullptr; - QPoint mCursorCenterPos; - QPointF mTransformedCursorPos; - PreferenceManager* mPrefs = nullptr; QPixmap mCanvas; diff --git a/core_lib/src/managers/preferencemanager.cpp b/core_lib/src/managers/preferencemanager.cpp index 229881a8f..b889f3686 100644 --- a/core_lib/src/managers/preferencemanager.cpp +++ b/core_lib/src/managers/preferencemanager.cpp @@ -73,7 +73,7 @@ void PreferenceManager::loadPrefs() // General set(SETTING::ANTIALIAS, settings.value(SETTING_ANTIALIAS, true).toBool()); set(SETTING::TOOL_CURSOR, settings.value(SETTING_TOOL_CURSOR, true).toBool()); - set(SETTING::DOTTED_CURSOR, settings.value(SETTING_DOTTED_CURSOR, true).toBool()); + set(SETTING::CANVAS_CURSOR, settings.value(SETTING_CANVAS_CURSOR, true).toBool()); set(SETTING::HIGH_RESOLUTION, settings.value(SETTING_HIGH_RESOLUTION, true).toBool()); set(SETTING::SHADOW, settings.value(SETTING_SHADOW, false).toBool()); set(SETTING::QUICK_SIZING, settings.value(SETTING_QUICK_SIZING, true).toBool()); @@ -421,8 +421,8 @@ void PreferenceManager::set(SETTING option, bool value) case SETTING::TOOL_CURSOR: settings.setValue(SETTING_TOOL_CURSOR, value); break; - case SETTING::DOTTED_CURSOR: - settings.setValue(SETTING_DOTTED_CURSOR, value); + case SETTING::CANVAS_CURSOR: + settings.setValue(SETTING_CANVAS_CURSOR, value); break; case SETTING::HIGH_RESOLUTION: settings.setValue(SETTING_HIGH_RESOLUTION, value); diff --git a/core_lib/src/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index 14233ec79..b124b767d 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -112,6 +112,7 @@ void ToolManager::setCurrentTool(ToolType eToolType) } mCurrentTool = getTool(eToolType); + mCurrentTool->enteringThisTool(); if (mTemporaryTool == nullptr && mTabletEraserTool == nullptr) { emit toolChanged(eToolType); diff --git a/core_lib/src/tool/basetool.cpp b/core_lib/src/tool/basetool.cpp index d02496cb4..c59bd5077 100644 --- a/core_lib/src/tool/basetool.cpp +++ b/core_lib/src/tool/basetool.cpp @@ -18,20 +18,12 @@ GNU General Public License for more details. #include "basetool.h" #include -#include -#include #include "editor.h" #include "viewmanager.h" -#include "toolmanager.h" #include "scribblearea.h" #include "strokemanager.h" #include "pointerevent.h" -// ---- shared static variables ---- ( only one instance for all the tools ) -qreal BaseTool::msOriginalPropertyValue; // start value (width, feather ..) -bool BaseTool::msIsAdjusting = false; - - QString BaseTool::TypeName(ToolType type) { static std::array map; @@ -73,6 +65,15 @@ QCursor BaseTool::cursor() return Qt::ArrowCursor; } +bool BaseTool::leavingThisTool() +{ + for (auto& connection : mActiveConnections) { + disconnect(connection); + mActiveConnections.removeOne(connection); + } + return true; +} + void BaseTool::initialize(Editor* editor) { Q_ASSERT(editor); @@ -118,207 +119,11 @@ bool BaseTool::isDrawingTool() return true; } -/** - * @brief precision circular cursor: used for drawing a cursor within scribble area. - * @return QPixmap - */ -QPixmap BaseTool::canvasCursor(float width, float feather, bool useFeather, float scalingFac, int windowWidth) -{ - float propWidth = width * scalingFac; - float propFeather = feather * scalingFac; - - float cursorWidth = 0.0f; - float xyA = 0.0f; - float xyB = 0.0f; - float whA = 0.0f; - float whB = 0.0f; - - if (useFeather) - { - cursorWidth = propWidth + 0.5 * propFeather; - xyA = 1 + propFeather / 2; - xyB = 1 + propFeather / 8; - whA = qMax(0, propWidth - xyA - 1); - whB = qMax(0, cursorWidth - propFeather / 4 - 2); - } - else - { - cursorWidth = (propWidth + 0.5); - whA = qMax(0, propWidth - 1); - whB = qMax(0, cursorWidth / 4 - 2); - } - - float radius = cursorWidth / 2; - - // deallocate when cursor width gets some value larger than the widget - if (cursorWidth > windowWidth * 2) - { - return QPixmap(0, 0); - } - - if (cursorWidth < 1) { cursorWidth = 1; } - - QPixmap cursorPixmap = QPixmap(cursorWidth, cursorWidth); - if (!cursorPixmap.isNull()) - { - cursorPixmap.fill(QColor(255, 255, 255, 0)); - QPainter cursorPainter(&cursorPixmap); - QPen cursorPen = cursorPainter.pen(); - cursorPainter.setRenderHint(QPainter::Antialiasing); - - // Draw cross in center - cursorPen.setStyle(Qt::SolidLine); - cursorPen.setColor(QColor(0, 0, 0, 127)); - cursorPainter.setPen(cursorPen); - cursorPainter.drawLine(QPointF(radius - 2, radius), QPointF(radius + 2, radius)); - cursorPainter.drawLine(QPointF(radius, radius - 2), QPointF(radius, radius + 2)); - - // Draw outer circle - if (useFeather) - { - cursorPen.setStyle(Qt::DotLine); - cursorPen.setColor(QColor(0, 0, 0, 255)); - cursorPainter.setPen(cursorPen); - cursorPainter.drawEllipse(QRectF(xyB, xyB, whB, whB)); - cursorPen.setDashOffset(4); - cursorPen.setColor(QColor(255, 255, 255, 255)); - cursorPainter.setPen(cursorPen); - cursorPainter.drawEllipse(QRectF(xyB, xyB, whB, whB)); - } - - // Draw inner circle - cursorPen.setStyle(Qt::DotLine); - cursorPen.setColor(QColor(0, 0, 0, 255)); - cursorPainter.setPen(cursorPen); - cursorPainter.drawEllipse(QRectF(xyA, xyA, whA, whA)); - cursorPen.setDashOffset(4); - cursorPen.setColor(QColor(255, 255, 255, 255)); - cursorPainter.setPen(cursorPen); - cursorPainter.drawEllipse(QRectF(xyA, xyA, whA, whA)); - - cursorPainter.end(); - } - return cursorPixmap; -} - bool BaseTool::isActive() { return strokeManager()->isActive(); } -/** - * @brief precision circular cursor: used for drawing stroke size while adjusting - * @return QPixmap - */ -QPixmap BaseTool::quickSizeCursor(qreal scalingFac) -{ - qreal propSize = qMax(0., properties.width) * scalingFac; - qreal propFeather = qMax(0., properties.feather) * scalingFac; - QRectF cursorRect(0, 0, propSize+2, propSize+2); - - QRectF sizeRect = cursorRect.adjusted(1, 1, -1, -1); - qreal featherRadius = (1 - propFeather / 100) * propSize / 2.; - - QPixmap cursorPixmap = QPixmap(cursorRect.size().toSize()); - if (!cursorPixmap.isNull()) - { - cursorPixmap.fill(QColor(255, 255, 255, 0)); - QPainter cursorPainter(&cursorPixmap); - cursorPainter.setRenderHints(QPainter::Antialiasing, true); - - // Draw width (outside circle) - cursorPainter.setPen(QColor(255, 127, 127, 127)); - cursorPainter.setBrush(QColor(0, 255, 127, 127)); - cursorPainter.drawEllipse(sizeRect); - - // Draw feather (inside circle) - cursorPainter.setCompositionMode(QPainter::CompositionMode_Darken); - cursorPainter.setPen(QColor(0, 0, 0, 0)); - cursorPainter.setBrush(QColor(0, 191, 95, 127)); - cursorPainter.drawEllipse(cursorRect.center(), featherRadius, featherRadius); - - // Draw cursor in center - cursorPainter.setRenderHints(QPainter::Antialiasing, false); - cursorPainter.setPen(QColor(0, 0, 0, 255)); - cursorPainter.drawLine(cursorRect.center() - QPoint(2, 0), cursorRect.center() + QPoint(2, 0)); - cursorPainter.drawLine(cursorRect.center() - QPoint(0, 2), cursorRect.center() + QPoint(0, 2)); - - cursorPainter.end(); - } - return cursorPixmap; -} - -bool BaseTool::startAdjusting(Qt::KeyboardModifiers modifiers, qreal step) -{ - if (mQuickSizingProperties.contains(modifiers)) - { - switch (mQuickSizingProperties.value(modifiers)) { - case WIDTH: - msOriginalPropertyValue = properties.width; - break; - case FEATHER: - msOriginalPropertyValue = properties.feather; - break; - case TOLERANCE: - msOriginalPropertyValue = properties.tolerance; - break; - default: - qDebug() << "Unhandled quick sizing property for tool" << typeName(); - Q_ASSERT(false); - return false; - } - - msIsAdjusting = true; - mAdjustmentStep = step; - mScribbleArea->updateCanvasCursor(); - return true; - } - return false; -} - -void BaseTool::stopAdjusting() -{ - msIsAdjusting = false; - mAdjustmentStep = 0; - msOriginalPropertyValue = 0; - mEditor->getScribbleArea()->updateCanvasCursor(); -} - -void BaseTool::adjustCursor(Qt::KeyboardModifiers modifiers) -{ - qreal inc = qPow(msOriginalPropertyValue * 100, 0.5); - qreal newValue = inc + getCurrentPoint().x(); - - if (newValue < 0) - { - newValue = 0; - } - - newValue = qPow(newValue, 2) / 100; - if (mAdjustmentStep > 0) - { - int tempValue = static_cast(newValue / mAdjustmentStep); // + 0.5 ? - newValue = tempValue * mAdjustmentStep; - } - - switch (mQuickSizingProperties.value(modifiers)) - { - case WIDTH: - mEditor->tools()->setWidth(qBound(1., newValue, 200.)); - break; - case FEATHER: - mEditor->tools()->setFeather(qBound(2., newValue, 200.)); - break; - case TOLERANCE: - mEditor->tools()->setTolerance(qBound(0., newValue, 100.)); - break; - default: - qDebug() << "Unhandled quick sizing property for tool" << typeName(); - Q_ASSERT(false); - break; - } -} - QPointF BaseTool::getCurrentPressPixel() const { return strokeManager()->getCurrentPressPixel(); diff --git a/core_lib/src/tool/basetool.h b/core_lib/src/tool/basetool.h index 533b59491..1c18ca635 100644 --- a/core_lib/src/tool/basetool.h +++ b/core_lib/src/tool/basetool.h @@ -24,6 +24,7 @@ GNU General Public License for more details. #include #include #include +#include #include "pencildef.h" class QPixmap; @@ -90,18 +91,9 @@ class BaseTool : public QObject virtual bool keyPressEvent(QKeyEvent*) { return false; } virtual bool keyReleaseEvent(QKeyEvent*) { return false; } - // dynamic cursor adjustment - virtual bool startAdjusting(Qt::KeyboardModifiers modifiers, qreal argStep); - virtual void stopAdjusting(); - virtual void adjustCursor(Qt::KeyboardModifiers modifiers); - virtual void clearToolData() {} virtual void resetToDefault() {} - static QPixmap canvasCursor(float brushWidth, float brushFeather, bool useFeather, float scalingFac, int windowWidth); - QPixmap quickSizeCursor(qreal scalingFac); - static bool isAdjusting() { return msIsAdjusting; } - /** Check if the tool is active. * * An active tool is definied as one which is actively modifying the buffer. @@ -133,9 +125,14 @@ class BaseTool : public QObject virtual void setPathDotColorType(const DotColorType dotColorType); virtual void resetCameraPath(); - virtual void paint(QPainter& painter) { Q_UNUSED(painter) }; + virtual void paint(QPainter& painter, const QRect& blitRect) { Q_UNUSED(painter) Q_UNUSED(blitRect) } + + /// Will clean up `active` connections + virtual bool leavingThisTool(); - virtual bool leavingThisTool() { return true; } + /// Setup `active` connections here that should only emit while tool is active + /// `leavingThisTool` will handle the cleanup of `active` connections + virtual bool enteringThisTool() { return true; } Properties properties; @@ -162,15 +159,10 @@ class BaseTool : public QObject Editor* mEditor = nullptr; ScribbleArea* mScribbleArea = nullptr; - - QHash mQuickSizingProperties; + QList mActiveConnections; private: StrokeManager* mStrokeManager = nullptr; - qreal mAdjustmentStep = 0.0f; - - static bool msIsAdjusting; - static qreal msOriginalPropertyValue; // start from previous value (width, or feather ...) }; #endif // BASETOOL_H diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 4bec803d5..f7127dcc6 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -46,6 +46,8 @@ ToolType BrushTool::type() void BrushTool::loadSettings() { + StrokeTool::loadSettings(); + mPropertyEnabled[WIDTH] = true; mPropertyEnabled[FEATHER] = true; mPropertyEnabled[PRESSURE] = true; @@ -139,14 +141,24 @@ QCursor BrushTool::cursor() void BrushTool::pointerPressEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + mMouseDownPoint = getCurrentPoint(); mLastBrushPoint = getCurrentPoint(); startStroke(event->inputType()); + + StrokeTool::pointerPressEvent(event); } void BrushTool::pointerMoveEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType) { mCurrentPressure = strokeManager()->getPressure(); @@ -154,10 +166,16 @@ void BrushTool::pointerMoveEvent(PointerEvent* event) if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel()) strokeManager()->setStabilizerLevel(properties.stabilizerLevel); } + + StrokeTool::pointerMoveEvent(event); } void BrushTool::pointerReleaseEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + if (event->inputType() != mCurrentInputType) return; Layer* layer = mEditor->layers()->currentLayer(); @@ -178,6 +196,7 @@ void BrushTool::pointerReleaseEvent(PointerEvent *event) } endStroke(); + StrokeTool::pointerReleaseEvent(event); } // draw a single paint dab at the given location diff --git a/core_lib/src/tool/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index 6ef11c59c..54e57f5aa 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -230,20 +230,6 @@ void BucketTool::pointerReleaseEvent(PointerEvent* event) endStroke(); } -bool BucketTool::startAdjusting(Qt::KeyboardModifiers modifiers, qreal argStep) -{ - mQuickSizingProperties.clear(); - if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR) - { - mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH); - } - else - { - mQuickSizingProperties.insert(Qt::ControlModifier, TOLERANCE); - } - return BaseTool::startAdjusting(modifiers, argStep); -} - void BucketTool::paintBitmap() { mBitmapBucket.paint(getCurrentPoint(), [this](BucketState progress, int layerIndex, int frameIndex) diff --git a/core_lib/src/tool/buckettool.h b/core_lib/src/tool/buckettool.h index 4f9660a61..99f65e869 100644 --- a/core_lib/src/tool/buckettool.h +++ b/core_lib/src/tool/buckettool.h @@ -40,8 +40,6 @@ class BucketTool : public StrokeTool void pointerMoveEvent(PointerEvent*) override; void pointerReleaseEvent(PointerEvent*) override; - bool startAdjusting(Qt::KeyboardModifiers modifiers, qreal argStep) override; - void setTolerance(const int tolerance) override; void setToleranceEnabled(const bool enabled) override; void setWidth(const qreal width) override; diff --git a/core_lib/src/tool/cameratool.cpp b/core_lib/src/tool/cameratool.cpp index e82edb487..ab3928539 100644 --- a/core_lib/src/tool/cameratool.cpp +++ b/core_lib/src/tool/cameratool.cpp @@ -438,7 +438,7 @@ void CameraTool::transformView(LayerCamera* layerCamera, CameraMoveType mode, co curCam->modification(); } -void CameraTool::paint(QPainter& painter) +void CameraTool::paint(QPainter& painter, const QRect& blitRect) { int frameIndex = mEditor->currentFrame(); LayerCamera* cameraLayerBelow = static_cast(mEditor->object()->getLayerBelow(mEditor->currentLayerIndex(), Layer::CAMERA)); diff --git a/core_lib/src/tool/cameratool.h b/core_lib/src/tool/cameratool.h index 83f99bc8d..51adb8b44 100644 --- a/core_lib/src/tool/cameratool.h +++ b/core_lib/src/tool/cameratool.h @@ -51,7 +51,7 @@ class CameraTool : public BaseTool QCursor cursor() override; ToolType type() override { return ToolType::CAMERA; } - void paint(QPainter& painter) override; + void paint(QPainter& painter, const QRect& blitRect) override; void loadSettings() override; diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index 7d57ade7e..d87ac2a12 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -42,6 +42,8 @@ ToolType EraserTool::type() void EraserTool::loadSettings() { + StrokeTool::loadSettings(); + mPropertyEnabled[WIDTH] = true; mPropertyEnabled[USEFEATHER] = true; mPropertyEnabled[FEATHER] = true; @@ -149,13 +151,23 @@ QCursor EraserTool::cursor() void EraserTool::pointerPressEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + startStroke(event->inputType()); mLastBrushPoint = getCurrentPoint(); mMouseDownPoint = getCurrentPoint(); + + StrokeTool::pointerPressEvent(event); } void EraserTool::pointerMoveEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType) { mCurrentPressure = strokeManager()->getPressure(); @@ -163,10 +175,16 @@ void EraserTool::pointerMoveEvent(PointerEvent* event) if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel()) strokeManager()->setStabilizerLevel(properties.stabilizerLevel); } + + StrokeTool::pointerMoveEvent(event); } void EraserTool::pointerReleaseEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + if (event->inputType() != mCurrentInputType) return; mEditor->backup(typeName()); @@ -183,6 +201,8 @@ void EraserTool::pointerReleaseEvent(PointerEvent *event) removeVectorPaint(); endStroke(); + + StrokeTool::pointerReleaseEvent(event); } // draw a single paint dab at the given location diff --git a/core_lib/src/tool/movetool.cpp b/core_lib/src/tool/movetool.cpp index ed3757da7..e93ffe252 100644 --- a/core_lib/src/tool/movetool.cpp +++ b/core_lib/src/tool/movetool.cpp @@ -319,6 +319,8 @@ void MoveTool::applyTransformation() bool MoveTool::leavingThisTool() { + BaseTool::leavingThisTool(); + if (currentPaintableLayer()) { applyTransformation(); diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index cd2169870..4fde93f17 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -39,6 +39,8 @@ PencilTool::PencilTool(QObject* parent) : StrokeTool(parent) void PencilTool::loadSettings() { + StrokeTool::loadSettings(); + mPropertyEnabled[WIDTH] = true; mPropertyEnabled[PRESSURE] = true; mPropertyEnabled[VECTORMERGE] = false; @@ -51,10 +53,7 @@ void PencilTool::loadSettings() properties.pressure = settings.value("pencilPressure", true).toBool(); properties.stabilizerLevel = settings.value("pencilLineStabilization", StabilizationLevel::STRONG).toInt(); properties.useAA = DISABLED; - properties.useFeather = true; properties.useFillContour = false; - // properties.invisibility = 1; - // properties.preserveAlpha = 0; mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH); } @@ -63,7 +62,7 @@ void PencilTool::resetToDefault() { setWidth(4.0); setFeather(50); - setUseFeather(true); + setUseFeather(false); setStabilizerLevel(StabilizationLevel::STRONG); } @@ -147,6 +146,10 @@ QCursor PencilTool::cursor() void PencilTool::pointerPressEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + mMouseDownPoint = getCurrentPoint(); mLastBrushPoint = getCurrentPoint(); @@ -157,10 +160,16 @@ void PencilTool::pointerPressEvent(PointerEvent *event) { mScribbleArea->toggleThinLines(); } + + StrokeTool::pointerPressEvent(event); } void PencilTool::pointerMoveEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType) { mCurrentPressure = strokeManager()->getPressure(); @@ -168,10 +177,15 @@ void PencilTool::pointerMoveEvent(PointerEvent* event) if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel()) strokeManager()->setStabilizerLevel(properties.stabilizerLevel); } + StrokeTool::pointerMoveEvent(event); } void PencilTool::pointerReleaseEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + if (event->inputType() != mCurrentInputType) return; mEditor->backup(typeName()); @@ -190,6 +204,8 @@ void PencilTool::pointerReleaseEvent(PointerEvent *event) paintVectorStroke(layer); } endStroke(); + + StrokeTool::pointerReleaseEvent(event); } // draw a single paint dab at the given location @@ -254,7 +270,6 @@ void PencilTool::drawStroke() } else if (layer->type() == Layer::VECTOR) { - properties.useFeather = false; mCurrentWidth = 0; // FIXME: WTF? QPen pen(mEditor->color()->frontColor(), 1, diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 64dbed970..a2f985872 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -38,6 +38,8 @@ PenTool::PenTool(QObject* parent) : StrokeTool(parent) void PenTool::loadSettings() { + StrokeTool::loadSettings(); + mPropertyEnabled[WIDTH] = true; mPropertyEnabled[PRESSURE] = true; mPropertyEnabled[VECTORMERGE] = true; @@ -118,14 +120,24 @@ QCursor PenTool::cursor() void PenTool::pointerPressEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + mMouseDownPoint = getCurrentPoint(); mLastBrushPoint = getCurrentPoint(); startStroke(event->inputType()); + + StrokeTool::pointerPressEvent(event); } void PenTool::pointerMoveEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType) { mCurrentPressure = strokeManager()->getPressure(); @@ -133,10 +145,16 @@ void PenTool::pointerMoveEvent(PointerEvent* event) if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel()) strokeManager()->setStabilizerLevel(properties.stabilizerLevel); } + + StrokeTool::pointerMoveEvent(event); } void PenTool::pointerReleaseEvent(PointerEvent *event) { + if (handleQuickSizing(event)) { + return; + } + if (event->inputType() != mCurrentInputType) return; mEditor->backup(typeName()); @@ -157,6 +175,8 @@ void PenTool::pointerReleaseEvent(PointerEvent *event) paintVectorStroke(layer); } endStroke(); + + StrokeTool::pointerReleaseEvent(event); } // draw a single paint dab at the given location diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index 4d723b487..5a3c664cd 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -30,7 +30,7 @@ GNU General Public License for more details. #include "vectorimage.h" -PolylineTool::PolylineTool(QObject* parent) : BaseTool(parent) +PolylineTool::PolylineTool(QObject* parent) : StrokeTool(parent) { } @@ -41,6 +41,8 @@ ToolType PolylineTool::type() void PolylineTool::loadSettings() { + StrokeTool::loadSettings(); + mPropertyEnabled[WIDTH] = true; mPropertyEnabled[BEZIER] = true; mPropertyEnabled[ANTI_ALIASING] = true; @@ -54,6 +56,8 @@ void PolylineTool::loadSettings() properties.preserveAlpha = OFF; properties.useAA = settings.value("brushAA").toBool(); properties.stabilizerLevel = -1; + + mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH); } void PolylineTool::resetToDefault() @@ -92,6 +96,7 @@ void PolylineTool::setAA(const int AA) bool PolylineTool::leavingThisTool() { + StrokeTool::leavingThisTool(); if (mPoints.size() > 0) { cancelPolyline(); @@ -118,6 +123,10 @@ void PolylineTool::clearToolData() void PolylineTool::pointerPressEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + Layer* layer = mEditor->layers()->currentLayer(); if (event->button() == Qt::LeftButton) @@ -140,19 +149,33 @@ void PolylineTool::pointerPressEvent(PointerEvent* event) emit isActiveChanged(POLYLINE, true); } } + + StrokeTool::pointerPressEvent(event); } -void PolylineTool::pointerMoveEvent(PointerEvent*) +void PolylineTool::pointerMoveEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + Layer* layer = mEditor->layers()->currentLayer(); if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR) { drawPolyline(mPoints, getCurrentPoint()); } + + StrokeTool::pointerMoveEvent(event); } -void PolylineTool::pointerReleaseEvent(PointerEvent *) -{} +void PolylineTool::pointerReleaseEvent(PointerEvent* event) +{ + if (handleQuickSizing(event)) { + return; + } + + StrokeTool::pointerReleaseEvent(event); +} void PolylineTool::pointerDoubleClickEvent(PointerEvent*) { diff --git a/core_lib/src/tool/polylinetool.h b/core_lib/src/tool/polylinetool.h index 41ce32a82..b1b11683d 100644 --- a/core_lib/src/tool/polylinetool.h +++ b/core_lib/src/tool/polylinetool.h @@ -20,9 +20,9 @@ GNU General Public License for more details. #include -#include "basetool.h" +#include "stroketool.h" -class PolylineTool : public BaseTool +class PolylineTool : public StrokeTool { Q_OBJECT public: diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index de94077e6..e3594b9ae 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -44,6 +44,8 @@ ToolType SmudgeTool::type() void SmudgeTool::loadSettings() { + StrokeTool::loadSettings(); + mPropertyEnabled[WIDTH] = true; mPropertyEnabled[FEATHER] = true; @@ -121,7 +123,7 @@ bool SmudgeTool::keyPressEvent(QKeyEvent *event) mScribbleArea->setCursor(cursor()); // update cursor return true; } - return BaseTool::keyPressEvent(event); + return StrokeTool::keyPressEvent(event); } bool SmudgeTool::keyReleaseEvent(QKeyEvent *event) @@ -132,12 +134,14 @@ bool SmudgeTool::keyReleaseEvent(QKeyEvent *event) mScribbleArea->setCursor(cursor()); // update cursor return true; } - return BaseTool::keyReleaseEvent(event); + return StrokeTool::keyReleaseEvent(event); } void SmudgeTool::pointerPressEvent(PointerEvent* event) { - //qDebug() << "smudgetool: mousePressEvent"; + if (handleQuickSizing(event)) { + return; + } Layer* layer = mEditor->layers()->currentLayer(); auto selectMan = mEditor->select(); @@ -186,10 +190,16 @@ void SmudgeTool::pointerPressEvent(PointerEvent* event) } } } + + StrokeTool::pointerPressEvent(event); } void SmudgeTool::pointerMoveEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + if (event->inputType() != mCurrentInputType) return; Layer* layer = mEditor->layers()->currentLayer(); @@ -240,10 +250,16 @@ void SmudgeTool::pointerMoveEvent(PointerEvent* event) mScribbleArea->update(); } } + + StrokeTool::pointerMoveEvent(event); } void SmudgeTool::pointerReleaseEvent(PointerEvent* event) { + if (handleQuickSizing(event)) { + return; + } + if (event->inputType() != mCurrentInputType) return; Layer* layer = mEditor->layers()->currentLayer(); @@ -276,6 +292,8 @@ void SmudgeTool::pointerReleaseEvent(PointerEvent* event) mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); } } + + StrokeTool::pointerReleaseEvent(event); } void SmudgeTool::drawStroke() diff --git a/core_lib/src/tool/stroketool.cpp b/core_lib/src/tool/stroketool.cpp index 6b8e8c112..5da6ad003 100644 --- a/core_lib/src/tool/stroketool.cpp +++ b/core_lib/src/tool/stroketool.cpp @@ -21,8 +21,12 @@ GNU General Public License for more details. #include "scribblearea.h" #include "strokemanager.h" #include "viewmanager.h" +#include "preferencemanager.h" #include "editor.h" #include "toolmanager.h" +#include "mathutils.h" + +#include "canvascursorpainter.h" #ifdef Q_OS_MAC extern "C" { @@ -38,11 +42,55 @@ extern "C" { } #endif +const qreal StrokeTool::FEATHER_MIN = 1.; +const qreal StrokeTool::FEATHER_MAX = 99.; +const qreal StrokeTool::WIDTH_MIN = 1.; +const qreal StrokeTool::WIDTH_MAX = 200.; + +// ---- shared static variables ---- ( only one instance for all the tools ) +bool StrokeTool::msIsAdjusting = false; +bool StrokeTool::mQuickSizingEnabled = false; + StrokeTool::StrokeTool(QObject* parent) : BaseTool(parent) { detectWhichOSX(); } +void StrokeTool::loadSettings() +{ + mQuickSizingEnabled = mEditor->preference()->isOn(SETTING::QUICK_SIZING); + mCanvasCursorEnabled = mEditor->preference()->isOn(SETTING::CANVAS_CURSOR); + + /// Given the way that we update preferences currently, this connection should not be removed + /// when the tool is not active. + connect(mEditor->preference(), &PreferenceManager::optionChanged, this, &StrokeTool::onPreferenceChanged); +} + +bool StrokeTool::enteringThisTool() +{ + mActiveConnections.append(connect(mEditor->view(), &ViewManager::viewChanged, this, &StrokeTool::onViewUpdated)); + return true; +} + +bool StrokeTool::leavingThisTool() +{ + return BaseTool::leavingThisTool(); +} + +void StrokeTool::onPreferenceChanged(SETTING setting) +{ + if (setting == SETTING::QUICK_SIZING) { + mQuickSizingEnabled = mEditor->preference()->isOn(setting); + } else if (setting == SETTING::CANVAS_CURSOR) { + mCanvasCursorEnabled = mEditor->preference()->isOn(setting); + } +} + +void StrokeTool::onViewUpdated() +{ + updateCanvasCursor(); +} + void StrokeTool::startStroke(PointerEvent::InputType inputType) { if (emptyFrameActionEnabled()) @@ -119,3 +167,175 @@ void StrokeTool::drawStroke() mFirstDraw = false; } } + +bool StrokeTool::handleQuickSizing(PointerEvent* event) +{ + if (event->eventType() == PointerEvent::Press) { + if (mQuickSizingEnabled) { + return startAdjusting(event->modifiers()); + } + } else if (event->eventType() == PointerEvent::Move) { + if (event->buttons() & Qt::LeftButton && msIsAdjusting) { + adjustCursor(event->modifiers()); + return true; + } + } else if (event->eventType() == PointerEvent::Release) { + if (msIsAdjusting) { + stopAdjusting(); + return true; + } + } + return false; +} + +void StrokeTool::pointerPressEvent(PointerEvent*) +{ + updateCanvasCursor(); +} + +void StrokeTool::pointerMoveEvent(PointerEvent*) +{ + updateCanvasCursor(); +} + +void StrokeTool::pointerReleaseEvent(PointerEvent*) +{ + updateCanvasCursor(); +} + +bool StrokeTool::event(QEvent *event) +{ + if (event->type() == QEvent::Leave && !isActive()) { + mCanvasCursorEnabled = false; + updateCanvasCursor(); + QObject::event(event); + return true; + } else if (event->type() == QEvent::Enter) { + mCanvasCursorEnabled = mEditor->preference()->isOn(SETTING::CANVAS_CURSOR); + QObject::event(event); + return true; + } + return QObject::event(event); +} + +void StrokeTool::updateCanvasCursor() +{ + const qreal brushWidth = properties.width; + const qreal brushFeather = properties.feather; + + const QPointF& cursorPos = msIsAdjusting ? mAdjustPosition : getCurrentPoint(); + const qreal cursorRad = brushWidth * 0.5; + const QPointF& cursorOffset = QPointF(cursorPos.x() - cursorRad, cursorPos.y() - cursorRad); + + CanvasCursorPainterOptions options; + options.widthRect = QRectF(cursorOffset, QSizeF(brushWidth, brushWidth)); + + const qreal featherWidthFactor = MathUtils::normalize(brushFeather, 0.0, FEATHER_MAX); + options.featherRect = QRectF(options.widthRect.center().x() - (cursorRad * featherWidthFactor), + options.widthRect.center().y() - (cursorRad * featherWidthFactor), + brushWidth * featherWidthFactor, + brushWidth * featherWidthFactor); + options.showCursor = mCanvasCursorEnabled; + options.isAdjusting = msIsAdjusting && mQuickSizingEnabled; + options.useFeather = mPropertyEnabled[FEATHER]; + + mCanvasCursorPainter.preparePainter(options, mEditor->view()->getView()); + + const QRect& dirtyRect = mCanvasCursorPainter.dirtyRect(); + const QRect& updateRect = mEditor->view()->getView().mapRect(QRectF(cursorOffset, QSizeF(brushWidth, brushWidth))).toAlignedRect(); + + if (!msIsAdjusting && !mCanvasCursorEnabled) { + if (mCanvasCursorPainter.isDirty()) { + // Adjusted to account for some pixel bleeding outside the update rect + mScribbleArea->update(mCanvasCursorPainter.dirtyRect().adjusted(-2, -2, 2, 2)); + mCanvasCursorPainter.clearDirty(); + } + return; + } + + // Adjusted to account for some pixel bleeding outside the update rect + mScribbleArea->update(updateRect.united(dirtyRect).adjusted(-2, -2, 2, 2)); +} + +bool StrokeTool::startAdjusting(Qt::KeyboardModifiers modifiers) +{ + if (!mQuickSizingProperties.contains(modifiers)) + { + return false; + } + + const QPointF& currentPressPoint = getCurrentPressPoint(); + const QPointF& currentPoint = getCurrentPoint(); + auto propertyType = mQuickSizingProperties.value(modifiers); + switch (propertyType) { + case WIDTH: { + const qreal factor = 0.5; + const qreal rad = properties.width * factor; + const qreal distance = QLineF(currentPressPoint - QPointF(rad, rad), currentPoint).length(); + mAdjustPosition = currentPressPoint - QPointF(distance * factor, distance * factor); + break; + } + case FEATHER: { + const qreal factor = 0.5; + const qreal cursorRad = properties.width * factor; + const qreal featherWidthFactor = MathUtils::normalize(properties.feather, 0.0, FEATHER_MAX); + const qreal offset = (cursorRad * featherWidthFactor) * factor; + const qreal distance = QLineF(currentPressPoint - QPointF(offset, offset), currentPoint).length(); + mAdjustPosition = currentPressPoint - QPointF(distance, distance); + break; + } + default: + Q_UNREACHABLE(); + qWarning() << "Unhandled quick sizing property for tool" << typeName(); + return false; + } + + msIsAdjusting = true; + updateCanvasCursor(); + return true; +} + +void StrokeTool::stopAdjusting() +{ + msIsAdjusting = false; + mAdjustPosition = QPointF(); + updateCanvasCursor(); +} + +void StrokeTool::adjustCursor(Qt::KeyboardModifiers modifiers) +{ + switch (mQuickSizingProperties.value(modifiers)) + { + case WIDTH: { + // The adjusted position is based on the radius of the circle, so in order to + // map it back to its original value, we can multiply by the factor we divided with + const qreal newValue = QLineF(mAdjustPosition, getCurrentPoint()).length() * 2.0; + + mEditor->tools()->setWidth(qBound(WIDTH_MIN, newValue, WIDTH_MAX)); + break; + } + case FEATHER: { + // The radius of the width is the max value we can get + const qreal inputMin = 0.0; + const qreal inputMax = properties.width * 0.5; + const qreal distance = QLineF(mAdjustPosition, getCurrentPoint()).length(); + const qreal outputMax = FEATHER_MAX; + const qreal outputMin = 0.0; + + // We flip min and max here in order to get the inverted value for the UI + const qreal mappedValue = MathUtils::map(distance, inputMin, inputMax, outputMax, outputMin); + + mEditor->tools()->setFeather(qBound(FEATHER_MIN, mappedValue, FEATHER_MAX)); + break; + } + default: + Q_UNREACHABLE(); + qWarning() << "Unhandled quick sizing property for tool" << typeName(); + } + updateCanvasCursor(); +} + +void StrokeTool::paint(QPainter& painter, const QRect& blitRect) +{ + mCanvasCursorPainter.paint(painter, blitRect); +} diff --git a/core_lib/src/tool/stroketool.h b/core_lib/src/tool/stroketool.h index 7f97c793b..28bc766c8 100644 --- a/core_lib/src/tool/stroketool.h +++ b/core_lib/src/tool/stroketool.h @@ -20,6 +20,9 @@ GNU General Public License for more details. #include "basetool.h" #include "pointerevent.h" +#include "preferencesdef.h" + +#include "canvascursorpainter.h" #include #include @@ -36,9 +39,42 @@ class StrokeTool : public BaseTool void drawStroke(); void endStroke(); + bool leavingThisTool() override; + bool enteringThisTool() override; + + void updateCanvasCursor(); + + static const qreal FEATHER_MIN; + static const qreal FEATHER_MAX; + static const qreal WIDTH_MIN; + static const qreal WIDTH_MAX; + + void loadSettings() override; + bool keyPressEvent(QKeyEvent* event) override; + void pointerPressEvent(PointerEvent* event) override; + void pointerMoveEvent(PointerEvent* event) override; + void pointerReleaseEvent(PointerEvent* event) override; + bool event(QEvent *event) override; + + bool handleQuickSizing(PointerEvent* event); + + void paint(QPainter& painter, const QRect& blitRect) override; + +public slots: + void onPreferenceChanged(SETTING setting); + void onViewUpdated(); protected: + // dynamic cursor adjustment + virtual bool startAdjusting(Qt::KeyboardModifiers modifiers); + virtual void stopAdjusting(); + virtual void adjustCursor(Qt::KeyboardModifiers modifiers); + + static bool mQuickSizingEnabled; + static bool msIsAdjusting; + + QHash mQuickSizingProperties; bool mFirstDraw = false; QList mStrokePoints; @@ -56,8 +92,12 @@ class StrokeTool : public BaseTool /// Returns true by default. virtual bool emptyFrameActionEnabled(); -private: + bool mCanvasCursorEnabled = false; QPointF mLastPixel { 0, 0 }; + + QPointF mAdjustPosition; + + CanvasCursorPainter mCanvasCursorPainter; }; #endif // STROKETOOL_H diff --git a/core_lib/src/util/mathutils.h b/core_lib/src/util/mathutils.h index 7583e5bc1..858515cb6 100644 --- a/core_lib/src/util/mathutils.h +++ b/core_lib/src/util/mathutils.h @@ -16,6 +16,31 @@ namespace MathUtils { return qAtan2(b.y() - a.y(), b.x() - a.x()); } + + /** Map one range onto another + * \param x The input value + * \param inputMin The input min value + * \param inputMax The input max value + * \param outputMin The output min value + * \param outputMax The output max value + * \return The value of x mapped to the corresponding range between outputMin and outputMax + */ + inline qreal map(qreal x, qreal inputMin, qreal inputMax, qreal outputMin, qreal outputMax) + { + qreal slope = (outputMax - outputMin) / (inputMax - inputMin); + return outputMin + slope * (x - inputMin); + } + + /** Normalize x to a value between 0 and 1; + * \param x The input value + * \param min The input min value + * \param max The input max value + * \return The value of x normalized to a range between 0 and 1 + */ + inline qreal normalize(qreal x, qreal min, qreal max) + { + return qAbs((x - max) / (min - max)); + } } #endif // MATHUTILS_H diff --git a/core_lib/src/util/pencildef.h b/core_lib/src/util/pencildef.h index e1b9b11ac..adf5aceb0 100644 --- a/core_lib/src/util/pencildef.h +++ b/core_lib/src/util/pencildef.h @@ -117,9 +117,6 @@ inline LayerVisibility& operator--(LayerVisibility& vis) // Max frames that can be imported and loaded onto the timeline const static int MaxFramesBound = 9999; -// Spacer for rotation handle offset -const static float RotationHandleOffset = 50; - // shortcuts command code #define CMD_NEW_FILE "CmdNewFile" #define CMD_OPEN_FILE "CmdOpenFile" @@ -228,7 +225,7 @@ const static float RotationHandleOffset = 50; #define SETTING_AUTO_SAVE "AutoSave" #define SETTING_AUTO_SAVE_NUMBER "AutosaveNumber" #define SETTING_TOOL_CURSOR "ToolCursors" -#define SETTING_DOTTED_CURSOR "DottedCursors" +#define SETTING_CANVAS_CURSOR "DottedCursors" #define SETTING_HIGH_RESOLUTION "HighResPosition" #define SETTING_BACKGROUND_STYLE "Background" #define SETTING_WINDOW_OPACITY "WindowOpacity" diff --git a/core_lib/src/util/pointerevent.cpp b/core_lib/src/util/pointerevent.cpp index b65c720ac..dc7c0f202 100644 --- a/core_lib/src/util/pointerevent.cpp +++ b/core_lib/src/util/pointerevent.cpp @@ -209,17 +209,37 @@ bool PointerEvent::isAccepted() return false; } -QEvent::Type PointerEvent::eventType() const +PointerEvent::Type PointerEvent::eventType() const { if (mMouseEvent) { - return mMouseEvent->type(); + switch (mMouseEvent->type()) + { + case QEvent::MouseButtonPress: + return Type::Press; + case QEvent::MouseMove: + return Type::Move; + case QEvent::MouseButtonRelease: + return Type::Release; + default: + return Type::Unmapped; + } } else if (mTabletEvent) { - return mTabletEvent->type(); - } - return QEvent::None; + switch (mTabletEvent->type()) + { + case QEvent::TabletPress: + return Type::Press; + case QEvent::TabletMove: + return Type::Move; + case QEvent::TabletRelease: + return Type::Release; + default: + return Type::Unmapped; + } + } + return Type::Unmapped; } PointerEvent::InputType PointerEvent::inputType() const diff --git a/core_lib/src/util/pointerevent.h b/core_lib/src/util/pointerevent.h index 871fe69a1..aad7ed692 100644 --- a/core_lib/src/util/pointerevent.h +++ b/core_lib/src/util/pointerevent.h @@ -14,6 +14,13 @@ class PointerEvent Unknown }; + enum Type { + Press, + Move, + Release, + Unmapped + }; + PointerEvent(QMouseEvent* event); PointerEvent(QTabletEvent* event); ~PointerEvent(); @@ -70,7 +77,7 @@ class PointerEvent bool isAccepted(); - QEvent::Type eventType() const; + Type eventType() const; InputType inputType() const; #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) diff --git a/core_lib/src/util/preferencesdef.h b/core_lib/src/util/preferencesdef.h index b71878eed..e8eb21605 100644 --- a/core_lib/src/util/preferencesdef.h +++ b/core_lib/src/util/preferencesdef.h @@ -30,7 +30,7 @@ enum class SETTING ONION_BLUE, ONION_RED, TOOL_CURSOR, - DOTTED_CURSOR, + CANVAS_CURSOR, HIGH_RESOLUTION, WINDOW_OPACITY, SHOW_STATUS_BAR,