Send parent_window handle to desktop portal

Sending parent_window to the portal allows it/the window manager to
better place portal dialogs in respect to the application window, for example
on top of it instead of a random position. For this use the focusWindow()
(if available) since the API in QDesktopServices does not take a QWindow
parameter. The file dialog has an explicit parent that can be used.
The introduced virtual is provided as a hook for the wayland platform
that already includes a class that inherits from QGenericUnixServices.

Change-Id: I5571f08d07e5a4e3ae32efd1f09e2727cec9e7c5
Reviewed-by: David Edmundson <davidedmundson@kde.org>
This commit is contained in:
David Redondo 2022-02-23 11:12:17 +01:00
parent f3cd571687
commit c142fd8f68
3 changed files with 45 additions and 14 deletions

View File

@ -3,6 +3,8 @@
#include "qgenericunixservices_p.h" #include "qgenericunixservices_p.h"
#include <QtGui/private/qtguiglobal_p.h> #include <QtGui/private/qtguiglobal_p.h>
#include "qguiapplication.h"
#include "qwindow.h"
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QFile> #include <QtCore/QFile>
@ -152,7 +154,7 @@ static inline bool checkNeedPortalSupport()
return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, "flatpak-info"_L1).isEmpty() || qEnvironmentVariableIsSet("SNAP"); return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, "flatpak-info"_L1).isEmpty() || qEnvironmentVariableIsSet("SNAP");
} }
static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url, const QString &parentWindow)
{ {
// DBus signature: // DBus signature:
// OpenFile (IN s parent_window, // OpenFile (IN s parent_window,
@ -176,19 +178,19 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
const QVariantMap options = {{"writable"_L1, true}}; const QVariantMap options = {{"writable"_L1, true}};
// FIXME parent_window_id message << parentWindow << QVariant::fromValue(descriptor) << options;
message << QString() << QVariant::fromValue(descriptor) << options;
return QDBusConnection::sessionBus().call(message); return QDBusConnection::sessionBus().call(message);
} }
#else #else
Q_UNUSED(url); Q_UNUSED(url);
Q_UNUSED(parentWindow)
#endif #endif
return QDBusMessage::createError(QDBusError::InternalError, qt_error_string()); return QDBusMessage::createError(QDBusError::InternalError, qt_error_string());
} }
static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url) static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url, const QString &parentWindow)
{ {
// DBus signature: // DBus signature:
// OpenURI (IN s parent_window, // OpenURI (IN s parent_window,
@ -206,12 +208,12 @@ static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url)
"org.freedesktop.portal.OpenURI"_L1, "org.freedesktop.portal.OpenURI"_L1,
"OpenURI"_L1); "OpenURI"_L1);
// FIXME parent_window_id and handle writable option // FIXME parent_window_id and handle writable option
message << QString() << url.toString() << QVariantMap(); message << parentWindow << url.toString() << QVariantMap();
return QDBusConnection::sessionBus().call(message); return QDBusConnection::sessionBus().call(message);
} }
static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url, const QString &parentWindow)
{ {
// DBus signature: // DBus signature:
// ComposeEmail (IN s parent_window, // ComposeEmail (IN s parent_window,
@ -251,8 +253,7 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url)
"org.freedesktop.portal.Email"_L1, "org.freedesktop.portal.Email"_L1,
"ComposeEmail"_L1); "ComposeEmail"_L1);
// FIXME parent_window_id message << parentWindow << options;
message << QString() << options;
return QDBusConnection::sessionBus().call(message); return QDBusConnection::sessionBus().call(message);
} }
@ -269,7 +270,10 @@ bool QGenericUnixServices::openUrl(const QUrl &url)
if (url.scheme() == "mailto"_L1) { if (url.scheme() == "mailto"_L1) {
#if QT_CONFIG(dbus) #if QT_CONFIG(dbus)
if (checkNeedPortalSupport()) { if (checkNeedPortalSupport()) {
QDBusError error = xdgDesktopPortalSendEmail(url); const QString parentWindow = QGuiApplication::focusWindow()
? portalWindowIdentifier(QGuiApplication::focusWindow())
: QString();
QDBusError error = xdgDesktopPortalSendEmail(url, parentWindow);
if (!error.isValid()) if (!error.isValid())
return true; return true;
@ -281,7 +285,10 @@ bool QGenericUnixServices::openUrl(const QUrl &url)
#if QT_CONFIG(dbus) #if QT_CONFIG(dbus)
if (checkNeedPortalSupport()) { if (checkNeedPortalSupport()) {
QDBusError error = xdgDesktopPortalOpenUrl(url); const QString parentWindow = QGuiApplication::focusWindow()
? portalWindowIdentifier(QGuiApplication::focusWindow())
: QString();
QDBusError error = xdgDesktopPortalOpenUrl(url, parentWindow);
if (!error.isValid()) if (!error.isValid())
return true; return true;
} }
@ -298,7 +305,10 @@ bool QGenericUnixServices::openDocument(const QUrl &url)
{ {
#if QT_CONFIG(dbus) #if QT_CONFIG(dbus)
if (checkNeedPortalSupport()) { if (checkNeedPortalSupport()) {
QDBusError error = xdgDesktopPortalOpenFile(url); const QString parentWindow = QGuiApplication::focusWindow()
? portalWindowIdentifier(QGuiApplication::focusWindow())
: QString();
QDBusError error = xdgDesktopPortalOpenFile(url, parentWindow);
if (!error.isValid()) if (!error.isValid())
return true; return true;
} }
@ -333,4 +343,12 @@ bool QGenericUnixServices::openDocument(const QUrl &url)
#endif // QT_NO_MULTIPROCESS #endif // QT_NO_MULTIPROCESS
QString QGenericUnixServices::portalWindowIdentifier(QWindow *window)
{
if (QGuiApplication::platformName() == QLatin1String("xcb"))
return "x11:"_L1 + QString::number(window->winId(), 16);
return QString();
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -21,6 +21,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QWindow;
class Q_GUI_EXPORT QGenericUnixServices : public QPlatformServices class Q_GUI_EXPORT QGenericUnixServices : public QPlatformServices
{ {
public: public:
@ -31,6 +33,8 @@ public:
bool openUrl(const QUrl &url) override; bool openUrl(const QUrl &url) override;
bool openDocument(const QUrl &url) override; bool openDocument(const QUrl &url) override;
virtual QString portalWindowIdentifier(QWindow *window);
private: private:
QString m_webBrowser; QString m_webBrowser;
QString m_documentLauncher; QString m_documentLauncher;

View File

@ -3,6 +3,10 @@
#include "qxdgdesktopportalfiledialog_p.h" #include "qxdgdesktopportalfiledialog_p.h"
#include <private/qgenericunixservices_p.h>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <QDBusConnection> #include <QDBusConnection>
#include <QDBusMessage> #include <QDBusMessage>
#include <QDBusPendingCall> #include <QDBusPendingCall>
@ -157,8 +161,6 @@ void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::Wi
"/org/freedesktop/portal/desktop"_L1, "/org/freedesktop/portal/desktop"_L1,
"org.freedesktop.portal.FileChooser"_L1, "org.freedesktop.portal.FileChooser"_L1,
d->saveFile ? "SaveFile"_L1 : "OpenFile"_L1); d->saveFile ? "SaveFile"_L1 : "OpenFile"_L1);
QString parentWindowId = "x11:"_L1 + QString::number(parent ? parent->winId() : 0, 16);
QVariantMap options; QVariantMap options;
if (!d->acceptLabel.isEmpty()) if (!d->acceptLabel.isEmpty())
options.insert("accept_label"_L1, d->acceptLabel); options.insert("accept_label"_L1, d->acceptLabel);
@ -262,7 +264,14 @@ void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::Wi
// TODO choices a(ssa(ss)s) // TODO choices a(ssa(ss)s)
// List of serialized combo boxes to add to the file chooser. // List of serialized combo boxes to add to the file chooser.
message << parentWindowId << d->title << options; auto unixServices = dynamic_cast<QGenericUnixServices *>(
QGuiApplicationPrivate::platformIntegration()->services());
if (parent && unixServices)
message << unixServices->portalWindowIdentifier(parent);
else
message << QString();
message << d->title << options;
QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);