diff --git a/krita/krita.action b/krita/krita.action --- a/krita/krita.action +++ b/krita/krita.action @@ -354,6 +354,19 @@ true + + + Toggle erase op + + Toggle eraser op + Toggle eraser op + 10000 + 0 + Shift+E + Shift+E + + + view-refresh Reload Original Preset diff --git a/krita/ui/kis_canvas_resource_provider.h b/krita/ui/kis_canvas_resource_provider.h --- a/krita/ui/kis_canvas_resource_provider.h +++ b/krita/ui/kis_canvas_resource_provider.h @@ -59,6 +59,7 @@ CurrentImage, CurrentKritaNode, CurrentPaintOpPreset, + CurrentEraserMode, CurrentGeneratorConfiguration, CurrentCompositeOp, MirrorHorizontal, @@ -107,6 +108,9 @@ KisPaintOpPresetSP previousPreset() const; void setPreviousPaintOpPreset(const KisPaintOpPresetSP preset); + bool currentEraserMode() const; + void setEraserMode(const bool mode); + KisFilterConfiguration* currentGeneratorConfiguration() const; void setCurrentCompositeOp(const QString& compositeOp); diff --git a/krita/ui/kis_canvas_resource_provider.cpp b/krita/ui/kis_canvas_resource_provider.cpp --- a/krita/ui/kis_canvas_resource_provider.cpp +++ b/krita/ui/kis_canvas_resource_provider.cpp @@ -179,6 +179,20 @@ m_resourceManager->setResource(CurrentPaintOpPreset, v); } + + +bool KisCanvasResourceProvider::currentEraserMode() const +{ + return m_resourceManager->resource(CurrentEraserMode).value(); +} + +void KisCanvasResourceProvider::setEraserMode(const bool eraserMode) +{ + dbgUI << "setEraserMode" << eraserMode; + m_resourceManager->setResource(CurrentEraserMode, qVariantFromValue(eraserMode)); +} + + KisPaintOpPresetSP KisCanvasResourceProvider::previousPreset() const { KisPaintOpPresetSP preset = m_resourceManager->resource(PreviousPaintOpPreset).value(); diff --git a/krita/ui/kis_paintop_box.h b/krita/ui/kis_paintop_box.h --- a/krita/ui/kis_paintop_box.h +++ b/krita/ui/kis_paintop_box.h @@ -141,7 +141,8 @@ void slotUpdatePreset(); void slotSetupDefaultPreset(); void slotNodeChanged(const KisNodeSP node); - void slotToggleEraseMode(bool checked); + void slotToggleEraserMode(bool checked); + void slotToggleEraseOp(); void slotSetCompositeMode(int index); void slotSetPaintop(const QString& paintOpId); void slotHorizontalMirrorChanged(bool value); @@ -164,7 +165,7 @@ void slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p); void slotDropLockedOption(KisPropertiesConfiguration* p); void slotDirtyPresetToggled(bool); - void slotEraserBrushSizeToggled(bool); + void slotEraserBrushSizeToggled(bool); void slotUpdateSelectionIcon(); private: @@ -190,6 +191,7 @@ KisFavoriteResourceManager* m_favoriteResourceManager; QToolButton* m_reloadButton; KisAction* m_eraseAction; + KisAction* m_eraseOpAction; KisAction* m_reloadAction; QString m_prevCompositeOpID; @@ -202,6 +204,7 @@ KisAction* m_hMirrorAction; KisAction* m_vMirrorAction; + // Tracking tools seems to duplicate functionality in KoToolManager? struct TabletToolID { TabletToolID(const KoInputDevice& dev) { uniqueID = dev.uniqueTabletId(); @@ -241,7 +244,7 @@ bool m_blockUpdate; bool m_dirtyPresetsEnabled; bool m_eraserBrushSizeEnabled; - + bool m_currentEraserMode; }; diff --git a/krita/ui/kis_paintop_box.cc b/krita/ui/kis_paintop_box.cc --- a/krita/ui/kis_paintop_box.cc +++ b/krita/ui/kis_paintop_box.cc @@ -143,6 +143,9 @@ m_eraseAction->setCheckable(true); m_eraseModeButton->setDefaultAction(m_eraseAction); + m_eraseOpAction = m_viewManager->actionManager()->createAction("erase_op"); + + eraserBrushSize = 0; // brush size changed when using erase mode m_reloadButton = new QToolButton(this); @@ -385,22 +388,23 @@ } m_presetsPopup->setPaintOpList(factoryList); - connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString))); - connect(m_presetsPopup , SIGNAL(savePresetClicked()) , SLOT(slotSaveActivePreset())); - connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset())); - connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResource*)), SLOT(resourceSelected(KoResource*))); - connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset())); - connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool))); - connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool))); + connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString))); + connect(m_presetsPopup , SIGNAL(savePresetClicked()) , SLOT(slotSaveActivePreset())); + connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset())); + connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResource*)), SLOT(resourceSelected(KoResource*))); + connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset())); + connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool))); + connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool))); - connect(m_presetsChooserPopup, SIGNAL(resourceSelected(KoResource*)) , SLOT(resourceSelected(KoResource*))); - connect(m_resourceProvider , SIGNAL(sigNodeChanged(const KisNodeSP)) , SLOT(slotNodeChanged(const KisNodeSP))); - connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int))); - connect(m_eraseAction , SIGNAL(triggered(bool)) , SLOT(slotToggleEraseMode(bool))); - connect(alphaLockAction , SIGNAL(triggered(bool)) , SLOT(slotToggleAlphaLockMode(bool))); - connect(m_hMirrorAction , SIGNAL(triggered(bool)) , SLOT(slotHorizontalMirrorChanged(bool))); - connect(m_vMirrorAction , SIGNAL(triggered(bool)) , SLOT(slotVerticalMirrorChanged(bool))); - connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset())); + connect(m_presetsChooserPopup , SIGNAL(resourceSelected(KoResource*)) , SLOT(resourceSelected(KoResource*))); + connect(m_resourceProvider , SIGNAL(sigNodeChanged(const KisNodeSP)) , SLOT(slotNodeChanged(const KisNodeSP))); + connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int))); + connect(m_eraseAction , SIGNAL(triggered(bool)) , SLOT(slotToggleEraserMode(bool))); + connect(m_eraseOpAction , SIGNAL(triggered()) , SLOT(slotToggleEraseOp())); + connect(alphaLockAction , SIGNAL(triggered(bool)) , SLOT(slotToggleAlphaLockMode(bool))); + connect(m_hMirrorAction , SIGNAL(triggered(bool)) , SLOT(slotHorizontalMirrorChanged(bool))); + connect(m_vMirrorAction , SIGNAL(triggered(bool)) , SLOT(slotVerticalMirrorChanged(bool))); + connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset())); connect(m_sliderChooser[0]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); connect(m_sliderChooser[0]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed())); @@ -619,13 +623,6 @@ m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID)); m_cmbCompositeOp->blockSignals(false); - m_eraseModeButton->defaultAction()->blockSignals(true); - m_eraseModeButton->blockSignals(true); - m_eraseModeButton->setChecked(compositeOpID == COMPOSITE_ERASE); - m_eraseModeButton->defaultAction()->setChecked(compositeOpID == COMPOSITE_ERASE); - m_eraseModeButton->blockSignals(false); - m_eraseModeButton->defaultAction()->blockSignals(false); - if (compositeOpID != m_currCompositeOpID) { m_resourceProvider->currentPreset()->settings()->setPaintOpCompositeOp(compositeOpID); m_optionWidget->setConfiguration(m_resourceProvider->currentPreset()->settings().data()); @@ -681,6 +678,15 @@ void KisPaintopBox::slotInputDeviceChanged(const KoInputDevice& inputDevice) { + // We will intercept cursor type and force it to be an eraser if eraserMode = true. + // This seems to duplicate functionality in KoToolManager. + KoInputDevice modInputDevice; + if (m_resourceProvider->currentEraserMode()) { + modInputDevice = KoInputDevice(inputDevice.device(), QTabletEvent::Eraser, inputDevice.uniqueTabletId()); + } + else { + modInputDevice = inputDevice; + } TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice); if (toolData == m_tabletToolMap.end()) { @@ -725,14 +731,17 @@ m_cmbCompositeOp->blockSignals(true); m_cmbCompositeOp->selectCompositeOp(KoID(compositeOp)); m_cmbCompositeOp->blockSignals(false); + } + if (m_eraseModeButton->isChecked() != m_resourceProvider->currentEraserMode()) { m_eraseModeButton->defaultAction()->blockSignals(true); m_eraseModeButton->blockSignals(true); - m_eraseModeButton->setChecked(compositeOp == COMPOSITE_ERASE); - m_eraseModeButton->defaultAction()->setChecked(compositeOp == COMPOSITE_ERASE); + m_eraseModeButton->setChecked(m_resourceProvider->currentEraserMode()); + m_eraseModeButton->defaultAction()->setChecked(m_resourceProvider->currentEraserMode()); m_eraseModeButton->blockSignals(false); m_eraseModeButton->defaultAction()->blockSignals(false); } + sender()->blockSignals(false); } } @@ -840,10 +849,20 @@ m_cmbCompositeOp->validate(colorSpace); } -void KisPaintopBox::slotToggleEraseMode(bool checked) +void KisPaintopBox::slotToggleEraserMode(bool checked) { - if (checked) + // Update. These two calls should always be made together! + KoToolManager::instance()->setEraserMode(checked); + m_resourceProvider->setEraserMode(checked); + // toggleHighlightedButton(m_eraseModeButton); +} + +void KisPaintopBox::slotToggleEraseOp() +{ + bool switchingToErase = false; + if (m_currCompositeOpID != COMPOSITE_ERASE) // Note - can't handle both ops being erasers. Whatever! { + switchingToErase = true; updateCompositeOp(COMPOSITE_ERASE); //add an option to enable eraser brush size if (m_eraserBrushSizeEnabled==true) @@ -877,7 +896,7 @@ //update value in UI (this is the main place the value is 'stored' in memory) - qreal updateSize = checked ? eraserBrushSize : normalBrushSize; + qreal updateSize = switchingToErase ? eraserBrushSize : normalBrushSize; m_sliderChooser[0]->getWidget("size")->setValue(updateSize); m_sliderChooser[1]->getWidget("size")->setValue(updateSize); m_sliderChooser[2]->getWidget("size")->setValue(updateSize); @@ -1087,6 +1106,7 @@ m_tool->setPalette(p); } } + void KisPaintopBox::slotReloadPreset() { KisSignalsBlocker blocker(m_optionWidget); diff --git a/krita/ui/widgets/kis_workspace_chooser.h b/krita/ui/widgets/kis_workspace_chooser.h --- a/krita/ui/widgets/kis_workspace_chooser.h +++ b/krita/ui/widgets/kis_workspace_chooser.h @@ -43,6 +43,7 @@ KoResourceItemChooser * m_itemChooser; KisViewManager* m_view; QLineEdit* m_nameEdit; + bool eraseMode; }; #endif // KIS_WORKSPACE_CHOOSER_H diff --git a/libs/flake/KoInputDevice.h b/libs/flake/KoInputDevice.h --- a/libs/flake/KoInputDevice.h +++ b/libs/flake/KoInputDevice.h @@ -49,7 +49,7 @@ explicit KoInputDevice(QTabletEvent::TabletDevice device, QTabletEvent::PointerType pointer, qint64 uniqueTabletId = -1); /** - * Constructor for the mouse as input device. + * Constructor for the default input device. */ KoInputDevice(); @@ -87,7 +87,6 @@ static KoInputDevice stylus(); ///< Wacom style/pen static KoInputDevice eraser(); ///< Wacom eraser - private: class Private; Private * const d; diff --git a/libs/flake/KoInputDevice.cpp b/libs/flake/KoInputDevice.cpp --- a/libs/flake/KoInputDevice.cpp +++ b/libs/flake/KoInputDevice.cpp @@ -39,6 +39,7 @@ { } +// Should this return Stylus by default?? KoInputDevice::KoInputDevice() : d(new Private(QTabletEvent::NoDevice, QTabletEvent::UnknownPointer, -1, true)) { @@ -85,6 +86,13 @@ d->uniqueTabletId == other.d->uniqueTabletId && d->mouse == other.d->mouse; } +// bool KoInputDevice::sameDevice(const KoInputDevice &other) const +// { +// return d->device == other.d->device && +// d->uniqueTabletId == other.d->uniqueTabletId && +// d->mouse == other.d->mouse; +// } + bool KoInputDevice::operator!=(const KoInputDevice &other) const { return !(operator==(other)); diff --git a/libs/flake/KoToolManager.h b/libs/flake/KoToolManager.h --- a/libs/flake/KoToolManager.h +++ b/libs/flake/KoToolManager.h @@ -256,6 +256,17 @@ void switchInputDeviceRequested(const KoInputDevice &id); /** + * Request using an eraser as the default tool. + */ + void setEraserMode(bool mode); + + /** + * Request changing to default input device. + * @param id the id of the input device + */ + void resetInputDeviceRequested(); + + /** * a new tool has become known to mankind */ void addDeferredToolFactory(KoToolFactoryBase *toolFactory); @@ -322,6 +333,7 @@ */ void addedTool(KoToolAction *toolAction, KoCanvasController *canvas); + private: KoToolManager(const KoToolManager&); KoToolManager operator=(const KoToolManager&); diff --git a/libs/flake/KoToolManager.cpp b/libs/flake/KoToolManager.cpp --- a/libs/flake/KoToolManager.cpp +++ b/libs/flake/KoToolManager.cpp @@ -70,12 +70,13 @@ class CanvasData { public: - CanvasData(KoCanvasController *cc, const KoInputDevice &id) - : activeTool(0), - canvas(cc), - inputDevice(id), - dummyToolWidget(0), - dummyToolLabel(0) + CanvasData(KoCanvasController *cc, const KoInputDevice &id, bool eraser) + : activeTool(0) + , canvas(cc) + , inputDevice(id) + , dummyToolWidget(0) + , dummyToolLabel(0) + , eraserMode(eraser) { } @@ -173,18 +174,26 @@ disabledCanvasShortcuts.clear(); } - KoToolBase *activeTool; // active Tool - QString activeToolId; // the id of the active Tool + KoToolBase *activeTool; // active Tool + QString activeToolId; // the id of the active Tool QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to. QHash allTools; // all the tools that are created for this canvas. QStack stack; // stack of temporary tools KoCanvasController *const canvas; const KoInputDevice inputDevice; + KoInputDevice eraserStashedDevice; QWidget *dummyToolWidget; // the widget shown in the toolDocker. QLabel *dummyToolLabel; QList > disabledActions; ///< disabled conflicting actions QList > disabledDisabledActions; ///< disabled conflicting actions that were already disabled QMap, QString> disabledCanvasShortcuts; ///< Shortcuts that were temporarily removed from canvas actions because the tool overrides + + + /// Save the true device that was used when eraser mode was activated. + /// Pressing "eraser mode" twice in a row returns to the original tool. + /// (This would be better to do per-canvas instead of globally.) + KoInputDevice stashedEraserDevice; + bool eraserMode; }; @@ -321,6 +330,18 @@ d->switchInputDevice(id); } +void KoToolManager::setEraserMode(bool mode) +{ + d->setEraserMode(mode); +} + +void KoToolManager::resetInputDeviceRequested() +{ + if (!d->canvasData) return; + d->resetInputDevice(); +} + + void KoToolManager::switchToolTemporaryRequested(const QString &id) { d->switchTool(id, true); @@ -534,7 +555,7 @@ } // helper method. -CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device) +CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device, bool eraserMode) { QHash toolsHash; Q_FOREACH (ToolHelper *tool, tools) { @@ -548,7 +569,7 @@ QString id = KoShapeRegistry::instance()->keys()[0]; createShapesTool->setShapeId(id); - CanvasData *cd = new CanvasData(controller, device); + CanvasData *cd = new CanvasData(controller, device, eraserMode); cd->allTools = toolsHash; return cd; } @@ -592,6 +613,20 @@ } +void KoToolManager::Private::setEraserMode(bool mode) +{ + // Update ALL canvasdatas associated with the current canvas + Q_FOREACH (auto cd, canvasses[canvasData->canvas]) { + cd->eraserMode = mode; + } + + if (mode) { + canvasData->stashedEraserDevice = inputDevice; + switchInputDevice(inputDevice); + } else { + switchInputDevice(canvasData->stashedEraserDevice); + } +} void KoToolManager::Private::disconnectActiveTool() { @@ -848,7 +883,7 @@ void KoToolManager::Private::attachCanvas(KoCanvasController *controller) { Q_ASSERT(controller); - CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse()); + CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse(), false); // switch to new canvas as the active one. switchCanvasData(cd); @@ -1006,17 +1041,36 @@ proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0); } -void KoToolManager::Private::switchInputDevice(const KoInputDevice &device) + +void KoToolManager::Private::resetInputDevice() +{ + switchInputDevice(KoInputDevice::stylus()); +} + +void KoToolManager::Private::switchInputDevice(const KoInputDevice &hardwareDevice) { Q_ASSERT(canvasData); if (!canvasData) return; + + // Override with eraser if we are in eraser mode + KoInputDevice device; + if (canvasData->eraserMode) { + device = KoInputDevice(hardwareDevice.device(), + QTabletEvent::Eraser, + hardwareDevice.uniqueTabletId()); + } else { + device = hardwareDevice; + } + + + if (inputDevice == device) return; if (inputDevice.isMouse() && device.isMouse()) return; if (device.isMouse() && !inputDevice.isMouse()) { // we never switch back to mouse from a tablet input device, so the user can use the // mouse to edit the settings for a tool activated by a tablet. See bugs // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501. - // We do continue to switch between tablet devices, thought. + // We do continue to switch between tablet devices, though. return; } @@ -1045,7 +1099,7 @@ } // still here? That means we need to create a new CanvasData instance with the current InputDevice. - CanvasData *cd = createCanvasData(canvasData->canvas, device); + CanvasData *cd = createCanvasData(canvasData->canvas, device, canvasData->eraserMode); // switch to new canvas as the active one. QString oldTool = canvasData->activeToolId; diff --git a/libs/flake/KoToolManager_p.h b/libs/flake/KoToolManager_p.h --- a/libs/flake/KoToolManager_p.h +++ b/libs/flake/KoToolManager_p.h @@ -53,6 +53,7 @@ void connectActiveTool(); void disconnectActiveTool(); + void setEraserMode(bool mode); void switchTool(KoToolBase *tool, bool temporary); void switchTool(const QString &id, bool temporary); void postSwitchTool(bool temporary); @@ -70,13 +71,19 @@ void currentLayerChanged(const KoShapeLayer *layer); void updateToolForProxy(); void switchToolTemporaryRequested(const QString &id); - CanvasData *createCanvasData(KoCanvasController *controller, const KoInputDevice &device); + CanvasData *createCanvasData(KoCanvasController *controller, const KoInputDevice &device, bool eraserMode); /** * Request a switch from to the param input device. * This will cause the tool for that device to be selected. */ - void switchInputDevice(const KoInputDevice &device); + void switchInputDevice(const KoInputDevice &hardwareDevice); + + + /** + * Request a switch to the default input device. + */ + void resetInputDevice(); /** * Whenever a new tool proxy class is instantiated, it will use this method to register itself diff --git a/libs/flake/KoToolProxy.cpp b/libs/flake/KoToolProxy.cpp --- a/libs/flake/KoToolProxy.cpp +++ b/libs/flake/KoToolProxy.cpp @@ -182,8 +182,7 @@ KoPointerEvent ev(event, point, touchPoints); - KoInputDevice id; - KoToolManager::instance()->priv()->switchInputDevice(id); + KoToolManager::instance()->priv()->resetInputDevice(); switch (event->type()) { case QEvent::TouchBegin: @@ -269,8 +268,7 @@ void KoToolProxy::mousePressEvent(KoPointerEvent *ev) { d->mouseLeaveWorkaround = false; - KoInputDevice id; - KoToolManager::instance()->priv()->switchInputDevice(id); + KoToolManager::instance()->priv()->resetInputDevice(); d->mouseDownPoint = ev->pos(); if (d->tabletPressed) // refuse to send a press unless there was a release first. @@ -339,8 +337,7 @@ d->mouseLeaveWorkaround = false; return; } - KoInputDevice id; - KoToolManager::instance()->priv()->switchInputDevice(id); + KoToolManager::instance()->priv()->resetInputDevice(); if (d->activeTool == 0) { event->ignore(); return; @@ -358,8 +355,8 @@ d->mouseLeaveWorkaround = false; return; } - KoInputDevice id; - KoToolManager::instance()->priv()->switchInputDevice(id); + + KoToolManager::instance()->priv()->resetInputDevice(); if (d->activeTool == 0) { event->ignore(); return; @@ -373,8 +370,7 @@ void KoToolProxy::mouseReleaseEvent(QMouseEvent *event, const QPointF &point) { d->mouseLeaveWorkaround = false; - KoInputDevice id; - KoToolManager::instance()->priv()->switchInputDevice(id); + KoToolManager::instance()->priv()->resetInputDevice(); d->scrollTimer.stop(); KoPointerEvent ev(event, point); @@ -409,8 +405,7 @@ void KoToolProxy::mouseReleaseEvent(KoPointerEvent* event) { d->mouseLeaveWorkaround = false; - KoInputDevice id; - KoToolManager::instance()->priv()->switchInputDevice(id); + KoToolManager::instance()->priv()->resetInputDevice(); d->scrollTimer.stop(); if (d->activeTool) { diff --git a/libs/widgetutils/kis_action_registry.h b/libs/widgetutils/kis_action_registry.h --- a/libs/widgetutils/kis_action_registry.h +++ b/libs/widgetutils/kis_action_registry.h @@ -66,7 +66,7 @@ QKeySequence getCategory(const QString &name); /** - * @return DOM info for an action @a name. Might be private. + * @return DOM info for an action @a name. THIS IS BAD. MAKE IT PRIVATE. * * Allows somewhat flexible info structure for KisActions, QActions, * whatever else we decide on doing later.