Add XDG Desktop Portal based file dialog implementation from Qt

This allows to use portal dialogs more flexibly (e.g. fallback mechanism)
This also allows to have any changes we want for portal dialogs without patchig Qt

No more need to override QT_QPA_PLATFORM to use portal dialogs
This commit is contained in:
Ilya Fedin 2021-02-05 02:06:21 +04:00 committed by John Preston
parent ce1b94eb16
commit 36acf60f7e
6 changed files with 700 additions and 33 deletions

View File

@ -840,6 +840,8 @@ PRIVATE
platform/linux/linux_open_with_dialog.h
platform/linux/linux_wayland_integration.cpp
platform/linux/linux_wayland_integration.h
platform/linux/linux_xdp_file_dialog.cpp
platform/linux/linux_xdp_file_dialog.h
platform/linux/linux_xlib_helper.cpp
platform/linux/linux_xlib_helper.h
platform/linux/file_utilities_linux.cpp
@ -1132,6 +1134,8 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
platform/linux/linux_gsd_media_keys.h
platform/linux/linux_notification_service_watcher.cpp
platform/linux/linux_notification_service_watcher.h
platform/linux/linux_xdp_file_dialog.cpp
platform/linux/linux_xdp_file_dialog.h
platform/linux/notifications_manager_linux.cpp
)

View File

