Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickcolordialogimpl.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
9
10#include <QtQuickTemplates2/private/qquickslider_p.h>
11
12#include <qpa/qplatformintegration.h>
13#include <qpa/qplatformservices.h>
14#include <private/qguiapplication_p.h>
15
17
18QColor grabScreenColor(const QPoint &p)
19{
20 QScreen *screen = QGuiApplication::screenAt(p);
21 if (!screen)
22 screen = QGuiApplication::primaryScreen();
23 const QRect screenRect = screen->geometry();
24 const QPixmap pixmap =
25 screen->grabWindow(0, p.x() - screenRect.x(), p.y() - screenRect.y(), 1, 1);
26 const QImage i = pixmap.toImage();
27 return i.pixel(0, 0);
28}
29
30bool QQuickEyeDropperEventFilter::eventFilter(QObject *obj, QEvent *event)
31{
32 switch (event->type()) {
33 case QEvent::MouseMove: {
34 m_lastPosition = static_cast<QMouseEvent *>(event)->globalPosition().toPoint();
35 m_update(m_lastPosition);
36 return true;
37 }
38 case QEvent::MouseButtonRelease: {
39 m_lastPosition = static_cast<QMouseEvent *>(event)->globalPosition().toPoint();
40 m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Default);
41 return true;
42 }
43 case QEvent::MouseButtonPress:
44 return true;
45 case QEvent::KeyPress: {
46 auto keyEvent = static_cast<QKeyEvent *>(event);
47#if QT_CONFIG(shortcut)
48 if (keyEvent->matches(QKeySequence::Cancel))
49 m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Cancel);
50 else
51#endif
52 if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
53 m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Default);
54 } else if (keyEvent->key() == Qt::Key_Escape) {
55 m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Cancel);
56 }
57 keyEvent->accept();
58 return true;
59 }
60 default:
61 return QObject::eventFilter(obj, event);
62 }
63}
64
66
68{
69 if (m_eyeDropperMode)
70 eyeDropperLeave(QCursor::pos(), QQuickEyeDropperEventFilter::LeaveReason::Default);
71}
72
73void QQuickColorDialogImplPrivate::handleClick(QQuickAbstractButton *button)
74{
75 Q_Q(QQuickColorDialogImpl);
76 const auto c = q->color();
77 if (buttonRole(button) == QPlatformDialogHelper::AcceptRole && c.isValid()) {
78 q->setColor(c);
79 q->accept();
80 }
81 QQuickDialogPrivate::handleClick(button);
82}
83
84QQuickColorDialogImplAttached *QQuickColorDialogImplPrivate::attachedOrWarn()
85{
86 Q_Q(QQuickColorDialogImpl);
87 QQuickColorDialogImplAttached *attached = static_cast<QQuickColorDialogImplAttached *>(
88 qmlAttachedPropertiesObject<QQuickColorDialogImpl>(q, false));
89 if (!attached)
90 qmlWarning(q) << "Expected ColorDialogImpl attached object to be present on" << this;
91 return attached;
92}
93
95{
96 Q_Q(QQuickColorDialogImpl);
98 return;
99
100 if (m_eyeDropperWindow.isNull()) {
101 if (window.isNull()) {
102 qWarning() << "No window found, cannot enter eyeDropperMode.";
103 return;
104 }
105
106 m_eyeDropperWindow = window;
107 }
108
109 if (auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services();
110 platformServices && platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) {
111 if (auto *colorPickerService = platformServices->colorPicker(m_eyeDropperWindow)) {
112 q->connect(colorPickerService, &QPlatformServiceColorPicker::colorPicked, q,
113 [q, colorPickerService](const QColor &color) {
114 colorPickerService->deleteLater();
115 q->setColor(color);
116 });
117 colorPickerService->pickColor();
118 return;
119 }
120 }
121
122 m_eyeDropperPreviousColor = q->color();
123
124 if (!bool(eyeDropperEventFilter))
125 eyeDropperEventFilter.reset(new QQuickEyeDropperEventFilter(
126 [this](QPoint pos, QQuickEyeDropperEventFilter::LeaveReason c) {
127 eyeDropperLeave(pos, c);
128 },
129 [this](QPoint pos) { eyeDropperPointerMoved(pos); }));
130
131 if (m_eyeDropperWindow->setMouseGrabEnabled(true)) {
132#if QT_CONFIG(cursor)
133 QGuiApplication::setOverrideCursor(Qt::CrossCursor);
134#endif
135 m_eyeDropperWindow->installEventFilter(eyeDropperEventFilter.get());
136 m_eyeDropperMode = true;
137 }
138}
139
141 const QPoint &pos, QQuickEyeDropperEventFilter::LeaveReason actionOnLeave)
142{
143 Q_Q(QQuickColorDialogImpl);
144
145 if (!m_eyeDropperMode)
146 return;
147
148 if (!m_eyeDropperWindow) {
149 qWarning() << "Window not set, cannot leave eyeDropperMode.";
150 return;
151 }
152
153 const QColor colorToUse = actionOnLeave == QQuickEyeDropperEventFilter::LeaveReason::Cancel
154 ? m_eyeDropperPreviousColor
155 : grabScreenColor(pos);
156 q->setColor(colorToUse);
157
158 m_eyeDropperWindow->removeEventFilter(eyeDropperEventFilter.get());
159 m_eyeDropperWindow->setMouseGrabEnabled(false);
160#if QT_CONFIG(cursor)
161 QGuiApplication::restoreOverrideCursor();
162#endif
163
164 m_eyeDropperMode = false;
165 m_eyeDropperWindow.clear();
166}
167
169{
170 Q_Q(QQuickColorDialogImpl);
171 q->setColor(grabScreenColor(pos));
172}
173
175{
176 Q_Q(QQuickColorDialogImpl);
177 if (auto attached = attachedOrWarn())
178 q->setAlpha(attached->alphaSlider()->value());
179}
180
181QQuickColorDialogImpl::QQuickColorDialogImpl(QObject *parent)
182 : QQuickDialog(*(new QQuickColorDialogImplPrivate), parent)
183{
184}
185
186QQuickColorDialogImplAttached *QQuickColorDialogImpl::qmlAttachedProperties(QObject *object)
187{
188 return new QQuickColorDialogImplAttached(object);
189}
190
191QColor QQuickColorDialogImpl::color() const
192{
193 Q_D(const QQuickColorDialogImpl);
194 return d->m_hsl ? QColor::fromHslF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.l, d->m_hsva.a)
195 : QColor::fromHsvF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.v, d->m_hsva.a);
196}
197
198void QQuickColorDialogImpl::setColor(const QColor &c)
199{
200 Q_D(QQuickColorDialogImpl);
201 if (const QColor old = color(); old.spec() == c.spec() && old == c)
202 return;
203
204 // If we get a QColor from an Hsv or Hsl color system,
205 // we want to get the raw values without the risk of QColor converting them,
206 // and possible deleting relevant information for achromatic cases.
207 if (c.spec() == QColor::Spec::Hsv) {
208 d->m_hsva.h = qBound(.0, c.hsvHueF(), 1.0);
209 if (d->m_hsl) {
210 const auto sl = getSaturationAndLightness(c.hsvSaturationF(), c.valueF());
211 d->m_hsva.s = qBound(.0, sl.first, 1.0);
212 d->m_hsva.l = qBound(.0, sl.second, 1.0);
213 } else {
214 d->m_hsva.s = qBound(.0, c.hsvSaturationF(), 1.0);
215 d->m_hsva.v = qBound(.0, c.valueF(), 1.0);
216 }
217 } else if (c.spec() == QColor::Spec::Hsl) {
218 d->m_hsva.h = qBound(.0, c.hslHueF(), 1.0);
219 if (d->m_hsl) {
220 d->m_hsva.s = qBound(.0, c.hslSaturationF(), 1.0);
221 d->m_hsva.l = qBound(.0, c.lightnessF(), 1.0);
222 } else {
223 const auto sv = getSaturationAndValue(c.hslSaturationF(), c.lightnessF());
224 d->m_hsva.s = qBound(.0, sv.first, 1.0);
225 d->m_hsva.v = qBound(.0, sv.second, 1.0);
226 }
227 } else {
228 d->m_hsva.h = qBound(.0, d->m_hsl ? c.hslHueF() : c.hsvHueF(), 1.0);
229 d->m_hsva.s = qBound(.0, d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF(), 1.0);
230 d->m_hsva.v = qBound(.0, d->m_hsl ? c.lightnessF() : c.valueF(), 1.0);
231 }
232
233 d->m_hsva.a = c.alphaF();
234
235 emit colorChanged(color());
236}
237
238int QQuickColorDialogImpl::red() const
239{
240 return color().red();
241}
242
243void QQuickColorDialogImpl::setRed(int red)
244{
245 Q_D(QQuickColorDialogImpl);
246
247 auto c = color();
248
249 if (c.red() == red)
250 return;
251
252 c.setRed(red);
253
254 d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
255 d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
256 d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
257 d->m_hsva.a = c.alphaF();
258
259 emit colorChanged(c);
260}
261
262int QQuickColorDialogImpl::green() const
263{
264 return color().green();
265}
266
267void QQuickColorDialogImpl::setGreen(int green)
268{
269 Q_D(QQuickColorDialogImpl);
270
271 auto c = color();
272
273 if (c.green() == green)
274 return;
275
276 c.setGreen(green);
277
278 d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
279 d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
280 d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
281 d->m_hsva.a = c.alphaF();
282
283 emit colorChanged(c);
284}
285
286int QQuickColorDialogImpl::blue() const
287{
288 return color().blue();
289}
290
291void QQuickColorDialogImpl::setBlue(int blue)
292{
293 Q_D(QQuickColorDialogImpl);
294
295 auto c = color();
296
297 if (c.blue() == blue)
298 return;
299
300 c.setBlue(blue);
301
302 d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
303 d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
304 d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
305 d->m_hsva.a = c.alphaF();
306
307 emit colorChanged(c);
308}
309
310qreal QQuickColorDialogImpl::alpha() const
311{
312 Q_D(const QQuickColorDialogImpl);
313 return d->m_hsva.a;
314}
315
316void QQuickColorDialogImpl::setAlpha(qreal alpha)
317{
318 Q_D(QQuickColorDialogImpl);
319
320 if (!qt_is_finite(alpha))
321 return;
322
323 alpha = qBound(.0, alpha, 1.0);
324
325 if (qFuzzyCompare(d->m_hsva.a, alpha))
326 return;
327
328 d->m_hsva.a = alpha;
329
330 emit colorChanged(color());
331}
332
333qreal QQuickColorDialogImpl::hue() const
334{
335 Q_D(const QQuickColorDialogImpl);
336 return d->m_hsva.h;
337}
338
339void QQuickColorDialogImpl::setHue(qreal hue)
340{
341 Q_D(QQuickColorDialogImpl);
342
343 if (!qt_is_finite(hue))
344 return;
345
346 d->m_hsva.h = hue;
347
348 emit colorChanged(color());
349}
350
351qreal QQuickColorDialogImpl::saturation() const
352{
353 Q_D(const QQuickColorDialogImpl);
354 return d->m_hsva.s;
355}
356
357void QQuickColorDialogImpl::setSaturation(qreal saturation)
358{
359 Q_D(QQuickColorDialogImpl);
360 if (!qt_is_finite(saturation))
361 return;
362
363 d->m_hsva.s = saturation;
364
365 emit colorChanged(color());
366}
367
368qreal QQuickColorDialogImpl::value() const
369{
370 Q_D(const QQuickColorDialogImpl);
371 return d->m_hsl ? getSaturationAndValue(d->m_hsva.s, d->m_hsva.l).second : d->m_hsva.v;
372}
373
374void QQuickColorDialogImpl::setValue(qreal value)
375{
376 Q_D(QQuickColorDialogImpl);
377 if (!qt_is_finite(value))
378 return;
379
380 d->m_hsva.v = value;
381
382 if (d->m_hsl)
383 d->m_hsva.s = getSaturationAndValue(d->m_hsva.s, d->m_hsva.l).first;
384
385 d->m_hsl = false;
386 emit colorChanged(color());
387}
388
389qreal QQuickColorDialogImpl::lightness() const
390{
391 Q_D(const QQuickColorDialogImpl);
392 return d->m_hsl ? d->m_hsva.l : getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v).second;
393}
394
395void QQuickColorDialogImpl::setLightness(qreal lightness)
396{
397 Q_D(QQuickColorDialogImpl);
398 if (!qt_is_finite(lightness))
399 return;
400
401 d->m_hsva.l = lightness;
402
403 if (!d->m_hsl)
404 d->m_hsva.s = getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v).first;
405
406 d->m_hsl = true;
407 emit colorChanged(color());
408}
409
410bool QQuickColorDialogImpl::isHsl() const
411{
412 Q_D(const QQuickColorDialogImpl);
413 return d->m_hsl;
414}
415
416void QQuickColorDialogImpl::setHsl(bool hsl)
417{
418 Q_D(QQuickColorDialogImpl);
419
420 if (d->m_hsl == hsl)
421 return;
422
423 d->m_hsl = hsl;
424 emit specChanged();
425}
426
427QSharedPointer<QColorDialogOptions> QQuickColorDialogImpl::options() const
428{
429 Q_D(const QQuickColorDialogImpl);
430 return d->options;
431}
432
433void QQuickColorDialogImpl::setOptions(const QSharedPointer<QColorDialogOptions> &options)
434{
435 Q_D(QQuickColorDialogImpl);
436 d->options = options;
437
438 QQuickColorDialogImplAttached *attached = d->attachedOrWarn();
439
440 if (attached) {
441 const auto *integration = QGuiApplicationPrivate::platformIntegration();
442 const bool canSupportEyeDropper =
443 integration->hasCapability(QPlatformIntegration::ScreenWindowGrabbing)
444 || integration->services()->hasCapability(QPlatformServices::Capability::ColorPicking);
445 const bool offscreen = qgetenv("QT_QPA_PLATFORM").compare(QLatin1String("offscreen"), Qt::CaseInsensitive) == 0;
446 const bool noEyeDropperButton = (d->options && d->options->options() & QColorDialogOptions::NoEyeDropperButton);
447 attached->eyeDropperButton()->setVisible(!noEyeDropperButton && canSupportEyeDropper && !offscreen);
448
449 if (d->options) {
450 attached->buttonBox()->setVisible(
451 !(d->options->options() & QColorDialogOptions::NoButtons));
452
453 const bool showAlpha = d->options->options() & QColorDialogOptions::ShowAlphaChannel;
454 attached->alphaSlider()->setVisible(showAlpha);
455 attached->colorInputs()->setShowAlpha(showAlpha);
456 }
457 }
458}
459
460void QQuickColorDialogImpl::invokeEyeDropper()
461{
462 Q_D(QQuickColorDialogImpl);
463 d->eyeDropperEnter();
464}
465
466QQuickColorDialogImplAttached::QQuickColorDialogImplAttached(QObject *parent)
467 : QObject(*(new QQuickColorDialogImplAttachedPrivate), parent)
468{
469 if (!qobject_cast<QQuickColorDialogImpl *>(parent)) {
470 qmlWarning(this) << "ColorDialogImpl attached properties should only be "
471 << "accessed through the root ColorDialogImpl instance";
472 }
473}
474
475QQuickDialogButtonBox *QQuickColorDialogImplAttached::buttonBox() const
476{
477 Q_D(const QQuickColorDialogImplAttached);
478 return d->buttonBox;
479}
480
481void QQuickColorDialogImplAttached::setButtonBox(QQuickDialogButtonBox *buttonBox)
482{
483 Q_D(QQuickColorDialogImplAttached);
484 if (d->buttonBox == buttonBox)
485 return;
486
487 if (d->buttonBox) {
488 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
489 if (colorDialogImpl) {
490 auto dialogPrivate = QQuickDialogPrivate::get(colorDialogImpl);
491 QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::accepted,
492 dialogPrivate, &QQuickDialogPrivate::handleAccept);
493 QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::rejected,
494 dialogPrivate, &QQuickDialogPrivate::handleReject);
495 QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::clicked,
496 dialogPrivate, &QQuickDialogPrivate::handleClick);
497 }
498 }
499
500 d->buttonBox = buttonBox;
501
502 if (d->buttonBox) {
503 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
504 if (colorDialogImpl) {
505 auto dialogPrivate = QQuickDialogPrivate::get(colorDialogImpl);
506 QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::accepted,
507 dialogPrivate, &QQuickDialogPrivate::handleAccept);
508 QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::rejected,
509 dialogPrivate, &QQuickDialogPrivate::handleReject);
510 QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::clicked,
511 dialogPrivate, &QQuickDialogPrivate::handleClick);
512 }
513 }
514
515 emit buttonBoxChanged();
516}
517
518QQuickAbstractButton *QQuickColorDialogImplAttached::eyeDropperButton() const
519{
520 Q_D(const QQuickColorDialogImplAttached);
521 return d->eyeDropperButton;
522}
523
524void QQuickColorDialogImplAttached::setEyeDropperButton(QQuickAbstractButton *eyeDropperButton)
525{
526 Q_D(QQuickColorDialogImplAttached);
527 Q_ASSERT(!d->eyeDropperButton);
528 if (d->eyeDropperButton == eyeDropperButton)
529 return;
530
531 d->eyeDropperButton = eyeDropperButton;
532 if (auto dialog = qobject_cast<QQuickColorDialogImpl *>(parent()))
533 connect(d->eyeDropperButton, &QQuickAbstractButton::clicked, dialog, &QQuickColorDialogImpl::invokeEyeDropper);
534 emit eyeDropperButtonChanged();
535}
536
537QQuickAbstractColorPicker *QQuickColorDialogImplAttached::colorPicker() const
538{
539 Q_D(const QQuickColorDialogImplAttached);
540 return d->colorPicker;
541}
542void QQuickColorDialogImplAttached::setColorPicker(QQuickAbstractColorPicker *colorPicker)
543{
544 Q_D(QQuickColorDialogImplAttached);
545 if (d->colorPicker == colorPicker)
546 return;
547
548 if (d->colorPicker) {
549 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
550 if (colorDialogImpl) {
551 QObject::disconnect(d->colorPicker, &QQuickAbstractColorPicker::colorPicked,
552 colorDialogImpl, &QQuickColorDialogImpl::setColor);
553 }
554 }
555
556 d->colorPicker = colorPicker;
557
558 if (d->colorPicker) {
559 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
560 if (colorDialogImpl) {
561 QObject::connect(d->colorPicker, &QQuickAbstractColorPicker::colorPicked,
562 colorDialogImpl, &QQuickColorDialogImpl::setColor);
563 }
564 }
565
566 emit colorPickerChanged();
567}
568
569QQuickSlider *QQuickColorDialogImplAttached::alphaSlider() const
570{
571 Q_D(const QQuickColorDialogImplAttached);
572 return d->alphaSlider;
573}
574
575void QQuickColorDialogImplAttached::setAlphaSlider(QQuickSlider *alphaSlider)
576{
577 Q_D(QQuickColorDialogImplAttached);
578 if (d->alphaSlider == alphaSlider)
579 return;
580
581 if (d->alphaSlider) {
582 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
583 if (colorDialogImpl) {
584 auto dialogPrivate = QQuickColorDialogImplPrivate::get(colorDialogImpl);
585 QObjectPrivate::disconnect(d->alphaSlider, &QQuickSlider::moved,
586 dialogPrivate, &QQuickColorDialogImplPrivate::alphaSliderMoved);
587 }
588 }
589
590 d->alphaSlider = alphaSlider;
591
592 if (d->alphaSlider) {
593 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
594 if (colorDialogImpl) {
595 auto dialogPrivate = QQuickColorDialogImplPrivate::get(colorDialogImpl);
596 QObjectPrivate::connect(d->alphaSlider, &QQuickSlider::moved,
597 dialogPrivate, &QQuickColorDialogImplPrivate::alphaSliderMoved);
598 }
599 }
600
601 emit alphaSliderChanged();
602}
603
604QQuickColorInputs *QQuickColorDialogImplAttached::colorInputs() const
605{
606 Q_D(const QQuickColorDialogImplAttached);
607 return d->colorInputs;
608}
609
610void QQuickColorDialogImplAttached::setColorInputs(QQuickColorInputs *colorInputs)
611{
612 Q_D(QQuickColorDialogImplAttached);
613
614 if (d->colorInputs == colorInputs)
615 return;
616
617 if (d->colorInputs) {
618 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
619 if (colorDialogImpl)
620 QObject::disconnect(d->colorInputs, &QQuickColorInputs::colorModified,
621 colorDialogImpl, &QQuickColorDialogImpl::setColor);
622 }
623
624 d->colorInputs = colorInputs;
625
626 if (d->colorInputs) {
627 QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
628 if (colorDialogImpl)
629 QObject::connect(d->colorInputs, &QQuickColorInputs::colorModified,
630 colorDialogImpl, &QQuickColorDialogImpl::setColor);
631 }
632
633 emit colorInputsChanged();
634}
635
636QT_END_NAMESPACE
void eyeDropperLeave(const QPoint &pos, QQuickEyeDropperEventFilter::LeaveReason actionOnLeave)
void handleClick(QQuickAbstractButton *button) override
QQuickColorDialogImplAttached * attachedOrWarn()
void eyeDropperPointerMoved(const QPoint &pos)
bool eventFilter(QObject *obj, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.