Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_color_selector_base.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2010 Adam Celarek <kdedev at xibo dot at>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QMouseEvent>
10#include <QApplication>
11#include <QScreen>
12#include <QScreen>
13#include <QTimer>
14#include <QCursor>
15#include <QPainter>
16#include <QMimeData>
17
18#include <kconfig.h>
19#include <kconfiggroup.h>
20#include <ksharedconfig.h>
21
22#include "KoColorSpace.h"
24
25#include "kis_canvas2.h"
27#include "kis_node.h"
28#include "KisViewManager.h"
29#include <KisView.h>
30#include "kis_image.h"
31#include "kis_global.h"
33
35
36class KisColorPreviewPopup : public QWidget {
37public:
39 : QWidget(parent), m_parent(parent)
40 {
41 setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint);
42 setQColor(QColor(0,0,0));
43 m_baseColor = QColor(0,0,0,0);
44 m_previousColor = QColor(0,0,0,0);
45 m_lastUsedColor = QColor(0,0,0,0);
46 }
47
48 void show()
49 {
51 QWidget::show();
52 }
53
55 {
56 QPoint parentPos = m_parent->mapToGlobal(QPoint(0,0));
57 const QRect availRect = this->screen()->availableGeometry();
58 QPoint targetPos;
59 if ( parentPos.x() - 100 > availRect.x() ) {
60 targetPos = QPoint(parentPos.x() - 100, parentPos.y());
61 } else if ( parentPos.x() + m_parent->width() + 100 < availRect.right()) {
62 targetPos = m_parent->mapToGlobal(QPoint(m_parent->width(), 0));
63 } else if ( parentPos.y() - 100 > availRect.y() ) {
64 targetPos = QPoint(parentPos.x(), parentPos.y() - 100);
65 } else {
66 targetPos = QPoint(parentPos.x(), parentPos.y() + m_parent->height());
67 }
68 setGeometry(targetPos.x(), targetPos.y(), 100, 150);
69 setAttribute(Qt::WA_TranslucentBackground);
70 }
71
72 void setQColor(const QColor& color)
73 {
74 m_color = color;
75 update();
76 }
77
82
83 void setBaseColor(const QColor& color)
84 {
85 m_baseColor = color;
86 update();
87 }
88
89 void setLastUsedColor(const QColor& color)
90 {
91 m_lastUsedColor = color;
92 update();
93 }
94
95protected:
96 void paintEvent(QPaintEvent *e) override {
97 Q_UNUSED(e);
98 QPainter p(this);
99 p.fillRect(0, 0, width(), width(), m_color);
100 p.fillRect(50, width(), width(), height(), m_previousColor);
101 p.fillRect(0, width(), 50, height(), m_lastUsedColor);
102 }
103
104#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
105 void enterEvent(QEvent *e) override
106#else
107 void enterEvent(QEnterEvent *e) override
108#endif
109 {
110 QWidget::enterEvent(e);
112 }
113
114 void leaveEvent(QEvent *e) override {
115 QWidget::leaveEvent(e);
117 }
118
119private:
121 QColor m_color;
125};
126
128 QWidget(parent),
129 m_canvas(0),
130 m_popup(0),
131 m_parent(0),
132 m_colorUpdateAllowed(true),
133 m_colorUpdateSelf(false),
134 m_hideTimer(new QTimer(this)),
135 m_popupOnMouseOver(false),
136 m_popupOnMouseClick(true),
137 m_colorSpace(0),
138 m_isPopup(false),
139 m_hideOnMouseClick(false),
140 m_colorPreviewPopup(new KisColorPreviewPopup(this))
141{
142 m_hideTimer->setInterval(0);
143 m_hideTimer->setSingleShot(true);
144 connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hidePopup()));
145
146 using namespace std::placeholders; // For _1 placeholder
147 auto function = std::bind(&KisColorSelectorBase::slotUpdateColorAndPreview, this, _1);
148 m_updateColorCompressor.reset(new ColorCompressorType(25 /* ms */, function));
149
150 // Color selectors don't have context menus. Setting this prevents any
151 // long-presses from delaying inputs, see KisLongPressEventFilter.cpp.
152 setContextMenuPolicy(Qt::PreventContextMenu);
153}
154
160
161void KisColorSelectorBase::setPopupBehaviour(bool onMouseOver, bool onMouseClick)
162{
163 m_popupOnMouseClick = onMouseClick;
164 m_popupOnMouseOver = onMouseOver;
165 if(onMouseClick) {
166 m_popupOnMouseOver = false;
167 }
168
170 setMouseTracking(true);
171 }
172}
173
178
180{
181 if (m_canvas) {
182 m_canvas->disconnectCanvasObserver(this);
183 }
184 m_canvas = canvas;
185 if (m_canvas) {
186 connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
187 SLOT(canvasResourceChanged(int,QVariant)), Qt::UniqueConnection);
188
189 connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()),
190 SLOT(reset()), Qt::UniqueConnection);
191
192 connect(canvas->imageView()->resourceProvider(), SIGNAL(sigFGColorUsed(KoColor)),
193 this, SLOT(updateLastUsedColorPreview(KoColor)), Qt::UniqueConnection);
194
195 if (m_canvas->viewManager() && m_canvas->viewManager()->canvasResourceProvider()) {
196 setColor(Acs::currentColor(m_canvas->viewManager()->canvasResourceProvider(), Acs::Foreground));
197 }
198 }
199 if (m_popup) {
200 m_popup->setCanvas(canvas);
201 }
202
203 reset();
204}
205
207{
208 if (m_popup) {
210 }
211 m_canvas = 0;
212}
213
214
215
217{
218 event->accept();
219
221 event->button() == Qt::MiddleButton) {
222
224
225 int x = event->globalX();
226 int y = event->globalY();
227 int popupsize = m_popup->width();
228 x-=popupsize/2;
229 y-=popupsize/2;
230
231 const QRect availRect = this->screen()->availableGeometry();
232
233 if(x<availRect.x())
234 x = availRect.x();
235 if(y<availRect.y())
236 y = availRect.y();
237 if(x+m_popup->width()>availRect.x()+availRect.width())
238 x = availRect.x()+availRect.width()-m_popup->width();
239 if(y+m_popup->height()>availRect.y()+availRect.height())
240 y = availRect.y()+availRect.height()-m_popup->height();
241
242 m_colorUpdateSelf=false;
243 m_popup->move(x, y);
246
247 } else if (m_isPopup && event->button() == Qt::MiddleButton) {
249 m_colorPreviewPopup->hide();
250 }
251 hide();
252 } else {
255 event->ignore();
256 }
257}
258
260
261 Q_UNUSED(e);
262
263 if (e->button() == Qt::MiddleButton) {
264 e->accept();
265 } else if (m_isPopup &&
267 !m_hideTimer->isActive()) {
269 m_colorPreviewPopup->hide();
270 }
271 hide();
272 }
273}
274#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
276#else
278#endif
279{
280 if (m_popup && m_popup->isVisible()) {
281 m_popup->m_hideTimer->stop();
282 }
283
284 if (m_isPopup && m_hideTimer->isActive()) {
285 m_hideTimer->stop();
286 }
287
288 // do not show the popup when boxed in
289 // the configuration dialog (m_canvas == 0)
290
291 if (m_canvas &&
292 !m_isPopup && m_popupOnMouseOver &&
293 (!m_popup || m_popup->isHidden())) {
294
295 lazyCreatePopup();
296
297 const QRect availRect = this->screen()->availableGeometry();
298
299 QPoint proposedTopLeft = rect().center() - m_popup->rect().center();
300 proposedTopLeft = mapToGlobal(proposedTopLeft);
301
302 QRect popupRect = QRect(proposedTopLeft, m_popup->size());
303 popupRect = kisEnsureInRect(popupRect, availRect);
304
305 m_popup->setGeometry(popupRect);
306 m_popup->setHidingTime(200);
307 showPopup(DontMove);
308 }
309
310#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
311 QWidget::enterEvent(e);
312#else
313 QEnterEvent *enterEvent = dynamic_cast<QEnterEvent*>(e);
314 QWidget::enterEvent(enterEvent);
315#endif
316
317}
318
320{
322 QWidget::leaveEvent(e);
323}
324
326{
327 if (m_isPopup) {
328 hidePopup();
329 }
330}
331
333{
334 if(e->mimeData()->hasColor())
335 e->acceptProposedAction();
336 if(e->mimeData()->hasText() && QColor(e->mimeData()->text()).isValid())
337 e->acceptProposedAction();
338}
339
341{
342 QColor color;
343 if(e->mimeData()->hasColor()) {
344 color = qvariant_cast<QColor>(e->mimeData()->colorData());
345 }
346 else if(e->mimeData()->hasText()) {
347 color.setNamedColor(e->mimeData()->text());
348 if(!color.isValid())
349 return;
350 }
351
352 KoColor kocolor(color , KoColorSpaceRegistry::instance()->rgb8());
353 updateColor(kocolor, Acs::Foreground, true);
354}
355
356void KisColorSelectorBase::updateColor(const KoColor &color, Acs::ColorRole role, bool needsExplicitColorReset)
357{
358 commitColor(color, role);
359
360 if (needsExplicitColorReset) {
361 setColor(color);
362 }
363}
364
366{
367 m_updateColorCompressor->start(qMakePair(color, role));
368}
369
370void KisColorSelectorBase::slotUpdateColorAndPreview(QPair<KoColor, Acs::ColorRole> color)
371{
372 updateColorPreview(color.first);
373 updateColor(color.first, color.second, false);
374}
375
377{
378 Q_UNUSED(color);
379}
380
382{
384
385 m_hideTimer->setInterval(time);
386}
387
389{
390 if (!m_popup) {
392 Q_ASSERT(m_popup);
393 m_popup->setParent(this);
394
395 // Setting Qt::BypassWindowManagerHint will prevent
396 // the WM from showing another taskbar entry,
397 // but will require that we handle window activation manually
398 m_popup->setWindowFlags(Qt::FramelessWindowHint |
399#if defined(Q_OS_MACOS) || defined(Q_OS_ANDROID)
400 Qt::Popup |
401#else
402 Qt::Window |
403#endif
404 Qt::NoDropShadowWindowHint |
405 Qt::BypassWindowManagerHint);
406 m_popup->m_parent = this;
407 m_popup->m_isPopup = true;
408 }
411}
412
414{
415 // This slot may be called by some action,
416 // so we need to be able to handle it
418
419 QPoint cursorPos = QCursor::pos();
420 const QRect availRect = this->screen()->availableGeometry();
421
422 if (move == MoveToMousePosition) {
423 m_popup->move(QPoint(cursorPos.x()-m_popup->width()/2, cursorPos.y()-m_popup->height()/2));
424 QRect rc = m_popup->geometry();
425 if (rc.x() < availRect.x()) rc.setX(availRect.x());
426 if (rc.y() < availRect.y()) rc.setY(availRect.y());
427 m_popup->setGeometry(rc);
428 }
429
431 m_colorPreviewPopup->hide();
432 }
433
434 m_popup->show();
436}
437
445
447{
448 if (!m_canvas)
449 return;
450
452
453 if (role == Acs::Foreground)
454 m_canvas->resourceManager()->setForegroundColor(color);
455 else
456 m_canvas->resourceManager()->setBackgroundColor(color);
457
459}
460
462{
463 if(m_colorPreviewPopup->isHidden()) {
465 }
466}
467
469{
470 m_colorPreviewPopup->setQColor(converter()->toQColor(color));
471}
472
473void KisColorSelectorBase::canvasResourceChanged(int key, const QVariant &v)
474{
476 KoColor realColor(v.value<KoColor>());
477 updateColorPreview(realColor);
479 setColor(realColor);
480 }
481 }
482}
483
488
490{
491 if(m_popup) {
493 }
494
495 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
496
497
498 int zoomSelectorOptions = (int) cfg.readEntry("zoomSelectorOptions", 0) ;
499 if (zoomSelectorOptions == 0) {
500 setPopupBehaviour(false, true); // middle mouse button click will open zoom selector
501 } else if (zoomSelectorOptions == 1) {
502 setPopupBehaviour(true, false); // move over will open the zoom selector
503 }
504 else
505 {
506 setPopupBehaviour(false, false); // do not show zoom selector
507 }
508
509
510 if(m_isPopup) {
511 m_hideOnMouseClick = cfg.readEntry("hidePopupOnClickCheck", false);
512 const int zoomSize = cfg.readEntry("zoomSize", 280);
513 resize(zoomSize, zoomSize);
514 }
515
516 reset();
517}
518
520{
521 update();
522}
523
528
533
538
545
547{
548 if (m_colorPreviewPopup->isVisible()) {
549 m_colorUpdateSelf=false; //this is for allowing advanced selector to listen to outside color-change events.
550 m_colorPreviewPopup->hide();
551 }
552
553 if (m_popup && m_popup->isVisible()) {
554 m_popup->m_hideTimer->start();
555 }
556
557 if (m_isPopup && !m_hideTimer->isActive()) {
558 m_hideTimer->start();
559 }
560}
561
562
564{
565 event->accept();
566}
567
568
570{
571 // hide the popup when another window becomes active, e.g. due to alt+tab
572 if(m_isPopup && event->type() == QEvent::ActivationChange && !isActiveWindow()) {
573 hidePopup();
574 }
575
576 QWidget::changeEvent(event);
577}
578
579void KisColorSelectorBase::showEvent(QShowEvent *event)
580{
581 QWidget::showEvent(event);
582
583 // manual activation required due to Qt::BypassWindowManagerHint
584 if(m_isPopup) {
585 activateWindow();
586 }
587}
const Params2D p
qreal v
QPointer< KisView > imageView() const
void paintEvent(QPaintEvent *e) override
KisColorPreviewPopup(KisColorSelectorBase *parent)
void setLastUsedColor(const QColor &color)
void leaveEvent(QEvent *e) override
void enterEvent(QEnterEvent *e) override
KisColorSelectorBase * m_parent
void setQColor(const QColor &color)
void setBaseColor(const QColor &color)
Base class for all color selectors, that should support color management and zooming.
KisColorSelectorBase * m_popup
void requestUpdateColorAndPreview(const KoColor &color, Acs::ColorRole role)
const KoColorSpace * m_colorSpace
void dropEvent(QDropEvent *) override
void updateColorPreview(const KoColor &color)
virtual void canvasResourceChanged(int key, const QVariant &v)
if you overwrite this, keep in mind, that you should set the color only, if m_colorUpdateAllowed is t...
void dragEnterEvent(QDragEnterEvent *) override
void updateLastUsedColorPreview(const KoColor &color)
void slotUpdateColorAndPreview(QPair< KoColor, Acs::ColorRole > color)
void changeEvent(QEvent *event) override
virtual void setCanvas(KisCanvas2 *canvas)
KisDisplayColorConverter * converter() const
const KoColorSpace * colorSpace() const
virtual void setColor(const KoColor &color)
void mouseReleaseEvent(QMouseEvent *) override
KisColorSelectorBase(QWidget *parent=0)
void mousePressEvent(QMouseEvent *) override
void enterEvent(QEnterEvent *e) override
KisColorPreviewPopup * m_colorPreviewPopup
void showEvent(QShowEvent *event) override
void mouseMoveEvent(QMouseEvent *event) override
void commitColor(const KoColor &koColor, Acs::ColorRole role)
QScopedPointer< ColorCompressorType > m_updateColorCompressor
QPointer< KisCanvas2 > m_canvas
KisSignalCompressorWithParam< QPair< KoColor, Acs::ColorRole > > ColorCompressorType
void updateBaseColorPreview(const KoColor &color)
void setPopupBehaviour(bool onMouseOver, bool onMouseClick)
void setColorSpace(const KoColorSpace *colorSpace)
virtual void showPopup(Move move=MoveToMousePosition)
void leaveEvent(QEvent *e) override
virtual KisColorSelectorBase * createPopup() const =0
void updateColor(const KoColor &color, Acs::ColorRole role, bool needsExplicitColorReset)
void keyPressEvent(QKeyEvent *) override
const KoColorSpace * paintingColorSpace() const
static KisDisplayColorConverter * dumbConverterInstance()
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
QRect kisEnsureInRect(QRect rc, const QRect &bounds)
Definition kis_global.h:291
KoColor currentColor(ResourceProvider *provider, ColorRole role)
@ Foreground
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.
static KoColorSpaceRegistry * instance()