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
Volker Hilsheimer 2020-08-10 16:03:17 +02:00
parent 53da069b32
commit 5382bf4220
2 changed files with 64 additions and 75 deletions

View File

@ -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

View File

@ -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;