QFileDialog: reduce number of times a file is stat'ed
When opening a QFileDialog with an initial directory that lives on a disconnected network drive, repeatedly testing that directory consumes a significant amount of time during which the UI is blocked. To reduce the amount of file accesses, refactor the initialization code to allow sharing of a QFileInfo for the default case of operating on a local and absolute file system. This reduces the amount of stat calls significantly during startup time, and in case of a disconnected network shaves of 10-15 seconds of blocked UI, if Windows has already noticed that the file system is disconnected. Pick-to: 5.15 Fixes: QTBUG-6039 Change-Id: Ie082e447db214033291455bef2087cd05f366806 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>bb10
parent
53da069b32
commit
5382bf4220
|
|
@ -1,6 +1,6 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtWidgets module of the Qt Toolkit.
|
||||
|
|
@ -360,7 +360,8 @@ QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags f)
|
|||
: QDialog(*new QFileDialogPrivate, parent, f)
|
||||
{
|
||||
Q_D(QFileDialog);
|
||||
d->init();
|
||||
QFileDialogArgs args;
|
||||
d->init(args);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -377,7 +378,10 @@ QFileDialog::QFileDialog(QWidget *parent,
|
|||
: QDialog(*new QFileDialogPrivate, parent, { })
|
||||
{
|
||||
Q_D(QFileDialog);
|
||||
d->init(QUrl::fromLocalFile(directory), filter, caption);
|
||||
QFileDialogArgs args(QUrl::fromLocalFile(directory));
|
||||
args.filter = filter;
|
||||
args.caption = caption;
|
||||
d->init(args);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -387,7 +391,7 @@ QFileDialog::QFileDialog(const QFileDialogArgs &args)
|
|||
: QDialog(*new QFileDialogPrivate, args.parent, { })
|
||||
{
|
||||
Q_D(QFileDialog);
|
||||
d->init(args.directory, args.filter, args.caption);
|
||||
d->init(args);
|
||||
setFileMode(args.mode);
|
||||
setOptions(args.options);
|
||||
selectFile(args.selection);
|
||||
|
|
@ -2143,11 +2147,9 @@ QUrl QFileDialog::getOpenFileUrl(QWidget *parent,
|
|||
Options options,
|
||||
const QStringList &supportedSchemes)
|
||||
{
|
||||
QFileDialogArgs args;
|
||||
QFileDialogArgs args(dir);
|
||||
args.parent = parent;
|
||||
args.caption = caption;
|
||||
args.directory = QFileDialogPrivate::workingDirectory(dir);
|
||||
args.selection = QFileDialogPrivate::initialSelection(dir);
|
||||
args.filter = filter;
|
||||
args.mode = ExistingFile;
|
||||
args.options = options;
|
||||
|
|
@ -2263,11 +2265,9 @@ QList<QUrl> QFileDialog::getOpenFileUrls(QWidget *parent,
|
|||
Options options,
|
||||
const QStringList &supportedSchemes)
|
||||
{
|
||||
QFileDialogArgs args;
|
||||
QFileDialogArgs args(dir);
|
||||
args.parent = parent;
|
||||
args.caption = caption;
|
||||
args.directory = QFileDialogPrivate::workingDirectory(dir);
|
||||
args.selection = QFileDialogPrivate::initialSelection(dir);
|
||||
args.filter = filter;
|
||||
args.mode = ExistingFiles;
|
||||
args.options = options;
|
||||
|
|
@ -2509,11 +2509,9 @@ QUrl QFileDialog::getSaveFileUrl(QWidget *parent,
|
|||
Options options,
|
||||
const QStringList &supportedSchemes)
|
||||
{
|
||||
QFileDialogArgs args;
|
||||
QFileDialogArgs args(dir);
|
||||
args.parent = parent;
|
||||
args.caption = caption;
|
||||
args.directory = QFileDialogPrivate::workingDirectory(dir);
|
||||
args.selection = QFileDialogPrivate::initialSelection(dir);
|
||||
args.filter = filter;
|
||||
args.mode = AnyFile;
|
||||
args.options = options;
|
||||
|
|
@ -2619,10 +2617,9 @@ QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent,
|
|||
Options options,
|
||||
const QStringList &supportedSchemes)
|
||||
{
|
||||
QFileDialogArgs args;
|
||||
QFileDialogArgs args(dir);
|
||||
args.parent = parent;
|
||||
args.caption = caption;
|
||||
args.directory = QFileDialogPrivate::workingDirectory(dir);
|
||||
args.mode = Directory;
|
||||
args.options = options;
|
||||
|
||||
|
|
@ -2633,58 +2630,54 @@ QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent,
|
|||
return QUrl();
|
||||
}
|
||||
|
||||
inline static QUrl _qt_get_directory(const QUrl &url)
|
||||
inline static QUrl _qt_get_directory(const QUrl &url, const QFileInfo &local)
|
||||
{
|
||||
if (url.isLocalFile()) {
|
||||
QFileInfo info = QFileInfo(QDir::current(), url.toLocalFile());
|
||||
QFileInfo info = local;
|
||||
if (!local.isAbsolute())
|
||||
info = QFileInfo(QDir::current(), url.toLocalFile());
|
||||
const QFileInfo pathInfo(info.absolutePath());
|
||||
if (!pathInfo.exists() || !pathInfo.isDir())
|
||||
return QUrl();
|
||||
if (info.exists() && info.isDir())
|
||||
return QUrl::fromLocalFile(QDir::cleanPath(info.absoluteFilePath()));
|
||||
info.setFile(info.absolutePath());
|
||||
if (info.exists() && info.isDir())
|
||||
return QUrl::fromLocalFile(info.absoluteFilePath());
|
||||
return QUrl();
|
||||
return QUrl::fromLocalFile(pathInfo.absoluteFilePath());
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Get the initial directory URL
|
||||
|
||||
\sa initialSelection()
|
||||
*/
|
||||
QUrl QFileDialogPrivate::workingDirectory(const QUrl &url)
|
||||
{
|
||||
if (!url.isEmpty()) {
|
||||
QUrl directory = _qt_get_directory(url);
|
||||
if (!directory.isEmpty())
|
||||
return directory;
|
||||
}
|
||||
QUrl directory = _qt_get_directory(*lastVisitedDir());
|
||||
if (!directory.isEmpty())
|
||||
return directory;
|
||||
return QUrl::fromLocalFile(QDir::currentPath());
|
||||
}
|
||||
|
||||
/*
|
||||
Get the initial selection given a path. The initial directory
|
||||
can contain both the initial directory and initial selection
|
||||
/home/user/foo.txt
|
||||
|
||||
\sa workingDirectory()
|
||||
*/
|
||||
QString QFileDialogPrivate::initialSelection(const QUrl &url)
|
||||
Initialize working directory and selection from \a url.
|
||||
*/
|
||||
QFileDialogArgs::QFileDialogArgs(const QUrl &url)
|
||||
{
|
||||
if (url.isEmpty())
|
||||
return QString();
|
||||
if (url.isLocalFile()) {
|
||||
QFileInfo info(url.toLocalFile());
|
||||
if (!info.isDir())
|
||||
return info.fileName();
|
||||
else
|
||||
return QString();
|
||||
// default case, re-use QFileInfo to avoid stat'ing
|
||||
const QFileInfo local(url.toLocalFile());
|
||||
// Get the initial directory URL
|
||||
if (!url.isEmpty())
|
||||
directory = _qt_get_directory(url, local);
|
||||
if (directory.isEmpty()) {
|
||||
const QUrl lastVisited = *lastVisitedDir();
|
||||
if (lastVisited != url)
|
||||
directory = _qt_get_directory(lastVisited, QFileInfo());
|
||||
}
|
||||
if (directory.isEmpty())
|
||||
directory = QUrl::fromLocalFile(QDir::currentPath());
|
||||
|
||||
/*
|
||||
The initial directory can contain both the initial directory
|
||||
and initial selection, e.g. /home/user/foo.txt
|
||||
*/
|
||||
if (selection.isEmpty() && !url.isEmpty()) {
|
||||
if (url.isLocalFile()) {
|
||||
if (!local.isDir())
|
||||
selection = local.fileName();
|
||||
} else {
|
||||
// With remote URLs we can only assume.
|
||||
selection = url.fileName();
|
||||
}
|
||||
}
|
||||
// With remote URLs we can only assume.
|
||||
return url.fileName();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -2923,14 +2916,13 @@ bool QFileDialogPrivate::restoreWidgetState(QStringList &history, int splitterPo
|
|||
|
||||
Create widgets, layout and set default values
|
||||
*/
|
||||
void QFileDialogPrivate::init(const QUrl &directory, const QString &nameFilter,
|
||||
const QString &caption)
|
||||
void QFileDialogPrivate::init(const QFileDialogArgs &args)
|
||||
{
|
||||
Q_Q(QFileDialog);
|
||||
if (!caption.isEmpty()) {
|
||||
if (!args.caption.isEmpty()) {
|
||||
useDefaultCaption = false;
|
||||
setWindowTitle = caption;
|
||||
q->setWindowTitle(caption);
|
||||
setWindowTitle = args.caption;
|
||||
q->setWindowTitle(args.caption);
|
||||
}
|
||||
|
||||
q->setAcceptMode(QFileDialog::AcceptOpen);
|
||||
|
|
@ -2938,17 +2930,17 @@ void QFileDialogPrivate::init(const QUrl &directory, const QString &nameFilter,
|
|||
if (!nativeDialogInUse)
|
||||
createWidgets();
|
||||
q->setFileMode(QFileDialog::AnyFile);
|
||||
if (!nameFilter.isEmpty())
|
||||
q->setNameFilter(nameFilter);
|
||||
if (!args.filter.isEmpty())
|
||||
q->setNameFilter(args.filter);
|
||||
// QTBUG-70798, prevent the default blocking the restore logic.
|
||||
const bool dontStoreDir = !directory.isValid() && !lastVisitedDir()->isValid();
|
||||
q->setDirectoryUrl(workingDirectory(directory));
|
||||
const bool dontStoreDir = !args.directory.isValid() && !lastVisitedDir()->isValid();
|
||||
q->setDirectoryUrl(args.directory);
|
||||
if (dontStoreDir)
|
||||
lastVisitedDir()->clear();
|
||||
if (directory.isLocalFile())
|
||||
q->selectFile(initialSelection(directory));
|
||||
if (args.directory.isLocalFile())
|
||||
q->selectFile(args.selection);
|
||||
else
|
||||
q->selectUrl(directory);
|
||||
q->selectUrl(args.directory);
|
||||
|
||||
#if QT_CONFIG(settings)
|
||||
// Try to restore from the FileDialog settings group; if it fails, fall back
|
||||
|
|
|
|||
|
|
@ -98,15 +98,15 @@ class QPlatformDialogHelper;
|
|||
|
||||
struct QFileDialogArgs
|
||||
{
|
||||
QFileDialogArgs() : parent(nullptr), mode(QFileDialog::AnyFile) {}
|
||||
QFileDialogArgs(const QUrl &url = {});
|
||||
|
||||
QWidget *parent;
|
||||
QWidget *parent = nullptr;
|
||||
QString caption;
|
||||
QUrl directory;
|
||||
QString selection;
|
||||
QString filter;
|
||||
QFileDialog::FileMode mode;
|
||||
QFileDialog::Options options;
|
||||
QFileDialog::FileMode mode = QFileDialog::AnyFile;
|
||||
QFileDialog::Options options = {};
|
||||
};
|
||||
|
||||
#define UrlRole (Qt::UserRole + 1)
|
||||
|
|
@ -133,12 +133,9 @@ public:
|
|||
void createMenuActions();
|
||||
void createWidgets();
|
||||
|
||||
void init(const QUrl &directory = QUrl(), const QString &nameFilter = QString(),
|
||||
const QString &caption = QString());
|
||||
void init(const QFileDialogArgs &args);
|
||||
bool itemViewKeyboardEvent(QKeyEvent *event);
|
||||
QString getEnvironmentVariable(const QString &string);
|
||||
static QUrl workingDirectory(const QUrl &path);
|
||||
static QString initialSelection(const QUrl &path);
|
||||
QStringList typedFiles() const;
|
||||
QList<QUrl> userSelectedFiles() const;
|
||||
QStringList addDefaultSuffixToFiles(const QStringList &filesToFix) const;
|
||||
|
|
|
|||
Loading…
Reference in New Issue