Revision control

Copy as Markdown

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qquickcolordialogimpl_p.h"
#include "qquickcolordialogimpl_p_p.h"
#include "qquickcolordialogutils_p.h"
#include <QtQuickTemplates2/private/qquickslider_p.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformservices.h>
#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
QColor grabScreenColor(const QPoint &p)
{
QScreen *screen = QGuiApplication::screenAt(p);
if (!screen)
screen = QGuiApplication::primaryScreen();
const QRect screenRect = screen->geometry();
const QPixmap pixmap =
screen->grabWindow(0, p.x() - screenRect.x(), p.y() - screenRect.y(), 1, 1);
const QImage i = pixmap.toImage();
return i.pixel(0, 0);
}
bool QQuickEyeDropperEventFilter::eventFilter(QObject *obj, QEvent *event)
{
switch (event->type()) {
case QEvent::MouseMove: {
m_lastPosition = static_cast<QMouseEvent *>(event)->globalPosition().toPoint();
m_update(m_lastPosition);
return true;
}
case QEvent::MouseButtonRelease: {
m_lastPosition = static_cast<QMouseEvent *>(event)->globalPosition().toPoint();
m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Default);
return true;
}
case QEvent::MouseButtonPress:
return true;
case QEvent::KeyPress: {
auto keyEvent = static_cast<QKeyEvent *>(event);
#if QT_CONFIG(shortcut)
if (keyEvent->matches(QKeySequence::Cancel))
m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Cancel);
else
#endif
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Default);
} else if (keyEvent->key() == Qt::Key_Escape) {
m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Cancel);
}
keyEvent->accept();
return true;
}
default:
return QObject::eventFilter(obj, event);
}
}
QQuickColorDialogImplPrivate::QQuickColorDialogImplPrivate() = default;
QQuickColorDialogImplPrivate::~QQuickColorDialogImplPrivate()
{
if (m_eyeDropperMode)
eyeDropperLeave(QCursor::pos(), QQuickEyeDropperEventFilter::LeaveReason::Default);
}
void QQuickColorDialogImplPrivate::handleClick(QQuickAbstractButton *button)
{
Q_Q(QQuickColorDialogImpl);
const auto c = q->color();
if (buttonRole(button) == QPlatformDialogHelper::AcceptRole && c.isValid()) {
q->setColor(c);
q->accept();
}
QQuickDialogPrivate::handleClick(button);
}
QQuickColorDialogImplAttached *QQuickColorDialogImplPrivate::attachedOrWarn()
{
Q_Q(QQuickColorDialogImpl);
QQuickColorDialogImplAttached *attached = static_cast<QQuickColorDialogImplAttached *>(
qmlAttachedPropertiesObject<QQuickColorDialogImpl>(q, false));
if (!attached)
qmlWarning(q) << "Expected ColorDialogImpl attached object to be present on" << this;
return attached;
}
void QQuickColorDialogImplPrivate::eyeDropperEnter()
{
Q_Q(QQuickColorDialogImpl);
if (m_eyeDropperMode)
return;
if (m_eyeDropperWindow.isNull()) {
if (window.isNull()) {
qWarning() << "No window found, cannot enter eyeDropperMode.";
return;
}
m_eyeDropperWindow = window;
}
if (auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services();
platformServices && platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) {
if (auto *colorPickerService = platformServices->colorPicker(m_eyeDropperWindow)) {
q->connect(colorPickerService, &QPlatformServiceColorPicker::colorPicked, q,
[q, colorPickerService](const QColor &color) {
colorPickerService->deleteLater();
q->setColor(color);
});
colorPickerService->pickColor();
return;
}
}
m_eyeDropperPreviousColor = q->color();
if (!bool(eyeDropperEventFilter))
eyeDropperEventFilter.reset(new QQuickEyeDropperEventFilter(
[this](QPoint pos, QQuickEyeDropperEventFilter::LeaveReason c) {
eyeDropperLeave(pos, c);
},
[this](QPoint pos) { eyeDropperPointerMoved(pos); }));
if (m_eyeDropperWindow->setMouseGrabEnabled(true)) {
#if QT_CONFIG(cursor)
QGuiApplication::setOverrideCursor(Qt::CrossCursor);
#endif
m_eyeDropperWindow->installEventFilter(eyeDropperEventFilter.get());
m_eyeDropperMode = true;
}
}
void QQuickColorDialogImplPrivate::eyeDropperLeave(
const QPoint &pos, QQuickEyeDropperEventFilter::LeaveReason actionOnLeave)
{
Q_Q(QQuickColorDialogImpl);
if (!m_eyeDropperMode)
return;
if (!m_eyeDropperWindow) {
qWarning() << "Window not set, cannot leave eyeDropperMode.";
return;
}
const QColor colorToUse = actionOnLeave == QQuickEyeDropperEventFilter::LeaveReason::Cancel
? m_eyeDropperPreviousColor
: grabScreenColor(pos);
q->setColor(colorToUse);
m_eyeDropperWindow->removeEventFilter(eyeDropperEventFilter.get());
m_eyeDropperWindow->setMouseGrabEnabled(false);
#if QT_CONFIG(cursor)
QGuiApplication::restoreOverrideCursor();
#endif
m_eyeDropperMode = false;
m_eyeDropperWindow.clear();
}
void QQuickColorDialogImplPrivate::eyeDropperPointerMoved(const QPoint &pos)
{
Q_Q(QQuickColorDialogImpl);
q->setColor(grabScreenColor(pos));
}
void QQuickColorDialogImplPrivate::alphaSliderMoved()
{
Q_Q(QQuickColorDialogImpl);
if (auto attached = attachedOrWarn())
q->setAlpha(attached->alphaSlider()->value());
}
QQuickColorDialogImpl::QQuickColorDialogImpl(QObject *parent)
: QQuickDialog(*(new QQuickColorDialogImplPrivate), parent)
{
}
QQuickColorDialogImplAttached *QQuickColorDialogImpl::qmlAttachedProperties(QObject *object)
{
return new QQuickColorDialogImplAttached(object);
}
QColor QQuickColorDialogImpl::color() const
{
Q_D(const QQuickColorDialogImpl);
return d->m_hsl ? QColor::fromHslF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.l, d->m_hsva.a)
: QColor::fromHsvF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.v, d->m_hsva.a);
}
void QQuickColorDialogImpl::setColor(const QColor &c)
{
Q_D(QQuickColorDialogImpl);
if (const QColor old = color(); old.spec() == c.spec() && old == c)
return;
// If we get a QColor from an Hsv or Hsl color system,
// we want to get the raw values without the risk of QColor converting them,
// and possible deleting relevant information for achromatic cases.
if (c.spec() == QColor::Spec::Hsv) {
d->m_hsva.h = qBound(.0, c.hsvHueF(), 1.0);
if (d->m_hsl) {
const auto sl = getSaturationAndLightness(c.hsvSaturationF(), c.valueF());
d->m_hsva.s = qBound(.0, sl.first, 1.0);
d->m_hsva.l = qBound(.0, sl.second, 1.0);
} else {
d->m_hsva.s = qBound(.0, c.hsvSaturationF(), 1.0);
d->m_hsva.v = qBound(.0, c.valueF(), 1.0);
}
} else if (c.spec() == QColor::Spec::Hsl) {
d->m_hsva.h = qBound(.0, c.hslHueF(), 1.0);
if (d->m_hsl) {
d->m_hsva.s = qBound(.0, c.hslSaturationF(), 1.0);
d->m_hsva.l = qBound(.0, c.lightnessF(), 1.0);
} else {
const auto sv = getSaturationAndValue(c.hslSaturationF(), c.lightnessF());
d->m_hsva.s = qBound(.0, sv.first, 1.0);
d->m_hsva.v = qBound(.0, sv.second, 1.0);
}
} else {
d->m_hsva.h = qBound(.0, d->m_hsl ? c.hslHueF() : c.hsvHueF(), 1.0);
d->m_hsva.s = qBound(.0, d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF(), 1.0);
d->m_hsva.v = qBound(.0, d->m_hsl ? c.lightnessF() : c.valueF(), 1.0);
}
d->m_hsva.a = c.alphaF();
emit colorChanged(color());
}
int QQuickColorDialogImpl::red() const
{
return color().red();
}
void QQuickColorDialogImpl::setRed(int red)
{
Q_D(QQuickColorDialogImpl);
auto c = color();
if (c.red() == red)
return;
c.setRed(red);
d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
d->m_hsva.a = c.alphaF();
emit colorChanged(c);
}
int QQuickColorDialogImpl::green() const
{
return color().green();
}
void QQuickColorDialogImpl::setGreen(int green)
{
Q_D(QQuickColorDialogImpl);
auto c = color();
if (c.green() == green)
return;
c.setGreen(green);
d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
d->m_hsva.a = c.alphaF();
emit colorChanged(c);
}
int QQuickColorDialogImpl::blue() const
{
return color().blue();
}
void QQuickColorDialogImpl::setBlue(int blue)
{
Q_D(QQuickColorDialogImpl);
auto c = color();
if (c.blue() == blue)
return;
c.setBlue(blue);
d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
d->m_hsva.a = c.alphaF();
emit colorChanged(c);
}
qreal QQuickColorDialogImpl::alpha() const
{
Q_D(const QQuickColorDialogImpl);
return d->m_hsva.a;
}
void QQuickColorDialogImpl::setAlpha(qreal alpha)
{
Q_D(QQuickColorDialogImpl);
if (!qt_is_finite(alpha))
return;
alpha = qBound(.0, alpha, 1.0);
if (qFuzzyCompare(d->m_hsva.a, alpha))
return;
d->m_hsva.a = alpha;
emit colorChanged(color());
}
qreal QQuickColorDialogImpl::hue() const
{
Q_D(const QQuickColorDialogImpl);
return d->m_hsva.h;
}
void QQuickColorDialogImpl::setHue(qreal hue)
{
Q_D(QQuickColorDialogImpl);
if (!qt_is_finite(hue))
return;
d->m_hsva.h = hue;
emit colorChanged(color());
}
qreal QQuickColorDialogImpl::saturation() const
{
Q_D(const QQuickColorDialogImpl);
return d->m_hsva.s;
}
void QQuickColorDialogImpl::setSaturation(qreal saturation)
{
Q_D(QQuickColorDialogImpl);
if (!qt_is_finite(saturation))
return;
d->m_hsva.s = saturation;
emit colorChanged(color());
}
qreal QQuickColorDialogImpl::value() const
{
Q_D(const QQuickColorDialogImpl);
return d->m_hsl ? getSaturationAndValue(d->m_hsva.s, d->m_hsva.l).second : d->m_hsva.v;
}
void QQuickColorDialogImpl::setValue(qreal value)
{
Q_D(QQuickColorDialogImpl);
if (!qt_is_finite(value))
return;
d->m_hsva.v = value;
if (d->m_hsl)
d->m_hsva.s = getSaturationAndValue(d->m_hsva.s, d->m_hsva.l).first;
d->m_hsl = false;
emit colorChanged(color());
}
qreal QQuickColorDialogImpl::lightness() const
{
Q_D(const QQuickColorDialogImpl);
return d->m_hsl ? d->m_hsva.l : getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v).second;
}
void QQuickColorDialogImpl::setLightness(qreal lightness)
{
Q_D(QQuickColorDialogImpl);
if (!qt_is_finite(lightness))
return;
d->m_hsva.l = lightness;
if (!d->m_hsl)
d->m_hsva.s = getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v).first;
d->m_hsl = true;
emit colorChanged(color());
}
bool QQuickColorDialogImpl::isHsl() const
{
Q_D(const QQuickColorDialogImpl);
return d->m_hsl;
}
void QQuickColorDialogImpl::setHsl(bool hsl)
{
Q_D(QQuickColorDialogImpl);
if (d->m_hsl == hsl)
return;
d->m_hsl = hsl;
emit specChanged();
}
QSharedPointer<QColorDialogOptions> QQuickColorDialogImpl::options() const
{
Q_D(const QQuickColorDialogImpl);
return d->options;
}
void QQuickColorDialogImpl::setOptions(const QSharedPointer<QColorDialogOptions> &options)
{
Q_D(QQuickColorDialogImpl);
d->options = options;
QQuickColorDialogImplAttached *attached = d->attachedOrWarn();
if (attached) {
const auto *integration = QGuiApplicationPrivate::platformIntegration();
const bool canSupportEyeDropper =
integration->hasCapability(QPlatformIntegration::ScreenWindowGrabbing)
|| integration->services()->hasCapability(QPlatformServices::Capability::ColorPicking);
const bool offscreen = qgetenv("QT_QPA_PLATFORM").compare(QLatin1String("offscreen"), Qt::CaseInsensitive) == 0;
const bool noEyeDropperButton = (d->options && d->options->options() & QColorDialogOptions::NoEyeDropperButton);
attached->eyeDropperButton()->setVisible(!noEyeDropperButton && canSupportEyeDropper && !offscreen);
if (d->options) {
attached->buttonBox()->setVisible(
!(d->options->options() & QColorDialogOptions::NoButtons));
const bool showAlpha = d->options->options() & QColorDialogOptions::ShowAlphaChannel;
attached->alphaSlider()->setVisible(showAlpha);
attached->colorInputs()->setShowAlpha(showAlpha);
}
}
}
void QQuickColorDialogImpl::invokeEyeDropper()
{
Q_D(QQuickColorDialogImpl);
d->eyeDropperEnter();
}
void QQuickColorDialogImpl::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
{
Q_D(QQuickColorDialogImpl);
QQuickDialog::itemChange(change, data);
if (change != QQuickItem::ItemVisibleHasChanged || !isComponentComplete() || !data.boolValue)
return;
if (QQuickColorDialogImplAttached *attached = d->attachedOrWarn(); attached && attached->buttonBox())
attached->buttonBox()->forceActiveFocus(Qt::OtherFocusReason);
}
QQuickColorDialogImplAttached::QQuickColorDialogImplAttached(QObject *parent)
: QObject(*(new QQuickColorDialogImplAttachedPrivate), parent)
{
if (!qobject_cast<QQuickColorDialogImpl *>(parent)) {
qmlWarning(this) << "ColorDialogImpl attached properties should only be "
<< "accessed through the root ColorDialogImpl instance";
}
}
QQuickDialogButtonBox *QQuickColorDialogImplAttached::buttonBox() const
{
Q_D(const QQuickColorDialogImplAttached);
return d->buttonBox;
}
void QQuickColorDialogImplAttached::setButtonBox(QQuickDialogButtonBox *buttonBox)
{
Q_D(QQuickColorDialogImplAttached);
if (d->buttonBox == buttonBox)
return;
if (d->buttonBox) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl) {
auto dialogPrivate = QQuickDialogPrivate::get(colorDialogImpl);
QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::accepted,
dialogPrivate, &QQuickDialogPrivate::handleAccept);
QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::rejected,
dialogPrivate, &QQuickDialogPrivate::handleReject);
QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::clicked,
dialogPrivate, &QQuickDialogPrivate::handleClick);
}
}
d->buttonBox = buttonBox;
if (d->buttonBox) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl) {
auto dialogPrivate = QQuickDialogPrivate::get(colorDialogImpl);
QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::accepted,
dialogPrivate, &QQuickDialogPrivate::handleAccept);
QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::rejected,
dialogPrivate, &QQuickDialogPrivate::handleReject);
QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::clicked,
dialogPrivate, &QQuickDialogPrivate::handleClick);
}
}
emit buttonBoxChanged();
}
QQuickAbstractButton *QQuickColorDialogImplAttached::eyeDropperButton() const
{
Q_D(const QQuickColorDialogImplAttached);
return d->eyeDropperButton;
}
void QQuickColorDialogImplAttached::setEyeDropperButton(QQuickAbstractButton *eyeDropperButton)
{
Q_D(QQuickColorDialogImplAttached);
Q_ASSERT(!d->eyeDropperButton);
if (d->eyeDropperButton == eyeDropperButton)
return;
d->eyeDropperButton = eyeDropperButton;
if (auto dialog = qobject_cast<QQuickColorDialogImpl *>(parent()))
connect(d->eyeDropperButton, &QQuickAbstractButton::clicked, dialog, &QQuickColorDialogImpl::invokeEyeDropper);
emit eyeDropperButtonChanged();
}
QQuickAbstractColorPicker *QQuickColorDialogImplAttached::colorPicker() const
{
Q_D(const QQuickColorDialogImplAttached);
return d->colorPicker;
}
void QQuickColorDialogImplAttached::setColorPicker(QQuickAbstractColorPicker *colorPicker)
{
Q_D(QQuickColorDialogImplAttached);
if (d->colorPicker == colorPicker)
return;
if (d->colorPicker) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl) {
QObject::disconnect(d->colorPicker, &QQuickAbstractColorPicker::colorPicked,
colorDialogImpl, &QQuickColorDialogImpl::setColor);
}
}
d->colorPicker = colorPicker;
if (d->colorPicker) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl) {
QObject::connect(d->colorPicker, &QQuickAbstractColorPicker::colorPicked,
colorDialogImpl, &QQuickColorDialogImpl::setColor);
}
}
emit colorPickerChanged();
}
QQuickSlider *QQuickColorDialogImplAttached::alphaSlider() const
{
Q_D(const QQuickColorDialogImplAttached);
return d->alphaSlider;
}
void QQuickColorDialogImplAttached::setAlphaSlider(QQuickSlider *alphaSlider)
{
Q_D(QQuickColorDialogImplAttached);
if (d->alphaSlider == alphaSlider)
return;
if (d->alphaSlider) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl) {
auto dialogPrivate = QQuickColorDialogImplPrivate::get(colorDialogImpl);
QObjectPrivate::disconnect(d->alphaSlider, &QQuickSlider::moved,
dialogPrivate, &QQuickColorDialogImplPrivate::alphaSliderMoved);
}
}
d->alphaSlider = alphaSlider;
if (d->alphaSlider) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl) {
auto dialogPrivate = QQuickColorDialogImplPrivate::get(colorDialogImpl);
QObjectPrivate::connect(d->alphaSlider, &QQuickSlider::moved,
dialogPrivate, &QQuickColorDialogImplPrivate::alphaSliderMoved);
}
}
emit alphaSliderChanged();
}
QQuickColorInputs *QQuickColorDialogImplAttached::colorInputs() const
{
Q_D(const QQuickColorDialogImplAttached);
return d->colorInputs;
}
void QQuickColorDialogImplAttached::setColorInputs(QQuickColorInputs *colorInputs)
{
Q_D(QQuickColorDialogImplAttached);
if (d->colorInputs == colorInputs)
return;
if (d->colorInputs) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl)
QObject::disconnect(d->colorInputs, &QQuickColorInputs::colorModified,
colorDialogImpl, &QQuickColorDialogImpl::setColor);
}
d->colorInputs = colorInputs;
if (d->colorInputs) {
QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
if (colorDialogImpl)
QObject::connect(d->colorInputs, &QQuickColorInputs::colorModified,
colorDialogImpl, &QQuickColorDialogImpl::setColor);
}
emit colorInputsChanged();
}
QT_END_NAMESPACE