@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/linux_gtk_integration.h"
#include "platform/linux/specific_linux.h"
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include "platform/linux/linux_xdp_file_dialog.h"
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include <QtGui/QDesktopServices>
extern "C" {
@ -77,6 +81,18 @@ bool Get(
if (parent) {
parent = parent->window();
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (XDP::Use(type)) {
return XDP::Get(
parent,
files,
remoteContent,
caption,
filter,
type,
startFile);
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (const auto integration = GtkIntegration::Instance()) {
if (integration->fileDialogSupported()
&& integration->useFileDialog(type)) {

View File

@ -640,24 +640,10 @@ bool Supported() {
}
bool Use(Type type) {
// use gtk file dialog on gtk-based desktop environments
// or if QT_QPA_PLATFORMTHEME=(gtk2|gtk3)
// or if portals are used and operation is to open folder
// and portal doesn't support folder choosing
const auto sandboxedOrCustomPortal = InFlatpak()
|| InSnap()
|| UseXDGDesktopPortal();
const auto neededForPortal = (type == Type::ReadFolder)
&& !CanOpenDirectoryWithPortal();
const auto neededNonForced = DesktopEnvironment::IsGtkBased()
|| (sandboxedOrCustomPortal && neededForPortal);
const auto excludeNonForced = sandboxedOrCustomPortal && !neededForPortal;
return IsGtkIntegrationForced()
|| (neededNonForced && !excludeNonForced);
|| DesktopEnvironment::IsGtkBased()
// use as a fallback for portal dialog
|| UseXDGDesktopPortal();
}
bool Get(

View File

@ -0,0 +1,536 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/linux/linux_xdp_file_dialog.h"
#include "platform/linux/specific_linux.h"
#include "storage/localstorage.h"
#include "base/qt_adapters.h"
#include <QtCore/qeventloop.h>
#include <QtDBus/QtDBus>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingCall>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
#include <QFile>
#include <QMetaType>
#include <QMimeType>
#include <QMimeDatabase>
#include <QRandomGenerator>
#include <QWindow>
namespace Platform {
namespace FileDialog {
namespace XDP {
namespace {
const char *filterRegExp =
"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
QStringList makeFilterList(const QString &filter) {
QString f(filter);
if (f.isEmpty())
return QStringList();
QString sep(QLatin1String(";;"));
int i = f.indexOf(sep, 0);
if (i == -1) {
if (f.indexOf(QLatin1Char('\n'), 0) != -1) {
sep = QLatin1Char('\n');
i = f.indexOf(sep, 0);
}
}
return f.split(sep);
}
} // namespace
bool Use(Type type) {
return UseXDGDesktopPortal()
&& (type != Type::ReadFolder || CanOpenDirectoryWithPortal());
}
bool Get(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
Type type,
QString startFile) {
XDPFileDialog dialog(parent, caption, QString(), filter);
dialog.setModal(true);
if (type == Type::ReadFile || type == Type::ReadFiles) {
dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
dialog.setAcceptMode(QFileDialog::AcceptOpen);
} else if (type == Type::ReadFolder) {
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setFileMode(QFileDialog::Directory);
dialog.setOption(QFileDialog::ShowDirsOnly);
} else {
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setAcceptMode(QFileDialog::AcceptSave);
}
if (startFile.isEmpty() || startFile.at(0) != '/') {
startFile = cDialogLastPath() + '/' + startFile;
}
dialog.selectFile(startFile);
int res = dialog.exec();
QString path = dialog.directory().path();
if (path != cDialogLastPath()) {
cSetDialogLastPath(path);
Local::writeSettings();
}
if (res == QDialog::Accepted) {
QStringList selectedFilesStrings;
ranges::transform(
dialog.selectedFiles(),
ranges::back_inserter(selectedFilesStrings),
[](const QUrl &url) { return url.path(); });
if (type == Type::ReadFiles) {
files = selectedFilesStrings;
} else {
files = selectedFilesStrings.mid(0, 1);
}
return true;
}
files = QStringList();
remoteContent = QByteArray();
return false;
}
QDBusArgument &operator <<(QDBusArgument &arg, const XDPFileDialog::FilterCondition &filterCondition) {
arg.beginStructure();
arg << filterCondition.type << filterCondition.pattern;
arg.endStructure();
return arg;
}
const QDBusArgument &operator >>(const QDBusArgument &arg, XDPFileDialog::FilterCondition &filterCondition) {
uint type;
QString filterPattern;
arg.beginStructure();
arg >> type >> filterPattern;
filterCondition.type = (XDPFileDialog::ConditionType)type;
filterCondition.pattern = filterPattern;
arg.endStructure();
return arg;
}
QDBusArgument &operator <<(QDBusArgument &arg, const XDPFileDialog::Filter filter) {
arg.beginStructure();
arg << filter.name << filter.filterConditions;
arg.endStructure();
return arg;
}
const QDBusArgument &operator >>(const QDBusArgument &arg, XDPFileDialog::Filter &filter) {
QString name;
XDPFileDialog::FilterConditionList filterConditions;
arg.beginStructure();
arg >> name >> filterConditions;
filter.name = name;
filter.filterConditions = filterConditions;
arg.endStructure();
return arg;
}
class XDPFileDialogPrivate {
public:
XDPFileDialogPrivate() {
}
WId winId = 0;
bool directoryMode = false;
bool modal = false;
bool multipleFiles = false;
bool saveFile = false;
QString acceptLabel;
QString directory;
QString title;
QStringList nameFilters;
QStringList mimeTypesFilters;
// maps user-visible name for portal to full name filter
QMap<QString, QString> userVisibleToNameFilter;
QString selectedMimeTypeFilter;
QString selectedNameFilter;
QStringList selectedFiles;
};
XDPFileDialog::XDPFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter)
: QDialog(parent)
, d_ptr(new XDPFileDialogPrivate())
, _windowTitle(caption)
, _initialDirectory(directory) {
Q_D(XDPFileDialog);
auto filters = makeFilterList(filter);
const int numFilters = filters.count();
_nameFilters.reserve(numFilters);
for (int i = 0; i < numFilters; ++i) {
_nameFilters << filters[i].simplified();
}
accepted(
) | rpl::start_with_next([=] {
Q_EMIT accept();
}, _lifetime);
rejected(
) | rpl::start_with_next([=] {
Q_EMIT reject();
}, _lifetime);
}
XDPFileDialog::~XDPFileDialog() {
}
void XDPFileDialog::initializeDialog() {
Q_D(XDPFileDialog);
if (_fileMode == QFileDialog::ExistingFiles)
d->multipleFiles = true;
if (_fileMode == QFileDialog::Directory || _options.testFlag(QFileDialog::ShowDirsOnly))
d->directoryMode = true;
#if 0 // it is commented in GtkFileDialog for some reason, do the same
if (options()->isLabelExplicitlySet(QFileDialog::Accept))
d->acceptLabel = options()->labelText(QFileDialog::Accept);
#endif
if (!_windowTitle.isEmpty())
d->title = _windowTitle;
if (_acceptMode == QFileDialog::AcceptSave)
d->saveFile = true;
if (!_nameFilters.isEmpty())
d->nameFilters = _nameFilters;
#if 0 // what is the right way to implement this?
if (!options()->mimeTypeFilters().isEmpty())
d->mimeTypesFilters = options()->mimeTypeFilters();
if (!options()->initiallySelectedMimeTypeFilter().isEmpty())
d->selectedMimeTypeFilter = options()->initiallySelectedMimeTypeFilter();
#endif
const auto initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front();
if (!initialNameFilter.isEmpty())
d->selectedNameFilter = initialNameFilter;
setDirectory(_initialDirectory);
}
void XDPFileDialog::openPortal() {
Q_D(XDPFileDialog);
QDBusMessage message = QDBusMessage::createMethodCall(
QLatin1String("org.freedesktop.portal.Desktop"),
QLatin1String("/org/freedesktop/portal/desktop"),
QLatin1String("org.freedesktop.portal.FileChooser"),
d->saveFile ? QLatin1String("SaveFile") : QLatin1String("OpenFile"));
QString parentWindowId = QLatin1String("x11:") + QString::number(d->winId, 16);
QVariantMap options;
if (!d->acceptLabel.isEmpty())
options.insert(QLatin1String("accept_label"), d->acceptLabel);
options.insert(QLatin1String("modal"), d->modal);
options.insert(QLatin1String("multiple"), d->multipleFiles);
options.insert(QLatin1String("directory"), d->directoryMode);
if (d->saveFile) {
if (!d->directory.isEmpty())
options.insert(QLatin1String("current_folder"), QFile::encodeName(d->directory).append('\0'));
if (!d->selectedFiles.isEmpty())
options.insert(QLatin1String("current_file"), QFile::encodeName(d->selectedFiles.first()).append('\0'));
}
// Insert filters
qDBusRegisterMetaType<FilterCondition>();
qDBusRegisterMetaType<FilterConditionList>();
qDBusRegisterMetaType<Filter>();
qDBusRegisterMetaType<FilterList>();
FilterList filterList;
auto selectedFilterIndex = filterList.size() - 1;
d->userVisibleToNameFilter.clear();
if (!d->mimeTypesFilters.isEmpty()) {
for (const QString &mimeTypefilter : d->mimeTypesFilters) {
QMimeDatabase mimeDatabase;
QMimeType mimeType = mimeDatabase.mimeTypeForName(mimeTypefilter);
// Creates e.g. (1, "image/png")
FilterCondition filterCondition;
filterCondition.type = MimeType;
filterCondition.pattern = mimeTypefilter;
// Creates e.g. [((1, "image/png"))]
FilterConditionList filterConditions;
filterConditions << filterCondition;
// Creates e.g. [("Images", [((1, "image/png"))])]
Filter filter;
filter.name = mimeType.comment();
filter.filterConditions = filterConditions;
filterList << filter;
if (!d->selectedMimeTypeFilter.isEmpty() && d->selectedMimeTypeFilter == mimeTypefilter)
selectedFilterIndex = filterList.size() - 1;
}
} else if (!d->nameFilters.isEmpty()) {
for (const QString &nameFilter : d->nameFilters) {
// Do parsing:
// Supported format is ("Images (*.png *.jpg)")
QRegularExpression regexp(QString::fromLatin1(filterRegExp));
QRegularExpressionMatch match = regexp.match(nameFilter);
if (match.hasMatch()) {
QString userVisibleName = match.captured(1);
QStringList filterStrings = match.captured(2).split(QLatin1Char(' '), base::QStringSkipEmptyParts);
if (filterStrings.isEmpty()) {
LOG(("XDP File Dialog Error: Filter %1 is empty and will be ignored.").arg(userVisibleName));
continue;
}
FilterConditionList filterConditions;
for (const QString &filterString : filterStrings) {
FilterCondition filterCondition;
filterCondition.type = GlobalPattern;
filterCondition.pattern = filterString;
filterConditions << filterCondition;
}
Filter filter;
filter.name = userVisibleName;
filter.filterConditions = filterConditions;
filterList << filter;
d->userVisibleToNameFilter.insert(userVisibleName, nameFilter);
if (!d->selectedNameFilter.isEmpty() && d->selectedNameFilter == nameFilter)
selectedFilterIndex = filterList.size() - 1;
}
}
}
if (!filterList.isEmpty())
options.insert(QLatin1String("filters"), QVariant::fromValue(filterList));
if (selectedFilterIndex != -1)
options.insert(QLatin1String("current_filter"), QVariant::fromValue(filterList[selectedFilterIndex]));
options.insert(QLatin1String("handle_token"), QStringLiteral("qt%1").arg(QRandomGenerator::global()->generate()));
// TODO choices a(ssa(ss)s)
// List of serialized combo boxes to add to the file chooser.
message << parentWindowId << d->title << options;
QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QDBusObjectPath> reply = *watcher;
if (reply.isError()) {
_reject.fire({});
} else {
QDBusConnection::sessionBus().connect(
nullptr,
reply.value().path(),
QLatin1String("org.freedesktop.portal.Request"),
QLatin1String("Response"),
this,
SLOT(gotResponse(uint,QVariantMap)));
}
});
}
bool XDPFileDialog::defaultNameFilterDisables() const {
return false;
}
void XDPFileDialog::setDirectory(const QUrl &directory) {
Q_D(XDPFileDialog);
d->directory = directory.path();
}
QUrl XDPFileDialog::directory() const {
Q_D(const XDPFileDialog);
return d->directory;
}
void XDPFileDialog::selectFile(const QUrl &filename) {
Q_D(XDPFileDialog);
d->selectedFiles << filename.path();
}
QList<QUrl> XDPFileDialog::selectedFiles() const {
Q_D(const XDPFileDialog);
QList<QUrl> files;
for (const QString &file : d->selectedFiles) {
files << QUrl(file);
}
return files;
}
void XDPFileDialog::setFilter() {
Q_D(XDPFileDialog);
}
void XDPFileDialog::selectMimeTypeFilter(const QString &filter) {
Q_D(XDPFileDialog);
}
QString XDPFileDialog::selectedMimeTypeFilter() const {
Q_D(const XDPFileDialog);
return d->selectedMimeTypeFilter;
}
void XDPFileDialog::selectNameFilter(const QString &filter) {
Q_D(XDPFileDialog);
}
QString XDPFileDialog::selectedNameFilter() const {
Q_D(const XDPFileDialog);
return d->selectedNameFilter;
}
int XDPFileDialog::exec() {
Q_D(XDPFileDialog);
bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_DeleteOnClose, false);
bool wasShowModal = testAttribute(Qt::WA_ShowModal);
setAttribute(Qt::WA_ShowModal, true);
setResult(0);
show();
QPointer<QDialog> guard = this;
// HACK we have to avoid returning until we emit that the dialog was accepted or rejected
QEventLoop loop;
rpl::lifetime lifetime;
accepted(
) | rpl::start_with_next([&] {
loop.quit();
}, lifetime);
rejected(
) | rpl::start_with_next([&] {
loop.quit();
}, lifetime);
loop.exec();
if (guard.isNull())
return QDialog::Rejected;
setAttribute(Qt::WA_ShowModal, wasShowModal);
return result();
}
void XDPFileDialog::setVisible(bool visible) {
if (visible) {
if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) {
return;
}
} else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) {
return;
}
if (visible) {
showHelper(windowFlags(), windowModality(), parentWidget() ? parentWidget()->windowHandle() : nullptr);
} else {
hideHelper();
}
// Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
// updates the state correctly, but skips showing the non-native version:
setAttribute(Qt::WA_DontShowOnScreen);
QDialog::setVisible(visible);
}
void XDPFileDialog::hideHelper() {
Q_D(XDPFileDialog);
}
void XDPFileDialog::showHelper(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) {
Q_D(XDPFileDialog);
initializeDialog();
d->modal = windowModality != Qt::NonModal;
d->winId = parent ? parent->winId() : 0;
openPortal();
}
void XDPFileDialog::gotResponse(uint response, const QVariantMap &results) {
Q_D(XDPFileDialog);
if (!response) {
if (results.contains(QLatin1String("uris")))
d->selectedFiles = results.value(QLatin1String("uris")).toStringList();
if (results.contains(QLatin1String("current_filter"))) {
const Filter selectedFilter = qdbus_cast<Filter>(results.value(QStringLiteral("current_filter")));
if (!selectedFilter.filterConditions.empty() && selectedFilter.filterConditions[0].type == MimeType) {
// s.a. XDPFileDialog::openPortal which basically does the inverse
d->selectedMimeTypeFilter = selectedFilter.filterConditions[0].pattern;
d->selectedNameFilter.clear();
} else {
d->selectedNameFilter = d->userVisibleToNameFilter.value(selectedFilter.name);
d->selectedMimeTypeFilter.clear();
}
}
_accept.fire({});
} else {
_reject.fire({});
}
}
rpl::producer<> XDPFileDialog::accepted() {
return _accept.events();
}
rpl::producer<> XDPFileDialog::rejected() {
return _reject.events();
}
} // namespace XDP
} // namespace FileDialog
} // namespace Platform

View File

@ -0,0 +1,136 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "core/file_utilities.h"
#include <QFileDialog>
#include <QVector>
namespace Platform {
namespace FileDialog {
namespace XDP {
class XDPFileDialogPrivate;
using Type = ::FileDialog::internal::Type;
bool Use(Type type = Type::ReadFile);
bool Get(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
const QString &caption,
const QString &filter,
Type type,
QString startFile);
// This is a patched copy of file dialog from qxdgdesktopportal theme plugin.
// It allows using XDP file dialog flexibly,
// without relying on QT_QPA_PLATFORMTHEME variable.
//
// XDP file dialog is a dialog obtained via a DBus service
// provided by the current desktop environment.
class XDPFileDialog : public QDialog {
Q_OBJECT
Q_DECLARE_PRIVATE(XDPFileDialog)
public:
enum ConditionType : uint {
GlobalPattern = 0,
MimeType = 1
};
// Filters a(sa(us))
// Example: [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])]
struct FilterCondition {
ConditionType type;
QString pattern; // E.g. '*ico' or 'image/png'
};
typedef QVector<FilterCondition> FilterConditionList;
struct Filter {
QString name; // E.g. 'Images' or 'Text
FilterConditionList filterConditions;; // E.g. [(0, '*.ico'), (1, 'image/png')] or [(0, '*.txt')]
};
typedef QVector<Filter> FilterList;
XDPFileDialog(
QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &directory = QString(),
const QString &filter = QString());
~XDPFileDialog();
void setVisible(bool visible) override;
void setWindowTitle(const QString &windowTitle) {
_windowTitle = windowTitle;
}
void setAcceptMode(QFileDialog::AcceptMode acceptMode) {
_acceptMode = acceptMode;
}
void setFileMode(QFileDialog::FileMode fileMode) {
_fileMode = fileMode;
}
void setOption(QFileDialog::Option option, bool on = true) {
if (on) {
_options |= option;
} else {
_options &= ~option;
}
}
bool defaultNameFilterDisables() const;
QUrl directory() const;
void setDirectory(const QUrl &directory);
void selectFile(const QUrl &filename);
QList<QUrl> selectedFiles() const;
void setFilter();
void selectNameFilter(const QString &filter);
QString selectedNameFilter() const;
void selectMimeTypeFilter(const QString &filter);
QString selectedMimeTypeFilter() const;
int exec() override;
private Q_SLOTS:
void gotResponse(uint response, const QVariantMap &results);
private:
void initializeDialog();
void openPortal();
void showHelper(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent);
void hideHelper();
rpl::producer<> accepted();
rpl::producer<> rejected();
QScopedPointer<XDPFileDialogPrivate> d_ptr;
// Options
QFileDialog::Options _options;
QString _windowTitle = "Choose file";
QString _initialDirectory;
QStringList _initialFiles;
QStringList _nameFilters;
QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen;
QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile;
rpl::event_stream<> _accept;
rpl::event_stream<> _reject;
rpl::lifetime _lifetime;
};
} // namespace XDP
} // namespace FileDialog
} // namespace Platform
Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::FilterCondition);
Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::FilterConditionList);
Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::Filter);
Q_DECLARE_METATYPE(Platform::FileDialog::XDP::XDPFileDialog::FilterList);

