Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tool_select_base.h
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2015 Michael Abrahams <miabraha@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#ifndef KISTOOLSELECTBASE_H
9#define KISTOOLSELECTBASE_H
10
11#include "KoPointerEvent.h"
12#include "kis_tool.h"
13#include "kis_canvas2.h"
14#include "kis_selection.h"
17#include "KisViewManager.h"
21#include "kis_image.h"
22#include "kis_cursor.h"
23#include "kis_action_manager.h"
24#include "kis_action.h"
27#include "kis_assert.h"
29
60template <class BaseClass>
61class KisToolSelectBase : public BaseClass
62{
63
64public:
65
66 KisToolSelectBase(KoCanvasBase* canvas, const QString toolName)
67 : BaseClass(canvas)
68 , m_widgetHelper(toolName)
70 {
72 }
73
74 KisToolSelectBase(KoCanvasBase* canvas, const QCursor cursor, const QString toolName)
75 : BaseClass(canvas, cursor)
76 , m_widgetHelper(toolName)
78 {
80 }
81
82 KisToolSelectBase(KoCanvasBase* canvas, QCursor cursor, QString toolName, KoToolBase *delegateTool)
83 : BaseClass(canvas, cursor, delegateTool)
84 , m_widgetHelper(toolName)
86 {
88 }
89
96
99 if (widget) {
102 this->action("selection_tool_mode_replace")->shortcut());
105 this->action("selection_tool_mode_add")->shortcut());
108 this->action("selection_tool_mode_subtract")->shortcut());
111 this->action("selection_tool_mode_intersect")->shortcut());
112 }
113 }
114
115 void activate(const QSet<KoShape *> &shapes) override
116 {
117 BaseClass::activate(shapes);
118
120 this->action("selection_tool_mode_replace"), SIGNAL(triggered()),
121 &m_widgetHelper, SLOT(slotReplaceModeRequested()));
122
124 this->action("selection_tool_mode_add"), SIGNAL(triggered()),
125 &m_widgetHelper, SLOT(slotAddModeRequested()));
126
128 this->action("selection_tool_mode_subtract"), SIGNAL(triggered()),
129 &m_widgetHelper, SLOT(slotSubtractModeRequested()));
130
132 this->action("selection_tool_mode_intersect"), SIGNAL(triggered()),
133 &m_widgetHelper, SLOT(slotIntersectModeRequested()));
134
136
138 if (isPixelOnly()) {
141 true);
142 }
145 }
146 }
147
148 void deactivate() override
149 {
150 BaseClass::deactivate();
152 }
153
154 QWidget *createOptionWidget() override
155 {
156 m_widgetHelper.createOptionWidget(this->toolId());
158
159 this->connect(this, SIGNAL(isActiveChanged(bool)), &m_widgetHelper, SLOT(slotToolActivatedChanged(bool)));
160 this->connect(&m_widgetHelper,
161 SIGNAL(selectionActionChanged(SelectionAction)),
162 this,
163 SLOT(resetCursorStyle()));
164
167 m_widgetHelper.optionWidget()->setContentsMargins(0, 10, 0, 10);
168 if (isPixelOnly()) {
171 true);
172 }
175 }
176
178 }
179
181 {
183 }
184
192
194 {
196 }
197
198 int growSelection() const
199 {
201 }
202
207
209 {
211 }
212
217
219 {
222 if (referenceLayers == KisSelectionOptions::AllLayers) {
223 return SampleAllLayers;
224 } else if (referenceLayers == KisSelectionOptions::CurrentLayer) {
225 return SampleCurrentLayer;
226 } else if (referenceLayers == KisSelectionOptions::ColorLabeledLayers) {
228 }
230 return SampleAllLayers;
231 }
232
237
242
244 {
246 }
247
249 {
250 Q_UNUSED(action);
251 BaseClass::activatePrimaryAction();
252 }
253
255 {
256 Q_UNUSED(action);
257 BaseClass::deactivatePrimaryAction();
258 }
259
261 KisTool::AlternateAction action) override
262 {
263 Q_UNUSED(action);
264 beginPrimaryAction(event);
265 }
266
268 KisTool::AlternateAction action) override
269 {
270 Q_UNUSED(action);
272 }
273
275 KisTool::AlternateAction action) override
276 {
277 Q_UNUSED(action);
278 endPrimaryAction(event);
279 }
280
281 KisNodeSP locateSelectionMaskUnderCursor(const QPointF &pos, Qt::KeyboardModifiers modifiers) {
282 if (modifiers != Qt::NoModifier) return 0;
283
284 KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
286
287 KisSelectionSP selection = canvas->viewManager()->selection();
288 if (selection &&
289 selection->outlineCacheValid()) {
290
291 const qreal handleRadius = qreal(this->handleRadius()) / canvas->coordinatesConverter()->effectiveZoom();
292 QPainterPath samplePath;
293 samplePath.addEllipse(pos, handleRadius, handleRadius);
294
295 const QPainterPath selectionPath = selection->outlineCache();
296
297 if (selectionPath.intersects(samplePath) && !selectionPath.contains(samplePath)) {
298 KisNodeSP parent = selection->parentNode();
299 if (parent && parent->isEditable()) {
300 return parent;
301 }
302 }
303 }
304
305 return 0;
306 }
307
308 void keyPressEvent(QKeyEvent *event) override
309 {
311 // Assume all the modifiers were unpressed...
312 m_currentModifiers = Qt::NoModifier;
313 // ...and add those which are right now
314 if (key == Qt::Key_Control || event->modifiers().testFlag(Qt::ControlModifier)) {
315 m_currentModifiers.setFlag(Qt::ControlModifier);
316 }
317 if (key == Qt::Key_Shift || event->modifiers().testFlag(Qt::ShiftModifier)) {
318 m_currentModifiers.setFlag(Qt::ShiftModifier);
319 }
320 if (key == Qt::Key_Alt || event->modifiers().testFlag(Qt::AltModifier)) {
321 m_currentModifiers.setFlag(Qt::AltModifier);
322 }
323
324 // Avoid changing the selection mode and cursor if the user is interacting
325 if (isSelecting()) {
326 BaseClass::keyPressEvent(event);
327 return;
328 }
329 if (isMovingSelection()) {
330 return;
331 }
332
334 this->resetCursorStyle();
335 }
336
337 void keyReleaseEvent(QKeyEvent *event) override
338 {
340 // Assume all the modifiers were pressed...
341 m_currentModifiers = Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier;
342 // ...and remove those which aren't right now
343 if (key == Qt::Key_Control || !event->modifiers().testFlag(Qt::ControlModifier)) {
344 m_currentModifiers.setFlag(Qt::ControlModifier, false);
345 }
346 if (key == Qt::Key_Shift || !event->modifiers().testFlag(Qt::ShiftModifier)) {
347 m_currentModifiers.setFlag(Qt::ShiftModifier, false);
348 }
349 if (key == Qt::Key_Alt || !event->modifiers().testFlag(Qt::AltModifier)) {
350 m_currentModifiers.setFlag(Qt::AltModifier, false);
351 }
352
353 // Avoid changing the selection mode and cursor if the user is interacting
354 if (isSelecting()) {
355 BaseClass::keyReleaseEvent(event);
356 return;
357 }
358 if (isMovingSelection()) {
359 return;
360 }
361
363 if (m_currentModifiers == Qt::NoModifier) {
365 if (selectionMask) {
366 this->useCursor(KisCursor::moveSelectionCursor());
367 } else {
368 this->resetCursorStyle();
369 }
370 } else {
371 this->resetCursorStyle();
372 }
373 }
374
375 void mouseMoveEvent(KoPointerEvent *event) override
376 {
377 m_currentPos = this->convertToPixelCoord(event->point);
378
379 if (isSelecting()) {
380 BaseClass::mouseMoveEvent(event);
381 return;
382 }
383 if (isMovingSelection()) {
384 return;
385 }
386
388 if (selectionMask) {
389 this->useCursor(KisCursor::moveSelectionCursor());
390 } else {
392 this->resetCursorStyle();
393 }
394 }
395
396 void beginPrimaryAction(KoPointerEvent *event) override
397 {
398 if (isSelecting()) {
399 BaseClass::beginPrimaryAction(event);
400 return;
401 }
402 if (isMovingSelection()) {
403 return;
404 }
405
406 const QPointF pos = this->convertToPixelCoord(event->point);
407 KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
409
410 KisNodeSP selectionMask = locateSelectionMaskUnderCursor(pos, event->modifiers());
411 if (selectionMask) {
412 if (this->beginMoveSelectionInteraction()) {
413 KisStrokeStrategy *strategy = new MoveStrokeStrategy({selectionMask}, this->image().data(), this->image().data());
414 m_moveStrokeId = this->image()->startStroke(strategy);
415 m_dragStartPos = pos;
416 m_didMove = true;
417 return;
418 }
419 }
420
421 m_didMove = false;
422 BaseClass::beginPrimaryAction(event);
423 }
424
426 {
427 if (isMovingSelection()) {
428 const QPointF pos = this->convertToPixelCoord(event->point);
429 const QPoint offset((pos - m_dragStartPos).toPoint());
430
431 this->image()->addJob(m_moveStrokeId, new MoveStrokeStrategy::Data(offset));
432 return;
433 }
434
435 BaseClass::continuePrimaryAction(event);
436 }
437
438 void endPrimaryAction(KoPointerEvent *event) override
439 {
440 if (isMovingSelection()) {
441 this->image()->endStroke(m_moveStrokeId);
442 m_moveStrokeId.clear();
444 return;
445 }
446
447 BaseClass::endPrimaryAction(event);
448 }
449
450 bool selectionDidMove() const
451 {
452 return m_didMove;
453 }
454
455 QMenu *popupActionsMenu() override
456 {
457 if (isSelecting()) {
458 return BaseClass::popupActionsMenu();
459 }
460
461 KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
463
465 }
466
468 {
469 if (isSelecting()) {
470 return BaseClass::popupWidget();
471 }
472 return nullptr;
473 }
474
477 return false;
478 }
480 return true;
481 }
482
484 if (!isMovingSelection()) {
485 return false;
486 }
489 return true;
490 }
491
494 return false;
495 }
497 return true;
498 }
499
501 if (!isSelecting()) {
502 return false;
503 }
506 return true;
507 }
508
512
513 bool isSelecting() const {
515 }
516
519 QTimer::singleShot(100, Qt::CoarseTimer,
520 this,
521 [this]()
522 {
524 if (selectionMask) {
525 this->useCursor(KisCursor::moveSelectionCursor());
526 } else {
527 this->resetCursorStyle();
528 }
529 }
530 );
531 }
532
533protected:
534 using BaseClass::canvas;
537
538 virtual bool isPixelOnly() const {
539 return false;
540 }
541
542 virtual bool usesColorLabels() const {
543 return false;
544 }
545
546private:
553
555
556 Qt::KeyboardModifiers m_currentModifiers;
557
561 bool m_didMove = false;
562
564};
565
567{
569 : KisTool(canvas, QCursor())
570 {
571 }
572
573 FakeBaseTool(KoCanvasBase* canvas, const QString &toolName)
574 : KisTool(canvas, QCursor())
575 {
576 Q_UNUSED(toolName);
577 }
578
581 {
582 }
583};
584
585
587
588
589#endif // KISTOOLSELECTBASE_H
SelectionMode
SelectionAction
@ SELECTION_REPLACE
@ SELECTION_INTERSECT
@ SELECTION_DEFAULT
@ SELECTION_SUBTRACT
@ SELECTION_ADD
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisCoordinatesConverter * coordinatesConverter
KisViewManager * viewManager() const
static QCursor moveSelectionCursor()
static Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent)
The PopupWidgetInterface abstract class defines the basic interface that will be used by all popup wi...
void updateActionButtonToolTip(SelectionAction action, const QKeySequence &shortcut)
void setModeSectionVisible(bool visible)
void setAdjustmentsSectionVisible(bool visible)
void setReferenceSectionVisible(bool visible)
KisSelectionOptions::ReferenceLayers referenceLayers() const
static QMenu * getSelectionContextMenu(KisCanvas2 *canvas)
void addUniqueConnection(Sender sender, Signal signal, Receiver receiver, Method method)
SelectionAction alternateSelectionAction() const
KisSelectionOptions * selectionOptionWidget()
SampleLayersMode sampleLayersMode() const
KisToolSelectBase(KoCanvasBase *canvas, const QString toolName)
void keyReleaseEvent(QKeyEvent *event) override
void mouseMoveEvent(KoPointerEvent *event) override
SelectionAction selectionAction() const
void beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) override
void beginPrimaryAction(KoPointerEvent *event) override
void endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) override
virtual void setAlternateSelectionAction(SelectionAction action)
QList< int > colorLabelsSelected() const
virtual bool isPixelOnly() const
KisToolSelectBase(KoCanvasBase *canvas, const QCursor cursor, const QString toolName)
void activate(const QSet< KoShape * > &shapes) override
virtual bool usesColorLabels() const
KisSelectionToolConfigWidgetHelper m_widgetHelper
void deactivateAlternateAction(KisTool::AlternateAction action) override
KisSignalAutoConnectionsStore m_modeConnections
SelectionMode selectionMode() const
void keyPressEvent(QKeyEvent *event) override
void continuePrimaryAction(KoPointerEvent *event) override
void activateAlternateAction(KisTool::AlternateAction action) override
KisNodeSP locateSelectionMaskUnderCursor(const QPointF &pos, Qt::KeyboardModifiers modifiers)
void endPrimaryAction(KoPointerEvent *event) override
void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) override
QMenu * popupActionsMenu() override
bool stopGrowingAtDarkestPixel() const
void deactivate() override
QWidget * createOptionWidget() override
bool antiAliasSelection() const
SelectionAction m_selectionActionAlternate
Qt::KeyboardModifiers m_currentModifiers
KisPopupWidgetInterface * popupWidget() override
KisToolSelectBase(KoCanvasBase *canvas, QCursor cursor, QString toolName, KoToolBase *delegateTool)
KisSelectionSP selection()
Qt::KeyboardModifiers modifiers() const
QPointF point
The point in document coordinates.
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
KisToolSelectBase< FakeBaseTool > KisToolSelect
FakeBaseTool(KoCanvasBase *canvas)
FakeBaseTool(KoCanvasBase *canvas, const QString &toolName)
FakeBaseTool(KoCanvasBase *canvas, const QCursor &cursor)
static KisSelectionModifierMapper * instance()
SelectionAction map(Qt::KeyboardModifiers m)
bool outlineCacheValid() const
KisNodeWSP parentNode
QPainterPath outlineCache() const
QCursor cursor
Definition kis_tool.cc:64
AlternateAction
Definition kis_tool.h:134
KisCanvas2 * canvas