diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/kernel/qplatformservices.cpp | 24 | ||||
-rw-r--r-- | src/gui/kernel/qplatformservices.h | 20 | ||||
-rw-r--r-- | src/gui/platform/unix/qgenericunixservices.cpp | 143 | ||||
-rw-r--r-- | src/gui/platform/unix/qgenericunixservices_p.h | 5 | ||||
-rw-r--r-- | src/widgets/dialogs/qcolordialog.cpp | 19 |
5 files changed, 209 insertions, 2 deletions
diff --git a/src/gui/kernel/qplatformservices.cpp b/src/gui/kernel/qplatformservices.cpp index a0db26dda3..1c8062389d 100644 --- a/src/gui/kernel/qplatformservices.cpp +++ b/src/gui/kernel/qplatformservices.cpp @@ -19,6 +19,19 @@ QT_BEGIN_NAMESPACE \brief The QPlatformServices provides the backend for desktop-related functionality. */ +/*! + \enum QPlatformServices::Capability + + Capabilities are used to determine a specific platform service's availability. + + \value ColorPickingFromScreen The platform natively supports color picking from screen. + This capability indicates that the platform supports "opaque" color picking, where the + platform implements a complete user experience for color picking and outputs a color. + This is in contrast to the application implementing the color picking user experience + (taking care of showing a cross hair, instructing the platform integration to obtain + the color at a given pixel, etc.). The related service function is pickColor(). + */ + QPlatformServices::QPlatformServices() { } @@ -49,5 +62,16 @@ QByteArray QPlatformServices::desktopEnvironment() const return QByteArray("UNKNOWN"); } +QPlatformServiceColorPicker *QPlatformServices::colorPicker(QWindow *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + +bool QPlatformServices::hasCapability(Capability capability) const +{ + Q_UNUSED(capability) + return false; +} QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformservices.h b/src/gui/kernel/qplatformservices.h index 50db36689e..063247af08 100644 --- a/src/gui/kernel/qplatformservices.h +++ b/src/gui/kernel/qplatformservices.h @@ -14,16 +14,32 @@ // #include <QtGui/qtguiglobal.h> +#include <QtCore/qobject.h> QT_BEGIN_NAMESPACE class QUrl; +class QWindow; + +class Q_GUI_EXPORT QPlatformServiceColorPicker : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; + virtual void pickColor() = 0; +Q_SIGNALS: + void colorPicked(const QColor &color); +}; class Q_GUI_EXPORT QPlatformServices { public: Q_DISABLE_COPY_MOVE(QPlatformServices) + enum Capability { + ColorPicking, + }; + QPlatformServices(); virtual ~QPlatformServices() { } @@ -31,6 +47,10 @@ public: virtual bool openDocument(const QUrl &url); virtual QByteArray desktopEnvironment() const; + + virtual bool hasCapability(Capability capability) const; + + virtual QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr); }; QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qgenericunixservices.cpp b/src/gui/platform/unix/qgenericunixservices.cpp index 54a0406cab..eb66e5b1a4 100644 --- a/src/gui/platform/unix/qgenericunixservices.cpp +++ b/src/gui/platform/unix/qgenericunixservices.cpp @@ -257,8 +257,132 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url, const QStr return QDBusConnection::sessionBus().call(message); } + +namespace { +struct XDGDesktopColor +{ + double r = 0; + double g = 0; + double b = 0; + + QColor toQColor() const + { + constexpr auto rgbMax = 255; + return { static_cast<int>(r * rgbMax), static_cast<int>(g * rgbMax), + static_cast<int>(b * rgbMax) }; + } +}; + +const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) +{ + argument.beginStructure(); + argument >> myStruct.r >> myStruct.g >> myStruct.b; + argument.endStructure(); + return argument; +} + +class XdgDesktopPortalColorPicker : public QPlatformServiceColorPicker +{ + Q_OBJECT +public: + XdgDesktopPortalColorPicker(const QString &parentWindowId, QWindow *parent) + : QPlatformServiceColorPicker(parent), m_parentWindowId(parentWindowId) + { + } + + void pickColor() override + { + // DBus signature: + // PickColor (IN s parent_window, + // IN a{sv} options + // OUT o handle) + // Options: + // handle_token (s) - A string that will be used as the last element of the @handle. + + QDBusMessage message = QDBusMessage::createMethodCall( + "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.portal.Screenshot"_L1, "PickColor"_L1); + message << m_parentWindowId << QVariantMap(); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply<QDBusObjectPath> reply = *watcher; + if (reply.isError()) { + qWarning("DBus call to pick color failed: %s", + qPrintable(reply.error().message())); + Q_EMIT colorPicked({}); + } else { + QDBusConnection::sessionBus().connect( + "org.freedesktop.portal.Desktop"_L1, reply.value().path(), + "org.freedesktop.portal.Request"_L1, "Response"_L1, this, + // clang-format off + SLOT(gotColorResponse(uint,QVariantMap)) + // clang-format on + ); + } + }); + } + +private Q_SLOTS: + void gotColorResponse(uint result, const QVariantMap &map) + { + if (result != 0) + return; + XDGDesktopColor color{}; + map.value(u"color"_s).value<QDBusArgument>() >> color; + Q_EMIT colorPicked(color.toQColor()); + deleteLater(); + } + +private: + const QString m_parentWindowId; +}; +} // namespace + #endif // QT_CONFIG(dbus) +QGenericUnixServices::QGenericUnixServices() +{ +#if QT_CONFIG(dbus) + QDBusMessage message = QDBusMessage::createMethodCall( + "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.DBus.Properties"_L1, "Get"_L1); + message << "org.freedesktop.portal.Screenshot"_L1 + << "version"_L1; + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply<QVariant> reply = *watcher; + if (!reply.isError() && reply.value().toUInt() >= 2) + m_hasScreenshotPortalWithColorPicking = true; + }); + +#endif +} + +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ +#if QT_CONFIG(dbus) + // Make double sure that we are in a wayland environment. In particular check + // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. + // Outside wayland we'll rather rely on other means than the XDG desktop portal. + if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") + || QGuiApplication::platformName().startsWith("wayland"_L1)) { + return new XdgDesktopPortalColorPicker(portalWindowIdentifier(parent), parent); + } + return nullptr; +#else + Q_UNUSED(parent); + return nullptr; +#endif +} + QByteArray QGenericUnixServices::desktopEnvironment() const { static const QByteArray result = detectDesktopEnvironment(); @@ -322,6 +446,8 @@ bool QGenericUnixServices::openDocument(const QUrl &url) } #else +QGenericUnixServices::QGenericUnixServices() = default; + QByteArray QGenericUnixServices::desktopEnvironment() const { return QByteArrayLiteral("UNKNOWN"); @@ -341,6 +467,12 @@ bool QGenericUnixServices::openDocument(const QUrl &url) return false; } +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + #endif // QT_NO_MULTIPROCESS QString QGenericUnixServices::portalWindowIdentifier(QWindow *window) @@ -351,4 +483,15 @@ QString QGenericUnixServices::portalWindowIdentifier(QWindow *window) return QString(); } +bool QGenericUnixServices::hasCapability(Capability capability) const +{ + switch (capability) { + case Capability::ColorPicking: + return m_hasScreenshotPortalWithColorPicking; + } + return false; +} + QT_END_NAMESPACE + +#include "qgenericunixservices.moc" diff --git a/src/gui/platform/unix/qgenericunixservices_p.h b/src/gui/platform/unix/qgenericunixservices_p.h index 2ddba91a3a..701bcfc78f 100644 --- a/src/gui/platform/unix/qgenericunixservices_p.h +++ b/src/gui/platform/unix/qgenericunixservices_p.h @@ -26,18 +26,21 @@ class QWindow; class Q_GUI_EXPORT QGenericUnixServices : public QPlatformServices { public: - QGenericUnixServices() {} + QGenericUnixServices(); QByteArray desktopEnvironment() const override; + bool hasCapability(Capability capability) const override; bool openUrl(const QUrl &url) override; bool openDocument(const QUrl &url) override; + QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr) override; virtual QString portalWindowIdentifier(QWindow *window); private: QString m_webBrowser; QString m_documentLauncher; + bool m_hasScreenshotPortalWithColorPicking = false; }; QT_END_NAMESPACE diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index deff2fbf27..e3265278c0 100644 --- a/src/widgets/dialogs/qcolordialog.cpp +++ b/src/widgets/dialogs/qcolordialog.cpp @@ -40,6 +40,7 @@ #include "private/qdialog_p.h" #include <qpa/qplatformintegration.h> +#include <qpa/qplatformservices.h> #include <private/qguiapplication_p.h> #include <algorithm> @@ -1576,6 +1577,20 @@ void QColorDialogPrivate::_q_newStandard(int r, int c) void QColorDialogPrivate::_q_pickScreenColor() { Q_Q(QColorDialog); + + auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services(); + if (platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) { + if (auto *colorPicker = platformServices->colorPicker(q->windowHandle())) { + q->connect(colorPicker, &QPlatformServiceColorPicker::colorPicked, q, + [q, colorPicker](const QColor &color) { + colorPicker->deleteLater(); + q->setCurrentColor(color); + }); + colorPicker->pickColor(); + return; + } + } + if (!colorPickingEventFilter) colorPickingEventFilter = new QColorPickingEventFilter(this, q); q->installEventFilter(colorPickingEventFilter); @@ -1854,7 +1869,9 @@ void QColorDialogPrivate::retranslateStrings() bool QColorDialogPrivate::supportsColorPicking() const { - return QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ScreenWindowGrabbing); + const auto integration = QGuiApplicationPrivate::platformIntegration(); + return integration->hasCapability(QPlatformIntegration::ScreenWindowGrabbing) + || integration->services()->hasCapability(QPlatformServices::Capability::ColorPicking); } bool QColorDialogPrivate::canBeNativeDialog() const |