Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_selection_actions_panel.cpp
Go to the documentation of this file.
1
2/*
3 * SPDX-FileCopyrightText: 2025 Ross Rosales <ross.erosales@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include "KoCanvasController.h"
12#include "kis_action.h"
13#include "kis_action_manager.h"
14#include "kis_config_notifier.h"
16#include "KisDocument.h"
17#include "KisViewManager.h"
18#include "kis_canvas2.h"
20#include "kis_icon_utils.h"
21#include "kis_painting_tweaks.h"
22#include "kis_selection.h"
27#include <QList>
28#include <QMouseEvent>
29#include <QPointF>
30#include <QPushButton>
31#include <QTabletEvent>
32#include <QTouchEvent>
33#include <QTransform>
34#include <kactioncollection.h>
35#include <kis_algebra_2d.h>
36#include <klocalizedstring.h>
37#include <ktoggleaction.h>
38#include <kconfiggroup.h>
39#include <KisPart.h>
40#include <KisMainWindow.h>
41
42#include <QApplication>
43#include <QPainter>
44#include <QPainterPath>
45#include <QMenu>
46
48
49static constexpr int BUTTON_SIZE = 30;
50static constexpr int BUFFER_SPACE = 5;
51
52static constexpr int BIG_BUFFER_SPACE_X = 30;
53static constexpr int BIG_BUFFER_SPACE_Y = 30;
54
55
56
58
66
69 {
70 }
73
75 bool m_pressed = false;
76 bool m_visible = false;
77 bool m_enabled = true;
78
79 struct DragHandle {
80 QPoint position = QPoint(0, 0);
81 QPoint dragOrigin = QPoint(0, 0);
82 QPoint dragOffset = QPoint(0, 0);
83 };
84
86
90 {
91 static const QVector<ActionButtonData> data = {
92 {"select-all", i18n("Select All"), &KisSelectionManager::selectAll},
93 {"select-invert", i18n("Invert Selection"), &KisSelectionManager::invert},
94 {"select-clear", i18n("Deselect"), &KisSelectionManager::deselect},
95 {"krita_tool_color_fill", i18n("Fill Selection with Color"), &KisSelectionManager::fillForegroundColor},
96 {"draw-eraser", i18n("Clear Selection"), &KisSelectionManager::clear},
97 {"duplicatelayer", i18n("Copy To New Layer"), &KisSelectionManager::copySelectionToNewLayer},
98 {"tool_crop", i18n("Crop to Selection"), &KisSelectionManager::imageResizeToSelection},
99 {"krita_tool_reference_images", i18n("Toggle pin selection actions bar"), &KisSelectionManager::toggleSAPpin}};
100 return data;
101 }
102 int m_buttonCount = buttonData().size() + 1; // buttons + drag handle
103
108
111
112 Orientation orientation = Orientation::Horizontal;
113 Position position = Position::Bottom;
114 Behavior behavior = Behavior::FreeFloating;
115
116 const QString dragOffsetConfigName = "selectionActionBarDragOffset";
117
119};
120
121
122
124 : d(new Private)
125{
126 d->m_viewManager = viewManager;
127 d->m_selectionManager = viewManager->selectionManager();
128
129
130 KisConfig kcfg(true);
131 // instead of loading the user's configuration, set it to the "old version" of the behaviour:
134 // whether it's fixed or free-floating can be determined by the pin icon, so let's load that one correctly and don't change it
135 // TODO: remove this whole part when restoring the behaviour
136
137 // Setup buttons...
139 for (int i = 0; i < data.length(); i++) {
140 const ActionButtonData &buttonData = data[i];
142 connect(button, &QAbstractButton::clicked, d->m_selectionManager, buttonData.slot);
143 connect(button, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
144 d->m_buttons.append(button);
145
146 if (buttonData.slot == &KisSelectionManager::toggleSAPpin) {
147 d->sapPinButtonIndex = i;
148 button->setCheckable(true);
149 }
150 }
151
152 d->m_handleWidget = new KisSelectionActionsPanelHandle(BUTTON_SIZE, d->orientation, viewManager->canvas());
153 connect(d->m_handleWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
154
155 d->disable_action = new KisAction(i18n("Disable selection actions bar"));
156 connect(d->disable_action, SIGNAL(triggered()), SLOT(disableSelectionActionsPanel()) );
157
158 d->configure_action = viewManager->actionManager()->actionByName("configure_sap");
159 connect(d->configure_action, SIGNAL(triggered()), SLOT(configureSelectionActionsPanel()));
160
161 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(configChanged()));
162 connect(d->m_viewManager->canvasBase()->canvasController()->proxyObject, SIGNAL(canvasStateChanged()), SLOT(canvasStateChanged()));
163
164 connect(KisPart::instance()->currentMainwindow(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
165
166 KConfigGroup cfg = KSharedConfig::openConfig()->group("");
167 d->m_dragHandle.dragOffset = cfg.readEntry(d->dragOffsetConfigName, QPoint(0, 0));
168
169 configChanged(true);
170
171}
172
176
177void KisSelectionActionsPanel::draw(QPainter &painter, const KoColorDisplayRendererInterface *displayRendererInterface)
178{
179 KisSelectionSP selection = d->m_viewManager->selection();
180
181 if (!selection || !d->m_enabled || !d->m_visible) {
182 return;
183 }
184
185 if (d->m_viewManager->canvas()) {
186 d->m_dragHandle.position = currentTopLeftPosition();
188 }
189
190 if (d->m_pressed && d->behavior == Behavior::FreeFloating) {
191 drawAnchorWhileMoving(painter);
192 }
193
194 //drawDebugRectanglesForFreeFloatingBehaviour(painter);
195
196 drawActionBarBackground(painter, displayRendererInterface);
197 Q_FOREACH(KisSelectionActionsPanelButton* button, d->m_buttons){
198 button->draw(painter, displayRendererInterface);
199 }
200
201 if (d->m_handleWidget->isEnabled()) {
202 d->m_handleWidget->draw(painter, displayRendererInterface);
203 }
204
205}
206
208{
209 d->orientation = mode;
210 d->m_handleWidget->setOrientation(mode);
212
213 //Recalcute the position of the bar to be inside the canvas
214 QWidget *canvasWidget = dynamic_cast<QWidget *>(d->m_viewManager->canvas());
215 if (canvasWidget) {
216 d->m_dragHandle.position = clipPositionToCanvasBoundaries(d->m_dragHandle.position, canvasWidget);
218 }
219}
220
222{
223 if (enabled && !d->m_handleWidget->isEnabled()) {
224 d->m_handleWidget->setEnabled(enabled);
225 d->m_buttonCount++;
226 }
227
228 if (!enabled && d->m_handleWidget->isEnabled()) {
229 d->m_handleWidget->setEnabled(enabled);
230 d->m_buttonCount--;
231 }
233}
234
236{
237 QWidget *canvasWidget = dynamic_cast<QWidget *>(d->m_viewManager->canvas());
238 if (!canvasWidget) {
239 return QPoint();
240 }
241
242 QRect canvasBounds = canvasWidget->rect();
243 QPoint result = QPoint();
244
245 switch (d->position) {
247 result.setY(canvasBounds.bottom());
248 result.setX((canvasBounds.width() - d->m_actionBarWidth) / 2);
249 break;
251 result.setY(canvasBounds.bottom());
252 result.setX(canvasBounds.left());
253 break;
255 result.setY(canvasBounds.bottom());
256 result.setX(canvasBounds.right());
257 break;
258 case KisConfig::Left:
259 result.setY((canvasBounds.height() - d->m_actionBarHeight) / 2);
260 result.setX(canvasBounds.left());
261 break;
262 case KisConfig::Right:
263 result.setY((canvasBounds.height() - d->m_actionBarHeight) / 2);
264 result.setX(canvasBounds.right());
265 break;
266 case KisConfig::Top:
267 result.setY(canvasBounds.top());
268 result.setX((canvasBounds.width() - d->m_actionBarWidth) / 2);
269 break;
271 result.setY(canvasBounds.top());
272 result.setX(canvasBounds.left());
273 break;
275 result.setY(canvasBounds.top());
276 result.setX(canvasBounds.right());
277 break;
278 }
279
280 return clipPositionToCanvasBoundaries(result, canvasWidget);
281}
282
284{
285 if (!d->m_handleWidget) return;
286
287 if (d->orientation == Orientation::Horizontal) {
288 d->m_innerActionBarHeight = d->m_actionBarHeight = BUTTON_SIZE;
289 d->m_innerActionBarWidth = d->m_actionBarWidth = BUTTON_SIZE * d->m_buttonCount;
290 if (d->m_handleWidget->isEnabled()) {
291 d->m_innerActionBarWidth = BUTTON_SIZE * (d->m_buttonCount - 1);
292 }
293 } else if (d->orientation == Orientation::Vertical) {
294 d->m_innerActionBarHeight = d->m_actionBarHeight = BUTTON_SIZE * d->m_buttonCount;
295 d->m_innerActionBarWidth = d->m_actionBarWidth = BUTTON_SIZE;
296 if (d->m_handleWidget->isEnabled()) {
297 d->m_innerActionBarHeight = BUTTON_SIZE * (d->m_buttonCount - 1);
298 }
299 }
300
302}
303
305{
306 QWidget *canvasWidget = dynamic_cast<QWidget *>(d->m_viewManager->canvas());
307 if (!canvasWidget) {
308 return;
309 }
310
311 p_visible &= d->m_enabled;
312
313 const bool VISIBILITY_CHANGED = d->m_visible != p_visible;
314 if (!VISIBILITY_CHANGED) {
315 return;
316 }
317
318 d->configure_action->setVisible(p_visible && d->m_viewManager->selection());
319
320 if (d->m_viewManager->selection() && p_visible) { // Now visible!
321 d->m_handleWidget->installEventFilter(this);
322 d->m_dragHandle.position = currentTopLeftPosition();
324 } else { // Now hidden!
325 d->m_handleWidget->removeEventFilter(this);
326
327 for (KisSelectionActionsPanelButton *button : d->m_buttons) {
328 button->hide();
329 }
330 d->m_handleWidget->hide();
331
332 d->m_pressed = false;
333 }
334
335 d->m_visible = p_visible;
336}
337
339{
340
341 bool configurationChanged = enabled != d->m_enabled;
342
343 d->configure_action->setVisible(enabled && d->m_viewManager->selection());
344
345 d->m_enabled = enabled;
346 if (configurationChanged) {
347 // Reset visibility when configuration changes
348 setVisible(enabled);
349 }
350}
351
352bool KisSelectionActionsPanel::eventFilter(QObject *obj, QEvent *event)
353{
354 Q_UNUSED(obj);
355
356 switch (event->type()) {
357 case QEvent::MouseButtonPress: {
358 const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
359 return handlePress(event, mouseEventPos(mouseEvent), mouseEvent->button());
360 }
361 case QEvent::TabletPress: {
362 const QTabletEvent *tabletEvent = static_cast<QTabletEvent *>(event);
363 return handlePress(event, tabletEventPos(tabletEvent));
364 }
365 case QEvent::TouchBegin: {
366 const QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
367 QPoint pos;
368 if (touchEventPos(touchEvent, pos)) {
369 return handlePress(event, pos);
370 }
371 break;
372 }
373
374 case QEvent::MouseMove:
375 if (d->m_pressed) {
376 const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
377 return handleMove(event, mouseEventPos(mouseEvent));
378 }
379 break;
380 case QEvent::TabletMove:
381 if (d->m_pressed) {
382 const QTabletEvent *tabletEvent = static_cast<QTabletEvent *>(event);
383 return handleMove(event, tabletEventPos(tabletEvent));
384 }
385 break;
386 case QEvent::TouchUpdate:
387 if (d->m_pressed) {
388 const QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
389 QPoint pos;
390 if (touchEventPos(touchEvent, pos)) {
391 return handleMove(event, pos);
392 }
393 }
394 break;
395
396 case QEvent::MouseButtonRelease:
397 case QEvent::TabletRelease:
398 case QEvent::TouchEnd:
399 case QEvent::TouchCancel:
400 if (d->m_pressed) {
401 return handleRelease(event);
402 }
403 break;
404
405 default:
406 break;
407 }
408 return false;
409}
410
411
413{
414
415 Q_FOREACH(QWidget* btn, d->m_buttons) {
416 btn->setParent(canvas->widget());
417 btn->show();
418 }
419
420 d->m_handleWidget->setParent(canvas->widget());
421 d->m_handleWidget->show();
422}
423
424QPoint KisSelectionActionsPanel::clipPositionToCanvasBoundaries(QPoint position, QWidget *canvasWidget) const
425{
426 QRect canvasBounds = canvasWidget->rect();
427
428 const int ACTION_BAR_WIDTH = d->m_actionBarWidth;
429 const int ACTION_BAR_HEIGHT = d->m_actionBarHeight;
430
431 int pos_x_min = canvasBounds.left() + BUFFER_SPACE;
432 int pos_x_max = canvasBounds.right() - ACTION_BAR_WIDTH - BUFFER_SPACE;
433
434 int pos_y_min = canvasBounds.top() + BUFFER_SPACE;
435 int pos_y_max = canvasBounds.bottom() - ACTION_BAR_HEIGHT - BUFFER_SPACE;
436
437 //Ensure that max is always bigger than min
438 //If the window is small enough max could be smaller than min
439 if (pos_x_max < pos_x_min) {
440 pos_x_max = pos_x_min;
441 }
442
443 //It is pretty implausible for it to happen vertically but better safe than sorry
444 if (pos_y_max < pos_y_min) {
445 pos_y_max = pos_y_min;
446 }
447
448 position.setX(qBound(pos_x_min, position.x(), pos_x_max));
449 position.setY(qBound(pos_y_min, position.y(), pos_y_max));
450
451 return position;
452}
453
455{
456 Position position = d->position;
457 QRect selection = getWidgetSelectionRect().toRect();
458 QPoint anchor = selection.center();
459 QPoint offsetted = selection.center();
460 qreal barHorizontalOffset = d->m_actionBarWidth;
461 qreal barVerticalOffset = d->m_actionBarHeight;
462
463 switch (position) {
464 case Position::Top:
465 case Position::Bottom:
466 anchor = QPoint(selection.center().x(), 0);
467 offsetted = anchor - QPoint(barHorizontalOffset/2, 0);
468 break;
469 case Position::TopLeft:
470 case Position::BottomLeft:
471 anchor = QPoint(selection.left(), 0);
472 offsetted = anchor;
473 break;
474 case Position::TopRight:
475 case Position::BottomRight:
476 anchor = QPoint(selection.right(), 0);
477 offsetted = anchor - QPoint(barHorizontalOffset, 0);
478 break;
479
480
481 case Position::Left:
482 anchor = QPoint(selection.left(), 0);
483 offsetted = anchor - QPoint(barHorizontalOffset + BIG_BUFFER_SPACE_X, 0);
484 break;
485
486 case Position::Right:
487 anchor = QPoint(selection.right(), 0);
488 offsetted = anchor + QPoint(BIG_BUFFER_SPACE_X, 0);
489 break;
490 }
491
492 switch (position) {
493 case Position::Top:
494 case Position::TopLeft:
495 case Position::TopRight:
496 anchor += QPoint(0, selection.top());
497 offsetted += QPoint(0, selection.top() - BIG_BUFFER_SPACE_Y - barVerticalOffset);
498 break;
499 case Position::Bottom:
500 case Position::BottomLeft:
501 case Position::BottomRight:
502 anchor += QPoint(0, selection.bottom());
503 offsetted += QPoint(0, selection.bottom() + BIG_BUFFER_SPACE_Y);
504 break;
505
506 case Position::Left:
507 case Position::Right:
508 anchor += QPoint(0, selection.center().y());
509 offsetted += QPoint(0, selection.center().y() - barVerticalOffset/2);
510 break;
511
512 }
513
514 if (calculateOnlyAnchor) {
515 return anchor;
516 }
517 return offsetted;
518
519}
520
522{
523 Position position = d->position;
524 QRect selection = getWidgetSelectionRect().toRect();
525 QPoint anchor = selection.center();
526 QPoint offsetted = selection.center();
527 qreal barVerticalOffset = d->m_actionBarHeight;
528 qreal barHorizontalOffset = d->m_actionBarWidth;
529
530 switch (position) {
531 case Position::Left:
532 case Position::Right:
533 anchor = QPoint(0, selection.center().y());
534 offsetted = -QPoint(0, barVerticalOffset/2);
535 break;
536 case Position::TopLeft:
537 case Position::TopRight:
538 anchor = QPoint(0, selection.top());
539 offsetted = QPoint();
540 break;
541 case Position::BottomLeft:
542 case Position::BottomRight:
543 anchor = QPoint(0, selection.bottom());
544 offsetted = -QPoint(0, barVerticalOffset);
545 break;
546
547 case Position::Top:
548 anchor = QPoint(0, selection.top());
549 offsetted = -QPoint(0, barVerticalOffset + BIG_BUFFER_SPACE_Y);
550 break;
551
552 case Position::Bottom:
553 anchor = QPoint(0, selection.bottom());
554 offsetted = QPoint(0, BIG_BUFFER_SPACE_Y);
555 break;
556 }
557
558 switch (position) {
559 case Position::Left:
560 case Position::TopLeft:
561 case Position::BottomLeft:
562 anchor += QPoint(selection.left(), 0);
563 offsetted += QPoint(- BIG_BUFFER_SPACE_X - barHorizontalOffset, 0);
564 break;
565 case Position::Right:
566 case Position::TopRight:
567 case Position::BottomRight:
568 anchor += QPoint(selection.right(), 0);
569 offsetted += QPoint(BIG_BUFFER_SPACE_X, 0);
570 break;
571
572 case Position::Top:
573 case Position::Bottom:
574 anchor += QPoint(selection.center().x(), 0);
575 offsetted += QPoint(- barHorizontalOffset/2, 0);
576 break;
577 }
578
579 if (calculateOnlyAnchor) {
580 return anchor;
581 }
582 return anchor + offsetted;
583}
584
586{
587 if (!d->m_viewManager) {
588 return QRectF();
589 }
590
591 KisSelectionSP selection = d->m_viewManager->selection();
592 KisCanvasWidgetBase *canvas = dynamic_cast<KisCanvasWidgetBase*>(d->m_viewManager->canvas());
593
594 if (!canvas || !selection) {
595 return QRectF();
596 }
597
598 QPainterPath selectionOutline = selection->outlineCache();
599 const KisCoordinatesConverter *converter = canvas->coordinatesConverter();
600
601 if (!converter) {
602 return QRectF();
603 }
604
605 QRectF selectionBoundsWidget = converter->imageToWidget(selectionOutline).boundingRect();
606
607 return selectionBoundsWidget;
608}
609
611{
612 if (d->orientation == Orientation::Horizontal) {
613 return horizontalFreeFloatingTopLeftPosition(calculateOnlyAnchor);
614 } else {
615 return verticalFreeFloatingTopLeftPosition(calculateOnlyAnchor);
616 }
617}
618
620{
621 if (d->behavior == Behavior::Fixed) {
622 return getFixedPosition();
623 }
624
625 QPoint widgetTopLeftPosition = freeFloatingInitialTopLeftPosition();
626 widgetTopLeftPosition += d->m_dragHandle.dragOffset;
627
628 return clipPositionToCanvasBoundaries(widgetTopLeftPosition, d->m_viewManager->canvas());
629}
630
632{
633 painter.save();
634
635 QPen pen = painter.pen();
636 pen.setStyle(Qt::CustomDashLine);
637 pen.setDashPattern({6, 6});
638 pen.setColor(Qt::darkGray);
639 painter.setPen(pen);
640 QPoint initial = freeFloatingInitialTopLeftPosition(true);
641 QPoint offset = QPoint(d->m_actionBarWidth/2, d->m_actionBarHeight/2);
642 painter.drawLine(QLine(initial, d->m_dragHandle.position + offset));
643
644 int xSize = 8;
645 pen.setStyle(Qt::SolidLine);
646 painter.setPen(pen);
647
648 painter.drawLine(initial + QPoint(xSize, xSize), initial - QPoint(xSize, xSize));
649 painter.drawLine(initial + QPoint(xSize, -xSize), initial + QPoint(-xSize, xSize));
650
651 painter.restore();
652}
653
654void KisSelectionActionsPanel::drawActionBarBackground(QPainter &painter, const KoColorDisplayRendererInterface *displayRendererInterface) const
655{
656 const int cornerRadius = 4;
657 QColor outlineColor = qApp->palette().window().color();
658
659 QColor bgColor = qApp->palette().window().color();
660
661 //Slightly lighten the color, to force `dragColor` to move it in the lighter direction, without this it will always darken the color
663 bgColor = bgColor.lighter(120);
664 }
665
666 KisPaintingTweaks::dragColor(&bgColor, outlineColor, 0.25);
667
668 // Manually convert here, to keep as much consistency as possible.
669 KoColor c;
670 c.fromQColor(bgColor);
671 bgColor = displayRendererInterface->convertColorToDisplayColorSpace(c);
672 c.fromQColor(outlineColor);
673 outlineColor = displayRendererInterface->convertColorToDisplayColorSpace(c);
674
675 QColor bgColorTrans = bgColor;
676 bgColorTrans.setAlpha(80);
677 const int outline_width = 4;
678
679 //an outer 1px wide outline for contrast against the background
680 QRectF contrastOutline(d->m_dragHandle.position - QPoint(outline_width + 1,outline_width + 1), QSize(d->m_actionBarWidth, d->m_actionBarHeight) +QSize(outline_width + 1,outline_width + 1) * 2);
681 QRectF midOutline(d->m_dragHandle.position - QPoint(outline_width,outline_width), QSize(d->m_actionBarWidth, d->m_actionBarHeight) +QSize(outline_width,outline_width) * 2);
682 //Add a bit of padding here for the icons
683 QRectF centerBackground(d->m_dragHandle.position - QPoint(outline_width,outline_width) / 2, QSize(d->m_innerActionBarWidth, d->m_innerActionBarHeight) +QSize(outline_width,outline_width));
684 QPainterPath bgPath;
685 QPainterPath outlinePath;
686 QPainterPath contrastOutlinePath;
687
688 bgPath.addRoundedRect(centerBackground, cornerRadius, cornerRadius);
689 outlinePath.addRoundedRect(midOutline, cornerRadius, cornerRadius);
690 contrastOutlinePath.addRoundedRect(contrastOutline, cornerRadius, cornerRadius);
691
692 painter.fillPath(contrastOutlinePath, bgColorTrans);
693 painter.fillPath(outlinePath, outlineColor);
694 painter.fillPath(bgPath, bgColor);
695}
696
697bool KisSelectionActionsPanel::handlePress(QEvent *event, const QPoint &pos, Qt::MouseButton button)
698{
699 if (d->m_pressed) {
700 event->accept();
701 return true;
702 }
703
704 if (button == Qt::LeftButton) {
705 d->m_pressed = true;
706 d->m_dragHandle.dragOrigin = pos - d->m_dragHandle.dragOffset;
707 d->m_handleWidget->set_held(true);
708
709 event->accept();
710 return true;
711 }
712
713 return false;
714}
715
716bool KisSelectionActionsPanel::handleMove(QEvent *event, const QPoint &pos)
717{
718 QWidget *canvasWidget = d->m_viewManager->canvas();
719 QPoint newPos = pos - d->m_dragHandle.dragOrigin;
720 d->m_dragHandle.dragOffset = newPos;
721
723 canvasWidget->update();
724 event->accept();
725 return true;
726}
727
729{
730 d->m_handleWidget->set_held(false);
731 d->m_pressed = false;
732 event->accept();
733
734 KConfigGroup cfg = KSharedConfig::openConfig()->group("");
735 cfg.writeEntry(d->dragOffsetConfigName, d->m_dragHandle.dragOffset);
736
737 return true;
738}
739
741{
742 //This function gets called on panel creation, when dragHandle is not initialized, so we need to handle that
743 if (!d->m_handleWidget)
744 return;
745
746 if (d->orientation == Orientation::Vertical) {
747 d->m_handleWidget->move(d->m_dragHandle.position.x(),
748 d->m_dragHandle.position.y() + d->m_buttons.size() * BUTTON_SIZE);
749 } else if (d->orientation == Orientation::Horizontal) {
750 d->m_handleWidget->move(d->m_dragHandle.position.x() + d->m_buttons.size() * BUTTON_SIZE,
751 d->m_dragHandle.position.y());
752 }
753 d->m_handleWidget->show();
754
755 int i = 0;
756 Q_FOREACH (KisSelectionActionsPanelButton *button, d->m_buttons) {
757 int buttonPosition = i * BUTTON_SIZE;
758
759 if (d->orientation == Orientation::Vertical) {
760 button->move(d->m_dragHandle.position.x(), d->m_dragHandle.position.y() + buttonPosition);
761 } else if (d->orientation == Orientation::Horizontal) {
762 button->move(d->m_dragHandle.position.x() + buttonPosition, d->m_dragHandle.position.y());
763 }
764 button->show();
765
766 i++;
767 }
768}
769
770QPoint KisSelectionActionsPanel::mouseEventPos(const QMouseEvent *mouseEvent)
771{
772#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
773 return transformHandleCoords(mouseEvent->position().toPoint());
774#else
775 return transformHandleCoords(mouseEvent->pos());
776#endif
777}
778
779QPoint KisSelectionActionsPanel::tabletEventPos(const QTabletEvent *tabletEvent)
780{
781#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
782 return transformHandleCoords(tabletEvent->position().toPoint());
783#else
784 return transformHandleCoords(tabletEvent->pos());
785#endif
786}
787
788bool KisSelectionActionsPanel::touchEventPos(const QTouchEvent *touchEvent, QPoint &outPos)
789{
790#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
791 if (touchEvent->pointCount() < 1) {
792 return false;
793 } else {
794 outPos = transformHandleCoords(touchEvent->points().first().position().toPoint());
795 return true;
796 }
797#else
798 const QList<QTouchEvent::TouchPoint> &touchPoints = touchEvent->touchPoints();
799 if (touchPoints.isEmpty()) {
800 return false;
801 } else {
802 outPos = transformHandleCoords(touchPoints.first().pos().toPoint());
803 return true;
804 }
805#endif
806}
807
809 return d->m_dragHandle.position + pos;
810}
811
813{
814 QMenu menu = QMenu();
815 menu.addAction(d->disable_action);
816 menu.addAction(d->configure_action);
817 menu.exec(pos);
818}
819
826
828{
829 KisAction *a = d->m_viewManager->actionManager()->actionByName("options_configure");
830
832
833 a->trigger();
834}
835
836void KisSelectionActionsPanel::configChanged(bool skipResettingOffset)
837{
838 KisConfig cfg(true);
839 KConfigGroup silentCfg = KSharedConfig::openConfig()->group("");
840
841
842 setHandleEnabled(cfg.selectionActionBarBehavior() != Behavior::Fixed);
843
844 bool resetOffset = false;
845 if (d->orientation != cfg.selectionActionBarOrientation()) {
846 resetOffset = true;
848 }
849 d->behavior = cfg.selectionActionBarBehavior();
850 if (d->position != cfg.selectionActionBarPosition()) {
851 resetOffset = true;
852 d->position = cfg.selectionActionBarPosition();
853 }
854
855 if (resetOffset && !skipResettingOffset) {
856 d->m_dragHandle.dragOffset = QPoint();
857 silentCfg.writeEntry(d->dragOffsetConfigName, QPoint());
858 }
859
860 if (d->behavior == Behavior::Fixed) {
861 d->m_dragHandle.position = currentTopLeftPosition();
863 }
864
865 if (d->sapPinButtonIndex >= 0 && d->m_buttons[d->sapPinButtonIndex] && d->m_buttons[d->sapPinButtonIndex]->isCheckable()) {
866 d->m_buttons[d->sapPinButtonIndex]->setChecked(cfg.selectionActionBarBehavior() == Behavior::Fixed);
867 }
868}
869
871{
872 if (d->behavior == Behavior::Fixed) {
873 d->m_dragHandle.position = currentTopLeftPosition();
874 } else {
875 d->m_dragHandle.position = currentTopLeftPosition();
876 d->m_dragHandle.position = clipPositionToCanvasBoundaries(d->m_dragHandle.position, d->m_viewManager->canvas());
877 }
878
880}
881
883{
884 Q_FOREACH(KisSelectionActionsPanelButton* button, d->m_buttons) {
886 }
887 d->m_handleWidget->themeChanged();
888}
889
890
891
893{
894 d->position = position;
895 QBrush brush(Qt::cyan);
897 brush.setColor(QColor(100 + 20*(int)position, 100, 100, 150));
898 painter.setBrush(brush);
899 painter.drawRect(QRect(p, QSize(d->m_actionBarWidth, d->m_actionBarHeight)));
900 painter.drawEllipse(freeFloatingInitialTopLeftPosition(true), 10, 10);
901}
902
903
905{
906 painter.save();
907 Position remember = d->position;
908
909 for (int position = 0; position < 8; position++) {
910 drawDebugRectangle(painter, (Position)position);
911 }
912
913 painter.restore();
914 d->position = remember;
915
916}
917
const Params2D p
virtual QWidget * widget()=0
KisAction * actionByName(const QString &name) const
KisActionManager * actionManager
KisCoordinatesConverter * coordinatesConverter() const
static KisConfigNotifier * instance()
SelectionActionsBarOrientation selectionActionBarOrientation(bool defaultValue=false) const
SelectionActionsBarPosition
Definition kis_config.h:780
SelectionActionsBarBehavior
Definition kis_config.h:775
SelectionActionsBarBehavior selectionActionBarBehavior(bool defaultValue=false) const
void setSelectionActionBarOrientation(SelectionActionsBarOrientation value)
SelectionActionsBarOrientation
Definition kis_config.h:791
SelectionActionsBarPosition selectionActionBarPosition(bool defaultValue=false) const
void setSelectionActionBarPosition(SelectionActionsBarPosition value)
void setSelectionActionBar(bool value)
_Private::Traits< T >::Result imageToWidget(const T &obj) const
static KisPart * instance()
Definition KisPart.cpp:131
Custom widget for selection actions panel buttons, to prevent them being drawn over the pop-up palett...
void drawAnchorWhileMoving(QPainter &painter) const
End of positioning ///.
bool handlePress(QEvent *event, const QPoint &pos, Qt::MouseButton button=Qt::LeftButton)
void movePanelWidgets()
Moves all the widgets that are a part of the panel.
void drawDebugRectangle(QPainter &painter, Position position)
void setOrientation(Orientation orientation)
void configChanged(bool skipResettingOffset=false)
bool eventFilter(QObject *obj, QEvent *event) override
QPoint tabletEventPos(const QTabletEvent *tabletEvent)
bool handleMove(QEvent *event, const QPoint &pos)
QPoint verticalFreeFloatingTopLeftPosition(bool calculateOnlyAnchor=false) const
QPoint horizontalFreeFloatingTopLeftPosition(bool calculateOnlyAnchor=false) const
QPoint getFixedPosition() const
Positioning ///.
QPoint clipPositionToCanvasBoundaries(QPoint position, QWidget *canvasWidget) const
QPoint mouseEventPos(const QMouseEvent *mouseEvent)
void canvasWidgetChanged(KisCanvasWidgetBase *canvas)
void draw(QPainter &painter, const KoColorDisplayRendererInterface *displayRendererInterface)
bool touchEventPos(const QTouchEvent *touchEvent, QPoint &outPos)
void drawDebugRectanglesForFreeFloatingBehaviour(QPainter &painter)
void drawActionBarBackground(QPainter &gc, const KoColorDisplayRendererInterface *displayRendererInterface) const
QPoint freeFloatingInitialTopLeftPosition(bool calculateOnlyAnchor=false) const
KisActionManager * actionManager() const
QWidget * canvas() const
Return the actual widget that is displaying the current image.
KisSelectionManager * selectionManager()
virtual QColor convertColorToDisplayColorSpace(const KoColor color) const =0
convertColorToDisplayColorSpace
void fromQColor(const QColor &c)
Convenient function for converting from a QColor.
Definition KoColor.cpp:213
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
static constexpr int BIG_BUFFER_SPACE_X
static constexpr int BUFFER_SPACE
static constexpr int BIG_BUFFER_SPACE_Y
static constexpr int BUTTON_SIZE
QString button(const QWheelEvent &ev)
void updateIcon(QAbstractButton *button)
void dragColor(QColor *color, const QColor &baseColor, qreal threshold)
void(KisSelectionManager::*)() TargetSlot
KisSelectionActionsPanelHandle * m_handleWidget
QList< KisSelectionActionsPanelButton * > m_buttons
static const QVector< ActionButtonData > & buttonData()
QPainterPath outlineCache() const