From 4730221dbce520e6884cd07346ba89c80ec2c466 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Sun, 21 Aug 2016 12:15:06 +0200 Subject: Fix FileDialog::selectedNameFilter review findings FileDialog::nameFilters contains an array of name filter strings that combine the name and extensions. For example: - "Text files (*.txt)" - "HTML files (*.html *.htm)" When dealing with multiple name filters, it is quite clumsy to use a string type of 'selectedNameFilter' which is one of the above full file name filter string values. Make it possible to read/write the index of the selected name filter, and provide the filter's name and extensions separately. Change-Id: Ie416cc4ab3dcde93c10769b6f7ac44915307f194 Reviewed-by: Mitch Curtis --- src/imports/platform/plugins.qmltypes | 26 ++- src/imports/platform/qquickplatformdialog.cpp | 6 + src/imports/platform/qquickplatformdialog_p.h | 1 + src/imports/platform/qquickplatformfiledialog.cpp | 183 ++++++++++++++++++++-- src/imports/platform/qquickplatformfiledialog_p.h | 46 +++++- src/imports/platform/qtlabsplatformplugin.cpp | 1 + 6 files changed, 242 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/imports/platform/plugins.qmltypes b/src/imports/platform/plugins.qmltypes index 2bee7276..01d8eb94 100644 --- a/src/imports/platform/plugins.qmltypes +++ b/src/imports/platform/plugins.qmltypes @@ -130,11 +130,35 @@ Module { Property { name: "folder"; type: "QUrl" } Property { name: "options"; type: "QFileDialogOptions::FileDialogOptions" } Property { name: "nameFilters"; type: "QStringList" } - Property { name: "selectedNameFilter"; type: "string" } + Property { + name: "selectedNameFilter" + type: "QQuickPlatformFileNameFilter" + isReadonly: true + isPointer: true + } Property { name: "defaultSuffix"; type: "string" } Property { name: "acceptLabel"; type: "string" } Property { name: "rejectLabel"; type: "string" } } + Component { + name: "QQuickPlatformFileNameFilter" + prototype: "QObject" + Property { name: "index"; type: "int" } + Property { name: "name"; type: "string"; isReadonly: true } + Property { name: "extensions"; type: "QStringList"; isReadonly: true } + Signal { + name: "indexChanged" + Parameter { name: "index"; type: "int" } + } + Signal { + name: "nameChanged" + Parameter { name: "name"; type: "string" } + } + Signal { + name: "extensionsChanged" + Parameter { name: "extensions"; type: "QStringList" } + } + } Component { name: "QQuickPlatformFolderDialog" defaultProperty: "data" diff --git a/src/imports/platform/qquickplatformdialog.cpp b/src/imports/platform/qquickplatformdialog.cpp index dee4335d..95bc6426 100644 --- a/src/imports/platform/qquickplatformdialog.cpp +++ b/src/imports/platform/qquickplatformdialog.cpp @@ -283,6 +283,7 @@ void QQuickPlatformDialog::close() if (!m_handle || !m_visible) return; + onHide(m_handle); m_handle->hide(); m_visible = false; emit visibleChanged(); @@ -385,6 +386,11 @@ void QQuickPlatformDialog::onShow(QPlatformDialogHelper *dialog) Q_UNUSED(dialog); } +void QQuickPlatformDialog::onHide(QPlatformDialogHelper *dialog) +{ + Q_UNUSED(dialog); +} + QWindow *QQuickPlatformDialog::findParentWindow() const { QObject *obj = parent(); diff --git a/src/imports/platform/qquickplatformdialog_p.h b/src/imports/platform/qquickplatformdialog_p.h index 2a5caee7..a9797c1d 100644 --- a/src/imports/platform/qquickplatformdialog_p.h +++ b/src/imports/platform/qquickplatformdialog_p.h @@ -129,6 +129,7 @@ protected: virtual bool useNativeDialog() const; virtual void onCreate(QPlatformDialogHelper *dialog); virtual void onShow(QPlatformDialogHelper *dialog); + virtual void onHide(QPlatformDialogHelper *dialog); QWindow *findParentWindow() const; diff --git a/src/imports/platform/qquickplatformfiledialog.cpp b/src/imports/platform/qquickplatformfiledialog.cpp index 9da774d4..6a0f4e13 100644 --- a/src/imports/platform/qquickplatformfiledialog.cpp +++ b/src/imports/platform/qquickplatformfiledialog.cpp @@ -36,6 +36,8 @@ #include "qquickplatformfiledialog_p.h" +#include + QT_BEGIN_NAMESPACE /*! @@ -97,7 +99,8 @@ QT_BEGIN_NAMESPACE QQuickPlatformFileDialog::QQuickPlatformFileDialog(QObject *parent) : QQuickPlatformDialog(QPlatformTheme::FileDialog, parent), m_fileMode(OpenFile), - m_options(QFileDialogOptions::create()) + m_options(QFileDialogOptions::create()), + m_selectedNameFilter(nullptr) { m_options->setFileMode(QFileDialogOptions::ExistingFile); m_options->setAcceptMode(QFileDialogOptions::AcceptOpen); @@ -335,6 +338,12 @@ void QQuickPlatformFileDialog::setNameFilters(const QStringList &filters) return; m_options->setNameFilters(filters); + if (m_selectedNameFilter) { + int index = m_selectedNameFilter->index(); + if (index < 0 || index >= filters.count()) + index = 0; + m_selectedNameFilter->update(filters.value(index)); + } emit nameFiltersChanged(); } @@ -344,24 +353,57 @@ void QQuickPlatformFileDialog::resetNameFilters() } /*! - \qmlproperty string Qt.labs.platform::FileDialog::selectedNameFilter + \qmlpropertygroup Qt.labs.platform::FileDialog::selectedNameFilter + \qmlproperty int Qt.labs.platform::FileDialog::selectedNameFilter.index + \qmlproperty string Qt.labs.platform::FileDialog::selectedNameFilter.name + \qmlproperty list Qt.labs.platform::FileDialog::selectedNameFilter.extensions + + These properties hold the currently selected name filter. + + \table + \header + \li Name + \li Description + \row + \li \b index : int + \li This property determines which \l {nameFilters}{name filter} is selected. + The specified filter is selected when the dialog is opened. The value is + updated when the user selects another filter. + \row + \li [read-only] \b name : string + \li This property holds the name of the selected filter. In the + example below, the name of the first filter is \c {"Text files"} + and the second is \c {"HTML files"}. + \row + \li [read-only] \b extensions : list + \li This property holds the list of extensions of the selected filter. + In the example below, the list of extensions of the first filter is + \c {["txt"]} and the second is \c {["html", "htm"]}. + \endtable - This property holds the currently selected name filter. + \code + FileDialog { + id: fileDialog + selectedNameFilter.index: 1 + nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)"] + } + + MyDocument { + id: document + fileType: fileDialog.selectedNameFilter.extensions[0] + } + \endcode \sa nameFilters */ -QString QQuickPlatformFileDialog::selectedNameFilter() const +QQuickPlatformFileNameFilter *QQuickPlatformFileDialog::selectedNameFilter() const { - if (QPlatformFileDialogHelper *fileDialog = qobject_cast(handle())) - return fileDialog->selectedNameFilter(); - return m_options->initiallySelectedNameFilter(); -} - -void QQuickPlatformFileDialog::setSelectedNameFilter(const QString &filter) -{ - if (QPlatformFileDialogHelper *fileDialog = qobject_cast(handle())) - fileDialog->selectNameFilter(filter); - m_options->setInitiallySelectedNameFilter(filter); + if (!m_selectedNameFilter) { + QQuickPlatformFileDialog *that = const_cast(this); + m_selectedNameFilter = new QQuickPlatformFileNameFilter(that); + m_selectedNameFilter->setOptions(m_options); + } + return m_selectedNameFilter; } /*! @@ -468,7 +510,6 @@ void QQuickPlatformFileDialog::onCreate(QPlatformDialogHelper *dialog) connect(fileDialog, &QPlatformFileDialogHelper::currentChanged, this, &QQuickPlatformFileDialog::currentFileChanged); connect(fileDialog, &QPlatformFileDialogHelper::currentChanged, this, &QQuickPlatformFileDialog::currentFilesChanged); connect(fileDialog, &QPlatformFileDialogHelper::directoryEntered, this, &QQuickPlatformFileDialog::folderChanged); - connect(fileDialog, &QPlatformFileDialogHelper::filterSelected, this, &QQuickPlatformFileDialog::selectedNameFilterChanged); fileDialog->setOptions(m_options); } } @@ -476,8 +517,24 @@ void QQuickPlatformFileDialog::onCreate(QPlatformDialogHelper *dialog) void QQuickPlatformFileDialog::onShow(QPlatformDialogHelper *dialog) { m_options->setWindowTitle(title()); - if (QPlatformFileDialogHelper *fileDialog = qobject_cast(dialog)) + if (QPlatformFileDialogHelper *fileDialog = qobject_cast(dialog)) { fileDialog->setOptions(m_options); + if (m_selectedNameFilter) { + const int index = m_selectedNameFilter->index(); + const QString filter = m_options->nameFilters().value(index); + m_options->setInitiallySelectedNameFilter(filter); + fileDialog->selectNameFilter(filter); + connect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickPlatformFileNameFilter::update); + } + } +} + +void QQuickPlatformFileDialog::onHide(QPlatformDialogHelper *dialog) +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast(dialog)) { + if (m_selectedNameFilter) + disconnect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickPlatformFileNameFilter::update); + } } void QQuickPlatformFileDialog::accept() @@ -506,4 +563,98 @@ QList QQuickPlatformFileDialog::addDefaultSuffixes(const QList &file return urls; } +QQuickPlatformFileNameFilter::QQuickPlatformFileNameFilter(QObject *parent) + : QObject(parent), m_index(-1) +{ +} + +int QQuickPlatformFileNameFilter::index() const +{ + return m_index; +} + +void QQuickPlatformFileNameFilter::setIndex(int index) +{ + if (m_index == index) + return; + + m_index = index; + emit indexChanged(index); +} + +QString QQuickPlatformFileNameFilter::name() const +{ + return m_name; +} + +QStringList QQuickPlatformFileNameFilter::extensions() const +{ + return m_extensions; +} + +QSharedPointer QQuickPlatformFileNameFilter::options() const +{ + return m_options; +} + +void QQuickPlatformFileNameFilter::setOptions(const QSharedPointer &options) +{ + m_options = options; +} + +static QString extractName(const QString &filter) +{ + return filter.left(filter.indexOf(QLatin1Char('(')) - 1); +} + +static QString extractExtension(const QString &filter) +{ + return filter.mid(filter.indexOf(QLatin1Char('.')) + 1); +} + +static QStringList extractExtensions(const QString &filter) +{ + QStringList extensions; + const int from = filter.indexOf(QLatin1Char('(')); + const int to = filter.lastIndexOf(QLatin1Char(')')) - 1; + if (from >= 0 && from < to) { + const QStringRef ref = filter.midRef(from + 1, to - from); + const QVector exts = ref.split(QLatin1Char(' '), QString::SkipEmptyParts); + for (const QStringRef &ref : exts) + extensions += extractExtension(ref.toString()); + } + + return extensions; +} + +void QQuickPlatformFileNameFilter::update(const QString &filter) +{ + const QStringList filters = nameFilters(); + + const int oldIndex = m_index; + const QString oldName = m_name; + const QStringList oldExtensions = m_extensions; + + m_index = filters.indexOf(filter); + m_name = extractName(filter); + m_extensions = extractExtensions(filter); + + if (oldIndex != m_index) + emit indexChanged(m_index); + if (oldName != m_name) + emit nameChanged(m_name); + if (oldExtensions != m_extensions) + emit extensionsChanged(m_extensions); +} + +QStringList QQuickPlatformFileNameFilter::nameFilters() const +{ + return m_options ? m_options->nameFilters() : QStringList(); +} + +QString QQuickPlatformFileNameFilter::nameFilter(int index) const +{ + return m_options ? m_options->nameFilters().value(index) : QString(); +} + QT_END_NAMESPACE diff --git a/src/imports/platform/qquickplatformfiledialog_p.h b/src/imports/platform/qquickplatformfiledialog_p.h index a4c61c54..51f88d46 100644 --- a/src/imports/platform/qquickplatformfiledialog_p.h +++ b/src/imports/platform/qquickplatformfiledialog_p.h @@ -54,6 +54,8 @@ QT_BEGIN_NAMESPACE +class QQuickPlatformFileNameFilter; + class QQuickPlatformFileDialog : public QQuickPlatformDialog { Q_OBJECT @@ -65,7 +67,7 @@ class QQuickPlatformFileDialog : public QQuickPlatformDialog Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged FINAL) Q_PROPERTY(QFileDialogOptions::FileDialogOptions options READ options WRITE setOptions RESET resetOptions NOTIFY optionsChanged FINAL) Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters RESET resetNameFilters NOTIFY nameFiltersChanged FINAL) - Q_PROPERTY(QString selectedNameFilter READ selectedNameFilter WRITE setSelectedNameFilter NOTIFY selectedNameFilterChanged FINAL) + Q_PROPERTY(QQuickPlatformFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT) Q_PROPERTY(QString defaultSuffix READ defaultSuffix WRITE setDefaultSuffix RESET resetDefaultSuffix NOTIFY defaultSuffixChanged FINAL) Q_PROPERTY(QString acceptLabel READ acceptLabel WRITE setAcceptLabel RESET resetAcceptLabel NOTIFY acceptLabelChanged FINAL) Q_PROPERTY(QString rejectLabel READ rejectLabel WRITE setRejectLabel RESET resetRejectLabel NOTIFY rejectLabelChanged FINAL) @@ -107,8 +109,7 @@ public: void setNameFilters(const QStringList &filters); void resetNameFilters(); - QString selectedNameFilter() const; - void setSelectedNameFilter(const QString &filter); + QQuickPlatformFileNameFilter *selectedNameFilter() const; QString defaultSuffix() const; void setDefaultSuffix(const QString &suffix); @@ -131,7 +132,6 @@ Q_SIGNALS: void folderChanged(); void optionsChanged(); void nameFiltersChanged(); - void selectedNameFilterChanged(); void defaultSuffixChanged(); void acceptLabelChanged(); void rejectLabelChanged(); @@ -140,6 +140,7 @@ protected: bool useNativeDialog() const override; void onCreate(QPlatformDialogHelper *dialog) override; void onShow(QPlatformDialogHelper *dialog) override; + void onHide(QPlatformDialogHelper *dialog) override; void accept() override; private: @@ -149,6 +150,43 @@ private: FileMode m_fileMode; QList m_files; QSharedPointer m_options; + mutable QQuickPlatformFileNameFilter *m_selectedNameFilter; +}; + +class QQuickPlatformFileNameFilter : public QObject +{ + Q_OBJECT + Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged FINAL) + Q_PROPERTY(QString name READ name NOTIFY nameChanged FINAL) + Q_PROPERTY(QStringList extensions READ extensions NOTIFY extensionsChanged FINAL) + +public: + explicit QQuickPlatformFileNameFilter(QObject *parent = nullptr); + + int index() const; + void setIndex(int index); + + QString name() const; + QStringList extensions() const; + + QSharedPointer options() const; + void setOptions(const QSharedPointer &options); + + void update(const QString &filter); + +Q_SIGNALS: + void indexChanged(int index); + void nameChanged(const QString &name); + void extensionsChanged(const QStringList &extensions); + +private: + QStringList nameFilters() const; + QString nameFilter(int index) const; + + int m_index; + QString m_name; + QStringList m_extensions; + QSharedPointer m_options; }; QT_END_NAMESPACE diff --git a/src/imports/platform/qtlabsplatformplugin.cpp b/src/imports/platform/qtlabsplatformplugin.cpp index 1762e267..f73b04e8 100644 --- a/src/imports/platform/qtlabsplatformplugin.cpp +++ b/src/imports/platform/qtlabsplatformplugin.cpp @@ -91,6 +91,7 @@ void QtLabsPlatformPlugin::registerTypes(const char *uri) qmlRegisterUncreatableType(uri, 1, 0, "Dialog", QQuickPlatformDialog::tr("Dialog is an abstract base class")); qmlRegisterType(uri, 1, 0, "ColorDialog"); qmlRegisterType(uri, 1, 0, "FileDialog"); + qmlRegisterType(); qmlRegisterType(uri, 1, 0, "FolderDialog"); qmlRegisterType(uri, 1, 0, "FontDialog"); qmlRegisterType(uri, 1, 0, "MessageDialog"); -- cgit v1.2.3