diff --git a/libs/ui/forms/wdgselectionoptions.ui b/libs/ui/forms/wdgselectionoptions.ui index 4f5427e9c9..7231b4c46d 100644 --- a/libs/ui/forms/wdgselectionoptions.ui +++ b/libs/ui/forms/wdgselectionoptions.ui @@ -1,195 +1,195 @@ WdgSelectionOptions 0 0 271 106 0 0 0 0 0 0 Intersect ... true false Vector Selection ... true false - Replace (Shortcut R) + Replace ... true true false - Add (Shortcut A) + Add ... true false CrossCursor Mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Action: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Pixel Selection ... true true false - Subtract (Shortcut S) + Subtract ... true false Qt::Horizontal 0 10 Anti-aliasing true KoGroupButton QToolButton
KoGroupButton.h
diff --git a/libs/ui/tool/kis_tool_select_base.h b/libs/ui/tool/kis_tool_select_base.h index ebfe759ae4..47fa3eb928 100644 --- a/libs/ui/tool/kis_tool_select_base.h +++ b/libs/ui/tool/kis_tool_select_base.h @@ -1,383 +1,405 @@ /* This file is part of the KDE project * Copyright (C) 2009 Boudewijn Rempt * Copyright (C) 2015 Michael Abrahams * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISTOOLSELECTBASE_H #define KISTOOLSELECTBASE_H #include "KoPointerEvent.h" #include "kis_tool.h" #include "kis_canvas2.h" #include "kis_selection.h" #include "kis_selection_options.h" #include "kis_selection_tool_config_widget_helper.h" #include "KisViewManager.h" #include "kis_selection_manager.h" #include "kis_selection_modifier_mapper.h" #include "strokes/move_stroke_strategy.h" #include "kis_image.h" #include "kis_cursor.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_signal_auto_connection.h" /** * This is a basic template to create selection tools from basic path based drawing tools. * The template overrides the ability to execute alternate actions correctly. * The default behavior for the modifier keys is as follows: * * Shift: add to selection * Alt: subtract from selection * Shift+Alt: intersect current selection * Ctrl: replace selection * * The mapping itself is done in KisSelectionModifierMapper. * * Certain tools also use modifier keys to alter their behavior, e.g. forcing square proportions with the rectangle tool. * The template enables the following rules for forwarding keys: * 1) Any modifier keys held *when the tool is first activated* will determine * the new selection method. This is recorded in m_selectionActionAlternate. A * value of m_selectionActionAlternate = SELECTION_DEFAULT means no modifier was * being pressed when the tool was activated. * * 2) If the underlying tool *does not take modifier keys*, pressing modifier * keys in the middle of a stroke will change the selection method. This is * recorded in m_selectionAction. A value of SELECTION_DEFAULT means no modifier * is being pressed. Applies to the lasso tool and polygon tool. * * 3) If the underlying tool *takes modifier keys,* they will always be * forwarded to the underlying tool, and it is not possible to change the * selection method in the middle of a stroke. */ template class KisToolSelectBase : public BaseClass { public: KisToolSelectBase(KoCanvasBase* canvas, const QString toolName) :BaseClass(canvas), m_widgetHelper(toolName), m_selectionAction(SELECTION_DEFAULT), m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); initShortcuts(); } KisToolSelectBase(KoCanvasBase* canvas, const QCursor cursor, const QString toolName) :BaseClass(canvas, cursor), m_widgetHelper(toolName), m_selectionAction(SELECTION_DEFAULT), m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); initShortcuts(); } KisToolSelectBase(KoCanvasBase* canvas, QCursor cursor, QString toolName, KisTool *delegateTool) :BaseClass(canvas, cursor, delegateTool), m_widgetHelper(toolName), m_selectionAction(SELECTION_DEFAULT), m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); initShortcuts(); } void initShortcuts() { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); KisActionManager *manager = viewManager->actionManager(); KisAction *action = 0; action = manager->createAction("selection_tool_mode_add"); this->addAction(action->objectName(), action); action = manager->createAction("selection_tool_mode_replace"); this->addAction(action->objectName(), action); action = manager->createAction("selection_tool_mode_subtract"); this->addAction(action->objectName(), action); action = manager->createAction("selection_tool_mode_intersect"); this->addAction(action->objectName(), action); } + void updateActionShortcutToolTips() { + KisSelectionOptions *widget = m_widgetHelper.optionWidget(); + if (widget) { + widget->updateActionButtonToolTip( + SELECTION_REPLACE, + this->action("selection_tool_mode_replace")->shortcut()); + widget->updateActionButtonToolTip( + SELECTION_ADD, + this->action("selection_tool_mode_add")->shortcut()); + widget->updateActionButtonToolTip( + SELECTION_SUBTRACT, + this->action("selection_tool_mode_subtract")->shortcut()); + widget->updateActionButtonToolTip( + SELECTION_INTERSECT, + this->action("selection_tool_mode_intersect")->shortcut()); + } + } + void activate(KoToolBase::ToolActivation activation, const QSet &shapes) { BaseClass::activate(activation, shapes); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_replace"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotReplaceModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_add"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotAddModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_subtract"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotSubtractModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_intersect"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotIntersectModeRequested())); + updateActionShortcutToolTips(); } void deactivate() { BaseClass::deactivate(); m_modeConnections.clear(); } QWidget* createOptionWidget() { KisCanvas2* canvas = dynamic_cast(this->canvas()); Q_ASSERT(canvas); m_widgetHelper.createOptionWidget(canvas, this->toolId()); this->connect(this, SIGNAL(isActiveChanged(bool)), &m_widgetHelper, SLOT(slotToolActivatedChanged(bool))); + + updateActionShortcutToolTips(); + return m_widgetHelper.optionWidget(); } SelectionMode selectionMode() const { return m_widgetHelper.selectionMode(); } SelectionAction selectionAction() const { if (alternateSelectionAction() == SELECTION_DEFAULT) { return m_widgetHelper.selectionAction(); } return alternateSelectionAction(); } bool antiAliasSelection() const { return m_widgetHelper.optionWidget()->antiAliasSelection(); } SelectionAction alternateSelectionAction() const { return m_selectionActionAlternate; } KisSelectionOptions* selectionOptionWidget() { return m_widgetHelper.optionWidget(); } virtual void setAlternateSelectionAction(SelectionAction action) { m_selectionActionAlternate = action; dbgKrita << "Changing to selection action" << m_selectionActionAlternate; } void activateAlternateAction(KisTool::AlternateAction action) { Q_UNUSED(action); BaseClass::activatePrimaryAction(); } void deactivateAlternateAction(KisTool::AlternateAction action) { Q_UNUSED(action); BaseClass::deactivatePrimaryAction(); } void beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); beginPrimaryAction(event); } void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); continuePrimaryAction(event); } void endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); endPrimaryAction(event); } KisNodeSP locateSelectionMaskUnderCursor(const QPointF &pos, Qt::KeyboardModifiers modifiers) { if (modifiers != Qt::NoModifier) return 0; KisCanvas2* canvas = dynamic_cast(this->canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(canvas, 0); KisSelectionSP selection = canvas->viewManager()->selection(); if (selection && selection->outlineCacheValid() && selection->outlineCache().contains(pos)) { KisNodeSP parent = selection->parentNode(); if (parent && parent->isEditable()) { return parent; } } return 0; } void mouseMoveEvent(KoPointerEvent *event) { if (!this->hasUserInteractionRunning()) { const QPointF pos = this->convertToPixelCoord(event->point); KisNodeSP selectionMask = locateSelectionMaskUnderCursor(pos, event->modifiers()); if (selectionMask) { this->useCursor(KisCursor::moveCursor()); } else { this->resetCursorStyle(); } } BaseClass::mouseMoveEvent(event); } virtual void beginPrimaryAction(KoPointerEvent *event) { if (!this->hasUserInteractionRunning()) { const QPointF pos = this->convertToPixelCoord(event->point); KisCanvas2* canvas = dynamic_cast(this->canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN(canvas); KisNodeSP selectionMask = locateSelectionMaskUnderCursor(pos, event->modifiers()); if (selectionMask) { KisStrokeStrategy *strategy = new MoveStrokeStrategy({selectionMask}, this->image().data(), this->image().data()); m_moveStrokeId = this->image()->startStroke(strategy); m_dragStartPos = pos; return; } } keysAtStart = event->modifiers(); setAlternateSelectionAction(KisSelectionModifierMapper::map(keysAtStart)); if (alternateSelectionAction() != SELECTION_DEFAULT) { BaseClass::listenToModifiers(false); } BaseClass::beginPrimaryAction(event); } virtual void continuePrimaryAction(KoPointerEvent *event) { if (m_moveStrokeId) { const QPointF pos = this->convertToPixelCoord(event->point); const QPoint offset((pos - m_dragStartPos).toPoint()); this->image()->addJob(m_moveStrokeId, new MoveStrokeStrategy::Data(offset)); return; } //If modifier keys have changed, tell the base tool it can start capturing modifiers if ((keysAtStart != event->modifiers()) && !BaseClass::listeningToModifiers()) { BaseClass::listenToModifiers(true); } //Always defer to the base class if it signals it is capturing modifier keys if (!BaseClass::listeningToModifiers()) { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); } BaseClass::continuePrimaryAction(event); } void endPrimaryAction(KoPointerEvent *event) { if (m_moveStrokeId) { this->image()->endStroke(m_moveStrokeId); m_moveStrokeId.clear(); return; } keysAtStart = Qt::NoModifier; //reset this with each action BaseClass::endPrimaryAction(event); } void changeSelectionAction(int newSelectionAction) { // Simple sanity check if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction) { m_selectionAction = (SelectionAction)newSelectionAction; } } bool selectionDragInProgress() const { return m_moveStrokeId; } protected: using BaseClass::canvas; KisSelectionToolConfigWidgetHelper m_widgetHelper; SelectionAction m_selectionAction; SelectionAction m_selectionActionAlternate; private: Qt::KeyboardModifiers keysAtStart; QPointF m_dragStartPos; KisStrokeId m_moveStrokeId; KisSignalAutoConnectionsStore m_modeConnections; }; struct FakeBaseTool : KisTool { FakeBaseTool(KoCanvasBase* canvas) : KisTool(canvas, QCursor()) { } FakeBaseTool(KoCanvasBase* canvas, const QString &toolName) : KisTool(canvas, QCursor()) { Q_UNUSED(toolName); } FakeBaseTool(KoCanvasBase* canvas, const QCursor &cursor) : KisTool(canvas, cursor) { } bool hasUserInteractionRunning() const { return false; } }; typedef KisToolSelectBase KisToolSelect; #endif // KISTOOLSELECTBASE_H diff --git a/libs/ui/widgets/kis_selection_options.cc b/libs/ui/widgets/kis_selection_options.cc index 6f86500d28..5b01b7cf9a 100644 --- a/libs/ui/widgets/kis_selection_options.cc +++ b/libs/ui/widgets/kis_selection_options.cc @@ -1,125 +1,164 @@ /* * Copyright (c) 2005 Boudewijn Rempt * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_options.h" #include #include #include #include #include #include #include #include "kis_types.h" #include "kis_layer.h" #include "kis_image.h" #include "kis_selection.h" #include "kis_paint_device.h" #include "canvas/kis_canvas2.h" #include "KisViewManager.h" KisSelectionOptions::KisSelectionOptions(KisCanvas2 * /*canvas*/) { m_page = new WdgSelectionOptions(this); Q_CHECK_PTR(m_page); QVBoxLayout * l = new QVBoxLayout(this); l->addWidget(m_page); l->addSpacerItem(new QSpacerItem(0,0, QSizePolicy::Preferred, QSizePolicy::Expanding)); l->setContentsMargins(0,0,0,0); m_mode = new QButtonGroup(this); m_mode->addButton(m_page->pixel, PIXEL_SELECTION); m_mode->addButton(m_page->shape, SHAPE_PROTECTION); m_action = new QButtonGroup(this); m_action->addButton(m_page->add, SELECTION_ADD); m_action->addButton(m_page->subtract, SELECTION_SUBTRACT); m_action->addButton(m_page->replace, SELECTION_REPLACE); m_action->addButton(m_page->intersect, SELECTION_INTERSECT); m_page->pixel->setGroupPosition(KoGroupButton::GroupLeft); m_page->shape->setGroupPosition(KoGroupButton::GroupRight); m_page->pixel->setIcon(KisIconUtils::loadIcon("select_pixel")); m_page->shape->setIcon(KisIconUtils::loadIcon("select_shape")); m_page->add->setGroupPosition(KoGroupButton::GroupCenter); m_page->subtract->setGroupPosition(KoGroupButton::GroupRight); m_page->replace->setGroupPosition(KoGroupButton::GroupLeft); m_page->intersect->setGroupPosition(KoGroupButton::GroupCenter); m_page->add->setIcon(KisIconUtils::loadIcon("selection_add")); m_page->subtract->setIcon(KisIconUtils::loadIcon("selection_subtract")); m_page->replace->setIcon(KisIconUtils::loadIcon("selection_replace")); m_page->intersect->setIcon(KisIconUtils::loadIcon("selection_intersect")); connect(m_mode, SIGNAL(buttonClicked(int)), this, SIGNAL(modeChanged(int))); connect(m_action, SIGNAL(buttonClicked(int)), this, SIGNAL(actionChanged(int))); connect(m_mode, SIGNAL(buttonClicked(int)), this, SLOT(hideActionsForSelectionMode(int))); } KisSelectionOptions::~KisSelectionOptions() { } int KisSelectionOptions::action() { return m_action->checkedId(); } void KisSelectionOptions::setAction(int action) { QAbstractButton* button = m_action->button(action); KIS_SAFE_ASSERT_RECOVER_RETURN(button); button->setChecked(true); } void KisSelectionOptions::setMode(int mode) { QAbstractButton* button = m_mode->button(mode); KIS_SAFE_ASSERT_RECOVER_RETURN(button); button->setChecked(true); hideActionsForSelectionMode(mode); } +void KisSelectionOptions::updateActionButtonToolTip(int action, const QKeySequence &shortcut) +{ + const QString shortcutString = shortcut.toString(QKeySequence::NativeText); + QString toolTipText; + switch ((SelectionAction)action) { + case SELECTION_DEFAULT: + case SELECTION_REPLACE: + toolTipText = shortcutString.isEmpty() ? + i18nc("@info:tooltip", "Replace") : + i18nc("@info:tooltip", "Replace (%1)", shortcutString); + + m_action->button(SELECTION_REPLACE)->setToolTip(toolTipText); + break; + case SELECTION_ADD: + toolTipText = shortcutString.isEmpty() ? + i18nc("@info:tooltip", "Add") : + i18nc("@info:tooltip", "Add (%1)", shortcutString); + + m_action->button(SELECTION_ADD)->setToolTip(toolTipText); + break; + case SELECTION_SUBTRACT: + toolTipText = shortcutString.isEmpty() ? + i18nc("@info:tooltip", "Subtract") : + i18nc("@info:tooltip", "Subtract (%1)", shortcutString); + + m_action->button(SELECTION_SUBTRACT)->setToolTip(toolTipText); + + break; + case SELECTION_INTERSECT: + toolTipText = shortcutString.isEmpty() ? + i18nc("@info:tooltip", "Intersect") : + i18nc("@info:tooltip", "Intersect (%1)", shortcutString); + + m_action->button(SELECTION_INTERSECT)->setToolTip(toolTipText); + + break; + } +} + //hide action buttons and antialiasing, if shape selection is active (actions currently don't work on shape selection) void KisSelectionOptions::hideActionsForSelectionMode(int mode) { const bool isPixelSelection = (mode == (int)PIXEL_SELECTION); m_page->chkAntiAliasing->setVisible(isPixelSelection); } bool KisSelectionOptions::antiAliasSelection() { return m_page->chkAntiAliasing->isChecked(); } void KisSelectionOptions::disableAntiAliasSelectionOption() { m_page->chkAntiAliasing->hide(); disconnect(m_page->pixel, SIGNAL(clicked()), m_page->chkAntiAliasing, SLOT(show())); } void KisSelectionOptions::disableSelectionModeOption() { m_page->lblMode->hide(); m_page->pixel->hide(); m_page->shape->hide(); } diff --git a/libs/ui/widgets/kis_selection_options.h b/libs/ui/widgets/kis_selection_options.h index db8d780088..b79bec8ee6 100644 --- a/libs/ui/widgets/kis_selection_options.h +++ b/libs/ui/widgets/kis_selection_options.h @@ -1,74 +1,77 @@ /* * Copyright (c) 2005 Boudewijn Rempt * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_SELECTION_OPTIONS_H__ #define __KIS_SELECTION_OPTIONS_H__ #include #include "kritaui_export.h" #include "ui_wdgselectionoptions.h" class KisCanvas2; class QButtonGroup; +class QKeySequence; class WdgSelectionOptions : public QWidget, public Ui::WdgSelectionOptions { Q_OBJECT public: WdgSelectionOptions(QWidget *parent) : QWidget(parent) { setupUi(this); } }; /** */ class KRITAUI_EXPORT KisSelectionOptions : public QWidget { Q_OBJECT public: KisSelectionOptions(KisCanvas2 * subject); ~KisSelectionOptions() override; int action(); bool antiAliasSelection(); void disableAntiAliasSelectionOption(); void disableSelectionModeOption(); void setAction(int); void setMode(int); + void updateActionButtonToolTip(int action, const QKeySequence &shortcut); + Q_SIGNALS: void actionChanged(int); void modeChanged(int); private Q_SLOTS: void hideActionsForSelectionMode(int mode); private: WdgSelectionOptions * m_page; QButtonGroup* m_mode; QButtonGroup* m_action; }; #endif