View File

@ -29,8 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QDesktopWidget>
#include <QtCore/QStandardPaths>
#include <QtCore/QProcess>
#include <QtCore/QVersionNumber>
#include <QtCore/QLibraryInfo>
#include <QtGui/QWindow>
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@ -596,18 +594,16 @@ bool AreQtPluginsBundled() {
bool UseXDGDesktopPortal() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
static const auto Result = [&] {
const auto onlyIn = AreQtPluginsBundled()
// it is handled by Qt for flatpak and snap
&& !InFlatpak()
&& !InSnap();
if (InFlatpak() || InSnap()) {
return true;
}
const auto envVar = qEnvironmentVariableIsSet("TDESKTOP_USE_PORTAL");
const auto portalPresent = IsXDGDesktopPortalPresent();
const auto neededForKde = DesktopEnvironment::IsKDE()
&& IsXDGDesktopPortalKDEPresent();
return onlyIn
&& portalPresent
return portalPresent
&& (neededForKde || envVar);
}();
@ -620,12 +616,7 @@ bool UseXDGDesktopPortal() {
bool CanOpenDirectoryWithPortal() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
static const auto Result = [&] {
#ifdef DESKTOP_APP_QT_PATCHED
return FileChooserPortalVersion() >= 3;
#else // DESKTOP_APP_QT_PATCHED
return QLibraryInfo::version() >= QVersionNumber(5, 15, 0)
&& FileChooserPortalVersion() >= 3;
#endif // !DESKTOP_APP_QT_PATCHED
}();
return Result;
@ -1037,14 +1028,12 @@ void start() {
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
// this can give us a chance to use
// a proper file dialog for current session
// Tell the user when XDP file dialog is used
DEBUG_LOG(("Checking for XDG Desktop Portal..."));
if (IsXDGDesktopPortalPresent()) {
DEBUG_LOG(("XDG Desktop Portal is present!"));
if (UseXDGDesktopPortal()) {
LOG(("Using XDG Desktop Portal."));
qputenv("QT_QPA_PLATFORMTHEME", "xdgdesktopportal");
} else {
DEBUG_LOG(("Not using XDG Desktop Portal."));
}