Fallback to another file dialog implementation when XDP is inaccessible

Fixes: QTBUG-98988
Change-Id: Idca1ab4cae0e9eabebc599f3c8efa136a7973918
Reviewed-by: Jan Grulich <jgrulich@redhat.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit fb981a0954119ed0dfa4a402fdef78e7257ffc96)
This commit is contained in:
Ilya Fedin 2022-06-01 15:40:56 +04:00
parent 96bf7b01f6
commit c5883aeb88
3 changed files with 53 additions and 33 deletions

View File

@ -69,15 +69,12 @@ const QDBusArgument &operator >>(const QDBusArgument &arg, QXdgDesktopPortalFile
class QXdgDesktopPortalFileDialogPrivate
{
public:
QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog)
QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion)
: nativeFileDialog(nativeFileDialog)
, fileChooserPortalVersion(fileChooserPortalVersion)
{ }
WId winId = 0;
bool directoryMode = false;
bool modal = false;
bool multipleFiles = false;
bool saveFile = false;
QEventLoop loop;
QString acceptLabel;
QString directory;
QString title;
@ -89,11 +86,16 @@ public:
QString selectedNameFilter;
QStringList selectedFiles;
std::unique_ptr<QPlatformFileDialogHelper> nativeFileDialog;
uint fileChooserPortalVersion = 0;
bool failedToOpen = false;
bool directoryMode = false;
bool multipleFiles = false;
bool saveFile = false;
};
QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog)
QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog, uint fileChooserPortalVersion)
: QPlatformFileDialogHelper()
, d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog))
, d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog, fileChooserPortalVersion))
{
Q_D(QXdgDesktopPortalFileDialog);
@ -101,6 +103,9 @@ QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelp
connect(d->nativeFileDialog.get(), SIGNAL(accept()), this, SIGNAL(accept()));
connect(d->nativeFileDialog.get(), SIGNAL(reject()), this, SIGNAL(reject()));
}
d->loop.connect(this, SIGNAL(accept()), SLOT(quit()));
d->loop.connect(this, SIGNAL(reject()), SLOT(quit()));
}
QXdgDesktopPortalFileDialog::~QXdgDesktopPortalFileDialog()
@ -144,7 +149,7 @@ void QXdgDesktopPortalFileDialog::initializeDialog()
setDirectory(options()->initialDirectory());
}
void QXdgDesktopPortalFileDialog::openPortal()
void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
{
Q_D(QXdgDesktopPortalFileDialog);
@ -152,13 +157,13 @@ void QXdgDesktopPortalFileDialog::openPortal()
"/org/freedesktop/portal/desktop"_L1,
"org.freedesktop.portal.FileChooser"_L1,
d->saveFile ? "SaveFile"_L1 : "OpenFile"_L1);
QString parentWindowId = "x11:"_L1 + QString::number(d->winId, 16);
QString parentWindowId = "x11:"_L1 + QString::number(parent ? parent->winId() : 0, 16);
QVariantMap options;
if (!d->acceptLabel.isEmpty())
options.insert("accept_label"_L1, d->acceptLabel);
options.insert("modal"_L1, d->modal);
options.insert("modal"_L1, windowModality != Qt::NonModal);
options.insert("multiple"_L1, d->multipleFiles);
options.insert("directory"_L1, d->directoryMode);
@ -261,10 +266,18 @@ void QXdgDesktopPortalFileDialog::openPortal()
QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) {
connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QDBusObjectPath> reply = *watcher;
if (reply.isError()) {
Q_EMIT reject();
// Any error means the dialog is not shown and we need to fallback
d->failedToOpen = reply.isError();
if (d->failedToOpen) {
if (d->nativeFileDialog) {
d->nativeFileDialog->show(windowFlags, windowModality, parent);
if (d->loop.isRunning())
d->nativeFileDialog->exec();
} else {
Q_EMIT reject();
}
} else {
QDBusConnection::sessionBus().connect(nullptr,
reply.value().path(),
@ -381,10 +394,7 @@ void QXdgDesktopPortalFileDialog::exec()
}
// HACK we have to avoid returning until we emit that the dialog was accepted or rejected
QEventLoop loop;
loop.connect(this, SIGNAL(accept()), SLOT(quit()));
loop.connect(this, SIGNAL(reject()), SLOT(quit()));
loop.exec();
d->loop.exec();
}
void QXdgDesktopPortalFileDialog::hide()
@ -401,13 +411,10 @@ bool QXdgDesktopPortalFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowMo
initializeDialog();
d->modal = windowModality != Qt::NonModal;
d->winId = parent ? parent->winId() : 0;
if (d->nativeFileDialog && useNativeFileDialog())
if (d->nativeFileDialog && useNativeFileDialog(OpenFallback))
return d->nativeFileDialog->show(windowFlags, windowModality, parent);
openPortal();
openPortal(windowFlags, windowModality, parent);
return true;
}
@ -437,13 +444,20 @@ void QXdgDesktopPortalFileDialog::gotResponse(uint response, const QVariantMap &
}
}
bool QXdgDesktopPortalFileDialog::useNativeFileDialog() const
bool QXdgDesktopPortalFileDialog::useNativeFileDialog(QXdgDesktopPortalFileDialog::FallbackType fallbackType) const
{
if (options()->fileMode() == QFileDialogOptions::Directory)
return true;
else if (options()->fileMode() == QFileDialogOptions::DirectoryOnly)
Q_D(const QXdgDesktopPortalFileDialog);
if (d->failedToOpen && fallbackType != OpenFallback)
return true;
if (d->fileChooserPortalVersion < 3) {
if (options()->fileMode() == QFileDialogOptions::Directory)
return true;
else if (options()->fileMode() == QFileDialogOptions::DirectoryOnly)
return true;
}
return false;
}

