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
156
157void KisColorSelectorBase::setPopupBehaviour(bool onMouseOver, bool onMouseClick)
158{
159 m_popupOnMouseClick = onMouseClick;
160 m_popupOnMouseOver = onMouseOver;
161 if(onMouseClick) {
162 m_popupOnMouseOver = false;
163 }
164
166 setMouseTracking(true);
167 }
168}
169
174
176{
177 if (m_canvas) {
178 m_canvas->disconnectCanvasObserver(this);
179 }
180 m_canvas = canvas;
181 if (m_canvas) {
182 connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
183 SLOT(canvasResourceChanged(int,QVariant)), Qt::UniqueConnection);
184
185 connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()),
186 SLOT(reset()), Qt::UniqueConnection);
187
188 connect(canvas->imageView()->resourceProvider(), SIGNAL(sigFGColorUsed(KoColor)),
189 this, SLOT(updateLastUsedColorPreview(KoColor)), Qt::UniqueConnection);
190
191 if (m_canvas->viewManager() && m_canvas->viewManager()->canvasResourceProvider()) {
192 setColor(Acs::currentColor(m_canvas->viewManager()->canvasResourceProvider(), Acs::Foreground));
193 }
194 }
195 if (m_popup) {
196 m_popup->setCanvas(canvas);
197 }
198
199 reset();
200}
201
203{
204 if (m_popup) {
206 }
207 m_canvas = 0;
208}
209
210
211
213{
214 event->accept();
215
217 event->button() == Qt::MiddleButton) {
218
220
221 int x = event->globalX();
222 int y = event->globalY();
223 int popupsize = m_popup->width();
224 x-=popupsize/2;
225 y-=popupsize/2;
226
227 const QRect availRect = this->screen()->availableGeometry();
228
229 if(x<availRect.x())
230 x = availRect.x();
231 if(y<availRect.y())
232 y = availRect.y();
233 if(x+m_popup->width()>availRect.x()+availRect.width())
234 x = availRect.x()+availRect.width()-m_popup->width();
235 if(y+m_popup->height()>availRect.y()+availRect.height())
236 y = availRect.y()+availRect.height()-m_popup->height();
237
238 m_colorUpdateSelf=false;
239 m_popup->move(x, y);
242
243 } else if (m_isPopup && event->button() == Qt::MiddleButton) {
245 m_colorPreviewPopup->hide();
246 }
247 hide();
248 } else {
251 event->ignore();
252 }
253}
254
256
257 Q_UNUSED(e);
258
259 if (e->button() == Qt::MiddleButton) {
260 e->accept();
261 } else if (m_isPopup &&
263 !m_hideTimer->isActive()) {
265 m_colorPreviewPopup->hide();
266 }
267 hide();
268 }
269}
270#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
272#else
274#endif
275{
276 if (m_popup && m_popup->isVisible()) {
277 m_popup->m_hideTimer->stop();
278 }
279
280 if (m_isPopup && m_hideTimer->isActive()) {
281 m_hideTimer->stop();
282 }
283
284 // do not show the popup when boxed in
285 // the configuration dialog (m_canvas == 0)
286
287 if (m_canvas &&
288 !m_isPopup && m_popupOnMouseOver &&
289 (!m_popup || m_popup->isHidden())) {
290
291 lazyCreatePopup();
292
293 const QRect availRect = this->screen()->availableGeometry();
294
295 QPoint proposedTopLeft = rect().center() - m_popup->rect().center();
296 proposedTopLeft = mapToGlobal(proposedTopLeft);
297
298 QRect popupRect = QRect(proposedTopLeft, m_popup->size());
299 popupRect = kisEnsureInRect(popupRect, availRect);
300
301 m_popup->setGeometry(popupRect);
302 m_popup->setHidingTime(200);
303 showPopup(DontMove);
304 }
305
306#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
307 QWidget::enterEvent(e);
308#else
309 QEnterEvent *enterEvent = dynamic_cast<QEnterEvent*>(e);
310 QWidget::enterEvent(enterEvent);
311#endif
312
313}
314
316{
318 QWidget::leaveEvent(e);
319}
320
322{
323 if (m_isPopup) {
324 hidePopup();
325 }
326}
327
329{
330 if(e->mimeData()->hasColor())
331 e->acceptProposedAction();
332 if(e->mimeData()->hasText() && QColor(e->mimeData()->text()).isValid())
333 e->acceptProposedAction();
334}
335
337{
338 QColor color;
339 if(e->mimeData()->hasColor()) {
340 color = qvariant_cast<QColor>(e->mimeData()->colorData());
341 }
342 else if(e->mimeData()->hasText()) {
343 color.setNamedColor(e->mimeData()->text());
344 if(!color.isValid())
345 return;
346 }
347
348 KoColor kocolor(color , KoColorSpaceRegistry::instance()->rgb8());
349 updateColor(kocolor, Acs::Foreground, true);
350}
351
352void KisColorSelectorBase::updateColor(const KoColor &color, Acs::ColorRole role, bool needsExplicitColorReset)
353{
354 commitColor(color, role);
355
356 if (needsExplicitColorReset) {
357 setColor(color);
358 }
359}
360
362{
363 m_updateColorCompressor->start(qMakePair(color, role));
364}
365
366void KisColorSelectorBase::slotUpdateColorAndPreview(QPair<KoColor, Acs::ColorRole> color)
367{
368 updateColorPreview(color.first);
369 updateColor(color.first, color.second, false);
370}
371
373{
374 Q_UNUSED(color);
375}
376
378{
380
381 m_hideTimer->setInterval(time);
382}
383
385{
386 if (!m_popup) {
388 Q_ASSERT(m_popup);
389 m_popup->setParent(this);
390
391 // Setting Qt::BypassWindowManagerHint will prevent
392 // the WM from showing another taskbar entry,
393 // but will require that we handle window activation manually
394 m_popup->setWindowFlags(Qt::FramelessWindowHint |
395#if defined(Q_OS_MACOS) || defined(Q_OS_ANDROID)
396 Qt::Popup |
397#else
398 Qt::Window |
399#endif
400 Qt::NoDropShadowWindowHint |
401 Qt::BypassWindowManagerHint);
402 m_popup->m_parent = this;
403 m_popup->m_isPopup = true;
404 }
407}
408
410{
411 // This slot may be called by some action,
412 // so we need to be able to handle it
414
415 QPoint cursorPos = QCursor::pos();
416 const QRect availRect = this->screen()->availableGeometry();
417
418 if (move == MoveToMousePosition) {
419 m_popup->move(QPoint(cursorPos.x()-m_popup->width()/2, cursorPos.y()-m_popup->height()/2));
420 QRect rc = m_popup->geometry();
421 if (rc.x() < availRect.x()) rc.setX(availRect.x());
422 if (rc.y() < availRect.y()) rc.setY(availRect.y());
423 m_popup->setGeometry(rc);
424 }
425
427 m_colorPreviewPopup->hide();
428 }
429
430 m_popup->show();
432}
433
441
443{
444 if (!m_canvas)
445 return;
446
448
449 if (role == Acs::Foreground)
450 m_canvas->resourceManager()->setForegroundColor(color);
451 else
452 m_canvas->resourceManager()->setBackgroundColor(color);
453
455}
456
458{
459 if(m_colorPreviewPopup->isHidden()) {
461 }
462}
463
465{
466 m_colorPreviewPopup->setQColor(converter()->toQColor(color));
467}
468
469void KisColorSelectorBase::canvasResourceChanged(int key, const QVariant &v)
470{
472 KoColor realColor(v.value<KoColor>());
473 updateColorPreview(realColor);
475 setColor(realColor);
476 }
477 }
478}
479
484
486{
487 if(m_popup) {
489 }
490
491 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
492
493
494 int zoomSelectorOptions = (int) cfg.readEntry("zoomSelectorOptions", 0) ;
495 if (zoomSelectorOptions == 0) {
496 setPopupBehaviour(false, true); // middle mouse button click will open zoom selector
497 } else if (zoomSelectorOptions == 1) {
498 setPopupBehaviour(true, false); // move over will open the zoom selector
499 }
500 else
501 {
502 setPopupBehaviour(false, false); // do not show zoom selector
503 }
504
505
506 if(m_isPopup) {
507 m_hideOnMouseClick = cfg.readEntry("hidePopupOnClickCheck", false);
508 const int zoomSize = cfg.readEntry("zoomSize", 280);
509 resize(zoomSize, zoomSize);
510 }
511
512 reset();
513}
514
516{
517 update();
518}
519
524
529
534
541
543{
544 if (m_colorPreviewPopup->isVisible()) {
545 m_colorUpdateSelf=false; //this is for allowing advanced selector to listen to outside color-change events.
546 m_colorPreviewPopup->hide();
547 }
548
549 if (m_popup && m_popup->isVisible()) {
550 m_popup->m_hideTimer->start();
551 }
552
553 if (m_isPopup && !m_hideTimer->isActive()) {
554 m_hideTimer->start();
555 }
556}
557
558
560{
561 event->accept();
562}
563
564
566{
567 // hide the popup when another window becomes active, e.g. due to alt+tab
568 if(m_isPopup && event->type() == QEvent::ActivationChange && !isActiveWindow()) {
569 hidePopup();
570 }
571
572 QWidget::changeEvent(event);
573}
574
575void KisColorSelectorBase::showEvent(QShowEvent *event)
576{
577 QWidget::showEvent(event);
578
579 // manual activation required due to Qt::BypassWindowManagerHint
580 if(m_isPopup) {
581 activateWindow();
582 }
583}
const Params2D p
qreal v
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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:267
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()