View File

@ -15,6 +15,11 @@ class QXdgDesktopPortalFileDialog : public QPlatformFileDialogHelper
Q_OBJECT
Q_DECLARE_PRIVATE(QXdgDesktopPortalFileDialog)
public:
enum FallbackType {
GenericFallback,
OpenFallback
};
enum ConditionType : uint {
GlobalPattern = 0,
MimeType = 1
@ -33,7 +38,7 @@ public:
};
typedef QList<Filter> FilterList;
QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog = nullptr);
QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog = nullptr, uint fileChooserPortalVersion = 0);
~QXdgDesktopPortalFileDialog();
bool defaultNameFilterDisables() const override;
@ -56,8 +61,8 @@ private Q_SLOTS:
private:
void initializeDialog();
void openPortal();
bool useNativeFileDialog() const;
void openPortal(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent);
bool useNativeFileDialog(FallbackType fallbackType = GenericFallback) const;
QScopedPointer<QXdgDesktopPortalFileDialogPrivate> d_ptr;
};

View File

@ -166,11 +166,12 @@ QPlatformDialogHelper* QXdgDesktopPortalTheme::createPlatformDialogHelper(Dialog
{
Q_D(const QXdgDesktopPortalTheme);
if (type == FileDialog) {
if (type == FileDialog && d->fileChooserPortalVersion) {
// Older versions of FileChooser portal don't support opening directories, therefore we fallback
// to native file dialog opened inside the sandbox to open a directory.
if (d->fileChooserPortalVersion < 3 && d->baseTheme->usePlatformNativeDialog(type))
return new QXdgDesktopPortalFileDialog(static_cast<QPlatformFileDialogHelper*>(d->baseTheme->createPlatformDialogHelper(type)));
if (d->baseTheme->usePlatformNativeDialog(type))
return new QXdgDesktopPortalFileDialog(static_cast<QPlatformFileDialogHelper*>(d->baseTheme->createPlatformDialogHelper(type)),
d->fileChooserPortalVersion);
return new QXdgDesktopPortalFileDialog;
}