aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/imports/CMakeLists.txt1
-rw-r--r--src/imports/dialogs/CMakeLists.txt29
-rw-r--r--src/imports/dialogs/doc/images/qtquickdialogs-filedialog-gtk.pngbin0 -> 39560 bytes
-rw-r--r--src/imports/dialogs/doc/qtquickdialogs.qdocconf40
-rw-r--r--src/imports/dialogs/doc/src/includes/fallback.qdocinc1
-rw-r--r--src/imports/dialogs/qmldir3
-rw-r--r--src/imports/dialogs/qtquickdialogsplugin.cpp60
-rw-r--r--src/quickdialogs2/CMakeLists.txt3
-rw-r--r--src/quickdialogs2/quickdialogs2/CMakeLists.txt43
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp454
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickabstractdialog_p.h161
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp612
-rw-r--r--src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h165
-rw-r--r--src/quickdialogs2/quickdialogs2/qtquickdialogs2foreign_p.h75
-rw-r--r--src/quickdialogs2/quickdialogs2/qtquickdialogs2global_p.h70
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt134
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.pngbin0 -> 119 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.svg136
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@2x.pngbin0 -> 268 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@3x.pngbin0 -> 360 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@4x.pngbin0 -> 422 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.pngbin0 -> 135 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.svg134
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@2x.pngbin0 -> 160 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@3x.pngbin0 -> 179 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@4x.pngbin0 -> 196 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.pngbin0 -> 238 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.svg117
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@2x.pngbin0 -> 396 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@3x.pngbin0 -> 546 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@4x.pngbin0 -> 698 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.pngbin0 -> 193 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.svg83
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@2x.pngbin0 -> 294 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@3x.pngbin0 -> 395 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@4x.pngbin0 -> 474 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.pngbin0 -> 205 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.svg93
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@2x.pngbin0 -> 306 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@3x.pngbin0 -> 431 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@4x.pngbin0 -> 574 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.pngbin0 -> 165 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.svg74
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@2x.pngbin0 -> 194 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@3x.pngbin0 -> 241 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@4x.pngbin0 -> 256 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.9.pngbin0 -> 113 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.svg358
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.pngbin0 -> 281 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.svg86
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@2x.pngbin0 -> 355 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@3x.pngbin0 -> 453 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@4x.pngbin0 -> 569 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.pngbin0 -> 171 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.svg79
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@2x.pngbin0 -> 212 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@3x.pngbin0 -> 251 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@4x.pngbin0 -> 274 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.pngbin0 -> 138 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.svg72
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@2x.pngbin0 -> 163 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@3x.pngbin0 -> 183 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@4x.pngbin0 -> 204 bytes
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialog.qml196
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialogDelegate.qml88
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FolderBreadcrumbBar.qml107
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialog.qml190
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialogDelegate.qml100
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FolderBreadcrumbBar.qml91
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialog.qml163
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialogDelegate.qml95
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FolderBreadcrumbBar.qml103
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialog.qml165
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialogDelegate.qml93
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FolderBreadcrumbBar.qml101
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialog.qml183
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegate.qml89
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegateLabel.qml98
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qml/FolderBreadcrumbBar.qml102
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp73
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory_p.h67
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate.cpp163
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate_p.h93
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp564
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h168
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h109
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar.cpp784
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p.h126
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p_p.h113
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp226
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog_p.h91
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplforeign_p.h115
-rw-r--r--src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplglobal_p.h70
-rw-r--r--src/quickdialogs2/quickdialogs2utils/CMakeLists.txt23
-rw-r--r--src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter.cpp158
-rw-r--r--src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter_p.h102
-rw-r--r--src/quickdialogs2/quickdialogs2utils/qtquickdialogs2utilsglobal_p.h67
-rw-r--r--src/quicktemplates2/qquickdeferredexecute_p_p.h7
-rw-r--r--src/quicktemplates2/qquickdialog.cpp49
-rw-r--r--src/quicktemplates2/qquickdialog_p.h2
-rw-r--r--src/quicktemplates2/qquickdialog_p_p.h6
-rw-r--r--src/quicktemplates2/qquickdialogbuttonbox.cpp10
-rw-r--r--src/quicktemplates2/qquickdialogbuttonbox_p_p.h4
-rw-r--r--src/quicktemplates2/qquickitemdelegate_p_p.h2
-rw-r--r--src/quicktemplates2/qquickpopup.cpp3
-rw-r--r--src/quicktemplates2/qquickpopup_p_p.h4
-rw-r--r--src/quicktemplates2/qquickpopupitem_p.h2
-rw-r--r--src/quicktemplates2/qquickpopupitem_p_p.h2
-rw-r--r--sync.profile3
-rw-r--r--tests/auto/CMakeLists.txt1
-rw-r--r--tests/auto/dialogs/CMakeLists.txt1
-rw-r--r--tests/auto/dialogs/dialogs.pro3
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/BLACKLIST7
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/CMakeLists.txt40
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml67
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml66
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml68
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/data/bindTitle.qml65
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml66
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/data/fileDialog.qml73
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/qquickfiledialogimpl.pro14
-rw-r--r--tests/auto/dialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp1349
-rw-r--r--tests/auto/shared/dialogtestutil.h144
-rw-r--r--tests/auto/shared/visualtestutil.h14
-rw-r--r--tests/manual/dialogs/CMakeLists.txt31
-rw-r--r--tests/manual/dialogs/FileDialogPage.qml357
-rw-r--r--tests/manual/dialogs/StringListView.qml79
-rw-r--r--tests/manual/dialogs/Theme.qml57
-rw-r--r--tests/manual/dialogs/dialogs.cpp80
-rw-r--r--tests/manual/dialogs/dialogs.pro11
-rw-r--r--tests/manual/dialogs/dialogs.qml127
-rw-r--r--tests/manual/dialogs/qmldir3
152 files changed, 10947 insertions, 30 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4ad5f89b..9b11240b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,4 +3,5 @@
add_subdirectory(quicktemplates2)
add_subdirectory(quickcontrols2)
add_subdirectory(quickcontrols2impl)
+add_subdirectory(quickdialogs2)
add_subdirectory(imports)
diff --git a/src/imports/CMakeLists.txt b/src/imports/CMakeLists.txt
index 1d9b7814..d056a899 100644
--- a/src/imports/CMakeLists.txt
+++ b/src/imports/CMakeLists.txt
@@ -2,6 +2,7 @@
add_subdirectory(controls)
add_subdirectory(controlsimpl)
+add_subdirectory(dialogs)
add_subdirectory(platform)
add_subdirectory(templates)
add_subdirectory(nativestyle)
diff --git a/src/imports/dialogs/CMakeLists.txt b/src/imports/dialogs/CMakeLists.txt
new file mode 100644
index 00000000..f96fd9ff
--- /dev/null
+++ b/src/imports/dialogs/CMakeLists.txt
@@ -0,0 +1,29 @@
+qt_internal_add_qml_module(qtquickdialogsplugin
+ URI "QtQuick.Dialogs"
+ VERSION "${CMAKE_PROJECT_VERSION}"
+ CLASSNAME QtQuickDialogsPlugin
+ SKIP_TYPE_REGISTRATION
+ SOURCES
+ qtquickdialogsplugin.cpp
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ INCLUDE_DIRECTORIES
+ quick
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickDialogs2Private
+ Qt::QuickPrivate
+ Qt::QuickTemplates2Private
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+)
+
+qt_internal_add_docs(qtquickdialogsplugin
+ doc/qtquickdialogs.qdocconf
+)
diff --git a/src/imports/dialogs/doc/images/qtquickdialogs-filedialog-gtk.png b/src/imports/dialogs/doc/images/qtquickdialogs-filedialog-gtk.png
new file mode 100644
index 00000000..9360d747
--- /dev/null
+++ b/src/imports/dialogs/doc/images/qtquickdialogs-filedialog-gtk.png
Binary files differ
diff --git a/src/imports/dialogs/doc/qtquickdialogs.qdocconf b/src/imports/dialogs/doc/qtquickdialogs.qdocconf
new file mode 100644
index 00000000..575c84d0
--- /dev/null
+++ b/src/imports/dialogs/doc/qtquickdialogs.qdocconf
@@ -0,0 +1,40 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtquickcontrols2.qdocconf)
+
+project = QtQuickDialogs
+description = Qt Quick Dialogs Reference Documentation
+version = $QT_VERSION
+
+qhp.projects = QtQuickDialogs
+
+qhp.QtQuickDialogs.file = qtquickdialogs.qhp
+qhp.QtQuickDialogs.namespace = org.qt-project.qtquickdialogs.$QT_VERSION_TAG
+qhp.QtQuickDialogs.virtualFolder = qtquickdialogs
+qhp.QtQuickDialogs.indexTitle = Qt Quick Dialogs
+qhp.QtQuickDialogs.indexRoot =
+
+qhp.QtQuickDialogs.filterAttributes = qtquickdialogs $QT_VERSION qtrefdoc
+qhp.QtQuickDialogs.customFilters.Qt.name = QtQuickDialogs $QT_VERSION
+qhp.QtQuickDialogs.customFilters.Qt.filterAttributes = qtquickdialogs $QT_VERSION
+
+qhp.QtQuickDialogs.subprojects = qmltypes
+qhp.QtQuickDialogs.subprojects.qmltypes.title = QML Types
+qhp.QtQuickDialogs.subprojects.qmltypes.indexTitle = Qt Quick Dialogs QML Types
+qhp.QtQuickDialogs.subprojects.qmltypes.selectors = qmlclass
+qhp.QtQuickDialogs.subprojects.qmltypes.sortPages = true
+
+depends = qtcore qtgui qtdoc qtqml qtquick qtquickcontrols
+
+# This module has no documented C++ types, clear the module header
+moduleheader =
+
+headerdirs += ../../../quickdialogs2/quickdialogs2
+sourcedirs += ../../../quickdialogs2/quickdialogs2 \
+ src
+
+imagedirs += images
+
+navigation.landingpage = "Qt Quick Dialogs"
+navigation.qmltypespage = "Qt Quick Dialogs QML Types"
+
+tagfile = qtquickdialogs.tags
diff --git a/src/imports/dialogs/doc/src/includes/fallback.qdocinc b/src/imports/dialogs/doc/src/includes/fallback.qdocinc
new file mode 100644
index 00000000..f3a4a180
--- /dev/null
+++ b/src/imports/dialogs/doc/src/includes/fallback.qdocinc
@@ -0,0 +1 @@
+Qt Quick Dialogs uses a Qt Quick implementation as a fallback on platforms that do not have a native implementation available.
diff --git a/src/imports/dialogs/qmldir b/src/imports/dialogs/qmldir
new file mode 100644
index 00000000..11d5bcdf
--- /dev/null
+++ b/src/imports/dialogs/qmldir
@@ -0,0 +1,3 @@
+module QtQuick.Dialogs
+plugin qtquickdialogsplugin
+classname QtQuickDialogsPlugin
diff --git a/src/imports/dialogs/qtquickdialogsplugin.cpp b/src/imports/dialogs/qtquickdialogsplugin.cpp
new file mode 100644
index 00000000..9bc06c25
--- /dev/null
+++ b/src/imports/dialogs/qtquickdialogsplugin.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Templates module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QtQuickDialogs2/private/qtquickdialogs2global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QtQuickDialogsPlugin: public QQmlEngineExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ QtQuickDialogsPlugin(QObject *parent = nullptr);
+};
+
+QtQuickDialogsPlugin::QtQuickDialogsPlugin(QObject *parent) : QQmlEngineExtensionPlugin(parent)
+{
+ volatile auto registration = &qml_register_types_QtQuick_Dialogs;
+ Q_UNUSED(registration)
+}
+
+QT_END_NAMESPACE
+
+#include "qtquickdialogsplugin.moc"
diff --git a/src/quickdialogs2/CMakeLists.txt b/src/quickdialogs2/CMakeLists.txt
new file mode 100644
index 00000000..88ae9633
--- /dev/null
+++ b/src/quickdialogs2/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(quickdialogs2utils)
+add_subdirectory(quickdialogs2quickimpl)
+add_subdirectory(quickdialogs2)
diff --git a/src/quickdialogs2/quickdialogs2/CMakeLists.txt b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
new file mode 100644
index 00000000..59f671d7
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2/CMakeLists.txt
@@ -0,0 +1,43 @@
+#####################################################################
+## QuickDialogs2 Module:
+#####################################################################
+
+qt_internal_add_module(QuickDialogs2
+ SOURCES
+ qquickabstractdialog.cpp
+ qquickabstractdialog_p.h
+ qquickfiledialog.cpp
+ qquickfiledialog_p.h
+ qtquickdialogs2foreign_p.h
+ qtquickdialogs2global_p.h
+ DEFINES
+ QT_BUILD_QUICKDIALOGS2_LIB
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ Qt::QuickDialogs2UtilsPrivate
+ Qt::QuickDialogs2QuickImplPrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+)
+
+## Scopes:
+#####################################################################
+
+set_target_properties(QuickDialogs2 PROPERTIES
+ QT_QML_MODULE_INSTALL_QMLTYPES TRUE
+ QT_QML_MODULE_VERSION ${CMAKE_PROJECT_VERSION}
+ QT_QML_MODULE_URI QtQuick.Dialogs
+ QT_QMLTYPES_FILENAME plugins.qmltypes
+ QT_QML_MODULE_INSTALL_DIR "${INSTALL_QMLDIR}/QtQuick/Dialogs"
+)
+
+qt6_qml_type_registration(QuickDialogs2)
diff --git a/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
new file mode 100644
index 00000000..401091f5
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2/qquickabstractdialog.cpp
@@ -0,0 +1,454 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickabstractdialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuickDialogs2QuickImpl/private/qquickdialogimplfactory_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcDialogs, "qt.quick.dialogs")
+
+/*!
+ \internal
+
+ A dialog that can be backed by different implementations.
+
+ Each dialog has a handle to QPlatformDialogHelper, which is created in create().
+ The helper acts as an intermediary between the QML-facing dialog object
+ and the native/widget/quick implementation:
+
+ +------------+ +------------------------------------+ +-------------------------------------+
+ | | | | | |
+ | FileDialog |----->| Native/Widget/Quick QPlatformFile- |----->| Native OS dialog/QQuickFileDialog/ |
+ | | | DialogHelper subclass | | QQuickFileDialogImpl |
+ | | | | | |
+ +------------+ +------------------------------------+ +-------------------------------------+
+*/
+
+/*!
+ \qmltype Dialog
+ \inherits QtObject
+//! \instantiates QQuickAbstractDialog
+ \inqmlmodule QtQuick.Dialogs
+ \since 6.2
+ \brief The base class of native dialogs.
+
+ The Dialog type provides common QML API for native platform dialogs.
+
+ To show a native dialog, construct an instance of one of the concrete
+ Dialog implementations, set the desired properties, and call \l open().
+ Dialog emits \l accepted() or \l rejected() when the user is done with
+ the dialog.
+*/
+
+/*!
+ \qmlsignal void QtQuick.Dialogs::Dialog::accepted()
+
+ This signal is emitted when the dialog has been accepted either
+ interactively or by calling \l accept().
+
+ \note This signal is \e not emitted when closing the dialog with \l close().
+
+ \sa rejected()
+*/
+
+/*!
+ \qmlsignal void QtQuick.Dialogs::Dialog::rejected()
+
+ This signal is emitted when the dialog has been rejected either
+ interactively or by calling \l reject().
+
+ \note This signal is \e not emitted when closing the dialog with \l close().
+
+ \sa accepted()
+*/
+
+Q_DECLARE_LOGGING_CATEGORY(lcDialogs)
+
+QQuickAbstractDialog::QQuickAbstractDialog(QPlatformTheme::DialogType type, QObject *parent)
+ : QObject(parent),
+ m_type(type)
+{
+}
+
+QQuickAbstractDialog::~QQuickAbstractDialog()
+{
+ destroy();
+}
+
+QPlatformDialogHelper *QQuickAbstractDialog::handle() const
+{
+ return m_handle.get();
+}
+
+/*!
+ \qmldefault
+ \qmlproperty list<Object> QtQuick.Dialogs::Dialog::data
+
+ This default property holds the list of all objects declared as children of
+ the dialog.
+*/
+QQmlListProperty<QObject> QQuickAbstractDialog::data()
+{
+ return QQmlListProperty<QObject>(this, &m_data);
+}
+
+/*!
+ \qmlproperty Window QtQuick.Dialogs::Dialog::parentWindow
+
+ This property holds the parent window of the dialog.
+
+ Unless explicitly set, the window is automatically resolved by iterating
+ the QML parent objects until a \l Window or an \l Item that has a window
+ is found.
+*/
+QWindow *QQuickAbstractDialog::parentWindow() const
+{
+ return m_parentWindow;
+}
+
+void QQuickAbstractDialog::setParentWindow(QWindow *window)
+{
+ qCDebug(lcDialogs) << "set parent window to" << window;
+ if (m_parentWindow == window)
+ return;
+
+ m_parentWindow = window;
+ emit parentWindowChanged();
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::Dialog::title
+
+ This property holds the title of the dialog.
+*/
+QString QQuickAbstractDialog::title() const
+{
+ return m_title;
+}
+
+void QQuickAbstractDialog::setTitle(const QString &title)
+{
+ if (m_title == title)
+ return;
+
+ m_title = title;
+ emit titleChanged();
+}
+
+/*!
+ \qmlproperty Qt::WindowFlags QtQuick.Dialogs::Dialog::flags
+
+ This property holds the window flags of the dialog. The default value is \c Qt.Dialog.
+*/
+Qt::WindowFlags QQuickAbstractDialog::flags() const
+{
+ return m_flags;
+}
+
+void QQuickAbstractDialog::setFlags(Qt::WindowFlags flags)
+{
+ if (m_flags == flags)
+ return;
+
+ m_flags = flags;
+ emit flagsChanged();
+}
+
+/*!
+ \qmlproperty Qt::WindowModality QtQuick.Dialogs::Dialog::modality
+
+ This property holds the modality of the dialog. The default value is \c Qt.WindowModal.
+
+ Available values:
+ \value Qt.NonModal The dialog is not modal and does not block input to other windows.
+ \value Qt.WindowModal The dialog is modal to a single window hierarchy and blocks input to its parent window, all grandparent windows, and all siblings of its parent and grandparent windows.
+ \value Qt.ApplicationModal The dialog is modal to the application and blocks input to all windows.
+*/
+Qt::WindowModality QQuickAbstractDialog::modality() const
+{
+ return m_modality;
+}
+
+void QQuickAbstractDialog::setModality(Qt::WindowModality modality)
+{
+ if (m_modality == modality)
+ return;
+
+ m_modality = modality;
+ emit modalityChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick.Dialogs::Dialog::visible
+
+ This property holds the visibility of the dialog. The default value is \c false.
+
+ \sa open(), close()
+*/
+bool QQuickAbstractDialog::isVisible() const
+{
+ return m_handle && m_visible;
+}
+
+void QQuickAbstractDialog::setVisible(bool visible)
+{
+ qCDebug(lcDialogs) << "setVisible called with" << visible;
+
+ if (visible) {
+ // Don't try to open before component completion, as we won't have a window yet,
+ // and open() sets m_visible to false if it fails.
+ if (!m_complete)
+ m_visibleRequested = true;
+ else
+ open();
+ } else {
+ close();
+ }
+}
+
+/*!
+ \qmlproperty StandardCode QtQuick.Dialogs::Dialog::result
+
+ This property holds the result code.
+
+ Standard result codes:
+ \value Dialog.Accepted
+ \value Dialog.Rejected
+
+ \note MessageDialog sets the result to the value of the clicked standard
+ button instead of using the standard result codes.
+*/
+QQuickAbstractDialog::StandardCode QQuickAbstractDialog::result() const
+{
+ return m_result;
+}
+
+void QQuickAbstractDialog::setResult(StandardCode result)
+{
+ if (m_result == result)
+ return;
+
+ m_result = result;
+ emit resultChanged();
+}
+
+/*!
+ \qmlmethod void QtQuick.Dialogs::Dialog::open()
+
+ Opens the dialog.
+
+ \sa visible, close()
+*/
+void QQuickAbstractDialog::open()
+{
+ qCDebug(lcDialogs) << "open called";
+ if (m_visible || !create())
+ return;
+
+ onShow(m_handle.get());
+ m_visible = m_handle->show(m_flags, m_modality, m_parentWindow);
+ if (m_visible)
+ emit visibleChanged();
+}
+
+/*!
+ \qmlmethod void QtQuick.Dialogs::Dialog::close()
+
+ Closes the dialog.
+
+ \sa visible, open()
+*/
+void QQuickAbstractDialog::close()
+{
+ if (!m_handle || !m_visible)
+ return;
+
+ onHide(m_handle.get());
+ m_handle->hide();
+ m_visible = false;
+ emit visibleChanged();
+}
+
+/*!
+ \qmlmethod void QtQuick.Dialogs::Dialog::accept()
+
+ Closes the dialog and emits the \l accepted() signal.
+
+ \sa reject()
+*/
+void QQuickAbstractDialog::accept()
+{
+ done(Accepted);
+}
+
+/*!
+ \qmlmethod void QtQuick.Dialogs::Dialog::reject()
+
+ Closes the dialog and emits the \l rejected() signal.
+
+ \sa accept()
+*/
+void QQuickAbstractDialog::reject()
+{
+ done(Rejected);
+}
+
+/*!
+ \qmlmethod void QtQuick.Dialogs::Dialog::done(StandardCode result)
+
+ Closes the dialog and sets the \a result.
+
+ \sa accept(), reject(), result
+*/
+void QQuickAbstractDialog::done(StandardCode result)
+{
+ close();
+ setResult(result);
+
+ if (result == Accepted)
+ emit accepted();
+ else if (result == Rejected)
+ emit rejected();
+}
+
+void QQuickAbstractDialog::classBegin()
+{
+}
+
+void QQuickAbstractDialog::componentComplete()
+{
+ qCDebug(lcDialogs) << "componentComplete";
+ m_complete = true;
+
+ if (!m_parentWindow) {
+ qCDebug(lcDialogs) << "- no parent window; searching for one";
+ setParentWindow(findParentWindow());
+ }
+
+ if (m_visibleRequested) {
+ qCDebug(lcDialogs) << "visible was bound to true before component completion; opening dialog";
+ open();
+ m_visibleRequested = false;
+ }
+}
+
+static const char *qmlTypeName(const QObject *object)
+{
+ return object->metaObject()->className() + qstrlen("QQuickPlatform");
+}
+
+bool QQuickAbstractDialog::create()
+{
+ qCDebug(lcDialogs) << qmlTypeName(this) << "attempting to create dialog backend of type"
+ << m_type << "with parent window" << m_parentWindow;
+ if (m_handle)
+ return m_handle.get();
+
+ qCDebug(lcDialogs) << "- attempting to create a native dialog";
+ if (useNativeDialog())
+ m_handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformDialogHelper(m_type));
+
+ if (!m_handle) {
+ qCDebug(lcDialogs) << "- attempting to create a quick dialog";
+ m_handle.reset(QQuickDialogImplFactory::createPlatformDialogHelper(m_type, this));
+ }
+
+ qCDebug(lcDialogs) << qmlTypeName(this) << "created ->" << m_handle.get();
+ if (m_handle) {
+ onCreate(m_handle.get());
+ connect(m_handle.get(), &QPlatformDialogHelper::accept, this, &QQuickAbstractDialog::accept);
+ connect(m_handle.get(), &QPlatformDialogHelper::reject, this, &QQuickAbstractDialog::reject);
+ }
+ return m_handle.get();
+}
+
+void QQuickAbstractDialog::destroy()
+{
+ m_handle.reset();
+}
+
+bool QQuickAbstractDialog::useNativeDialog() const
+{
+ if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)) {
+ qCDebug(lcDialogs) << " - Qt::AA_DontUseNativeDialogs was set; not using native dialog";
+ return false;
+ }
+
+ if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(m_type)) {
+ qCDebug(lcDialogs) << " - the platform theme told us a native dialog isn't available; not using native dialog";
+ return false;
+ }
+
+ return true;
+}
+
+void QQuickAbstractDialog::onCreate(QPlatformDialogHelper *dialog)
+{
+ Q_UNUSED(dialog);
+}
+
+void QQuickAbstractDialog::onShow(QPlatformDialogHelper *dialog)
+{
+ Q_UNUSED(dialog);
+}
+
+void QQuickAbstractDialog::onHide(QPlatformDialogHelper *dialog)
+{
+ Q_UNUSED(dialog);
+}
+
+QWindow *QQuickAbstractDialog::findParentWindow() const
+{
+ QObject *obj = parent();
+ while (obj) {
+ QWindow *window = qobject_cast<QWindow *>(obj);
+ if (window)
+ return window;
+ QQuickItem *item = qobject_cast<QQuickItem *>(obj);
+ if (item && item->window())
+ return item->window();
+ obj = obj->parent();
+ }
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs2/quickdialogs2/qquickabstractdialog_p.h b/src/quickdialogs2/quickdialogs2/qquickabstractdialog_p.h
new file mode 100644
index 00000000..5da2231d
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2/qquickabstractdialog_p.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKABSTRACTDIALOG_P_H
+#define QQUICKABSTRACTDIALOG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <memory>
+
+#include <QtCore/qobject.h>
+#include <QtGui/qpa/qplatformtheme.h>
+#include <QtGui/qpa/qplatformdialoghelper.h>
+#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qqml.h>
+
+#include "qtquickdialogs2global_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+class QPlatformDialogHelper;
+
+class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickAbstractDialog : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(QQmlListProperty<QObject> data READ data FINAL)
+ Q_PROPERTY(QWindow *parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged FINAL)
+ Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
+ Q_PROPERTY(Qt::WindowFlags flags READ flags WRITE setFlags NOTIFY flagsChanged FINAL)
+ Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged FINAL)
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL)
+ Q_PROPERTY(StandardCode result READ result WRITE setResult NOTIFY resultChanged FINAL)
+ Q_CLASSINFO("DefaultProperty", "data")
+ Q_MOC_INCLUDE(<QtGui/qwindow.h>)
+ QML_ANONYMOUS
+ QML_ADDED_IN_VERSION(6, 2)
+
+public:
+ explicit QQuickAbstractDialog(QPlatformTheme::DialogType type, QObject *parent = nullptr);
+ ~QQuickAbstractDialog();
+
+ QPlatformDialogHelper *handle() const;
+
+ QQmlListProperty<QObject> data();
+
+ QWindow *parentWindow() const;
+ void setParentWindow(QWindow *window);
+
+ QString title() const;
+ void setTitle(const QString &title);
+
+ Qt::WindowFlags flags() const;
+ void setFlags(Qt::WindowFlags flags);
+
+ Qt::WindowModality modality() const;
+ void setModality(Qt::WindowModality modality);
+
+ bool isVisible() const;
+ void setVisible(bool visible);
+
+ enum StandardCode { Rejected, Accepted };
+ Q_ENUM(StandardCode)
+
+ StandardCode result() const;
+ void setResult(StandardCode result);
+
+public Q_SLOTS:
+ void open();
+ void close();
+ virtual void accept();
+ virtual void reject();
+ virtual void done(StandardCode result);
+
+Q_SIGNALS:
+ void accepted();
+ void rejected();
+ void parentWindowChanged();
+ void titleChanged();
+ void flagsChanged();
+ void modalityChanged();
+ void visibleChanged();
+ void resultChanged();
+
+protected:
+ void classBegin() override;
+ void componentComplete() override;
+
+ bool create();
+ void destroy();
+
+ virtual bool useNativeDialog() const;
+ virtual void onCreate(QPlatformDialogHelper *dialog);
+ virtual void onShow(QPlatformDialogHelper *dialog);
+ virtual void onHide(QPlatformDialogHelper *dialog);
+
+ QWindow *findParentWindow() const;
+
+private:
+ bool m_visibleRequested = false;
+ bool m_visible = false;
+ bool m_complete = false;
+ StandardCode m_result = Rejected;
+ QWindow *m_parentWindow = nullptr;
+ QString m_title;
+ Qt::WindowFlags m_flags = Qt::Dialog;
+ Qt::WindowModality m_modality = Qt::WindowModal;
+ QPlatformTheme::DialogType m_type;
+ QList<QObject *> m_data;
+ std::unique_ptr<QPlatformDialogHelper> m_handle;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickAbstractDialog)
+
+#endif // QQUICKABSTRACTDIALOG_P_H
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
new file mode 100644
index 00000000..e2b34075
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog.cpp
@@ -0,0 +1,612 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickfiledialog_p.h"
+
+#include <QtCore/qlist.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtQml/qqmlfile.h>
+
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcFileDialog, "qt.quick.dialogs.filedialog")
+
+/*!
+ \qmltype FileDialog
+ \inherits Dialog
+//! \instantiates QQuickFileDialog
+ \inqmlmodule QtQuick.Dialogs
+ \since 6.2
+ \brief A file dialog.
+
+ The FileDialog type provides a QML API for file dialogs.
+
+ \image qtquickdialogs-filedialog-gtk.png
+
+ To show a file dialog, construct an instance of FileDialog, set the
+ desired properties, and call \l {Dialog::}{open()}. The \l currentFile
+ or \l currentFiles properties can be used to determine the currently
+ selected file(s) in the dialog. The \l selectedFile and \l selectedFiles
+ properties are updated only after the final selection has been made by
+ accepting the dialog.
+
+ \code
+ MenuItem {
+ text: "Open..."
+ onTriggered: fileDialog.open()
+ }
+
+ FileDialog {
+ id: fileDialog
+ currentFile: document.source
+ folder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
+ }
+
+ MyDocument {
+ id: document
+ source: fileDialog.file
+ }
+ \endcode
+
+ \section2 Availability
+
+ A native platform file dialog is currently available on the following platforms:
+
+ \list
+ \li iOS
+ \li Linux (when running with the GTK+ platform theme)
+ \li macOS
+ \li Windows
+ \endlist
+
+ \include includes/fallback.qdocinc
+
+ \sa FolderDialog, StandardPaths
+*/
+
+Q_DECLARE_LOGGING_CATEGORY(lcDialogs)
+
+QQuickFileDialog::QQuickFileDialog(QObject *parent)
+ : QQuickAbstractDialog(QPlatformTheme::FileDialog, parent),
+ m_fileMode(OpenFile),
+ m_options(QFileDialogOptions::create()),
+ m_selectedNameFilter(nullptr)
+{
+ m_options->setFileMode(QFileDialogOptions::ExistingFile);
+ m_options->setAcceptMode(QFileDialogOptions::AcceptOpen);
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Dialogs::FileDialog::fileMode
+
+ This property holds the mode of the dialog.
+
+ Available values:
+ \value FileDialog.OpenFile The dialog is used to select an existing file (default).
+ \value FileDialog.OpenFiles The dialog is used to select multiple existing files.
+ \value FileDialog.SaveFile The dialog is used to select any file. The file does not have to exist.
+*/
+QQuickFileDialog::FileMode QQuickFileDialog::fileMode() const
+{
+ return m_fileMode;
+}
+
+void QQuickFileDialog::setFileMode(FileMode mode)
+{
+ qCDebug(lcFileDialog) << "setFileMode called with" << mode;
+ if (mode == m_fileMode)
+ return;
+
+ switch (mode) {
+ case OpenFile:
+ m_options->setFileMode(QFileDialogOptions::ExistingFile);
+ m_options->setAcceptMode(QFileDialogOptions::AcceptOpen);
+ break;
+ case OpenFiles:
+ m_options->setFileMode(QFileDialogOptions::ExistingFiles);
+ m_options->setAcceptMode(QFileDialogOptions::AcceptOpen);
+ break;
+ case SaveFile:
+ m_options->setFileMode(QFileDialogOptions::AnyFile);
+ m_options->setAcceptMode(QFileDialogOptions::AcceptSave);
+ break;
+ default:
+ break;
+ }
+
+ m_fileMode = mode;
+ emit fileModeChanged();
+}
+
+/*!
+ \qmlproperty url QtQuick.Dialogs::FileDialog::selectedFile
+ \readonly
+
+ This property holds the final accepted file.
+
+ If there are multiple selected files, this property refers to the first
+ file.
+
+ Unlike the \l currentFile property, the \c selectedFile property is not
+ updated while the user is selecting files in the dialog, but only after the
+ final selection has been made. That is, when the user has clicked
+ \uicontrol OK to accept a file. Alternatively, the
+ \l {Dialog::}{accepted()} signal can be handled to get the final selection.
+
+ \sa selectedFiles, currentFile, {Dialog::}{accepted()}, currentFolder
+*/
+QUrl QQuickFileDialog::selectedFile() const
+{
+ return addDefaultSuffix(m_selectedFiles.value(0));
+}
+
+/*!
+ \qmlproperty list<url> QtQuick.Dialogs::FileDialog::selectedFiles
+
+ This property holds the final accepted files.
+
+ Unlike the \l currentFiles property, the \c selectedFiles property is not
+ updated while the user is selecting files in the dialog, but only after the
+ final selection has been made. That is, when the user has clicked
+ \uicontrol OK to accept files. Alternatively, the \l {Dialog::}{accepted()}
+ signal can be handled to get the final selection.
+
+ \sa currentFiles, {Dialog::}{accepted()}, currentFolder
+*/
+QList<QUrl> QQuickFileDialog::selectedFiles() const
+{
+ return addDefaultSuffixes(m_selectedFiles);
+}
+
+void QQuickFileDialog::setSelectedFiles(const QList<QUrl> &selectedFiles)
+{
+ if (m_selectedFiles == selectedFiles)
+ return;
+
+ bool firstChanged = m_selectedFiles.value(0) != selectedFiles.value(0);
+ m_selectedFiles = selectedFiles;
+ if (firstChanged)
+ emit selectedFileChanged();
+ emit selectedFilesChanged();
+}
+
+/*!
+ \qmlproperty url QtQuick.Dialogs::FileDialog::currentFile
+
+ This property holds the currently selected file in the dialog.
+
+ Unlike the \l selectedFile property, the \c currentFile property is updated
+ while the user is selecting files in the dialog, even before the final
+ selection has been made.
+
+ \sa selectedFile, currentFiles, currentFolder
+*/
+QUrl QQuickFileDialog::currentFile() const
+{
+ return currentFiles().value(0);
+}
+
+void QQuickFileDialog::setCurrentFile(const QUrl &file)
+{
+ setCurrentFiles(QList<QUrl>() << file);
+}
+
+/*!
+ \qmlproperty list<url> QtQuick.Dialogs::FileDialog::currentFiles
+
+ This property holds the currently selected files in the dialog.
+
+ Unlike the \l selectedFiles property, the \c currentFiles property is
+ updated while the user is selecting files in the dialog, even before the
+ final selection has been made.
+
+ \sa selectedFiles, currentFile, currentFolder
+*/
+QList<QUrl> QQuickFileDialog::currentFiles() const
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle()))
+ return fileDialog->selectedFiles();
+ return m_options->initiallySelectedFiles();
+}
+
+void QQuickFileDialog::setCurrentFiles(const QList<QUrl> &currentFiles)
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) {
+ for (const QUrl &file : currentFiles)
+ fileDialog->selectFile(file);
+ }
+ m_options->setInitiallySelectedFiles(currentFiles);
+}
+
+/*!
+ \qmlproperty url QtQuick.Dialogs::FileDialog::currentFolder
+
+ This property holds the folder where files are selected. It can be set to
+ control the initial directory that is shown when the dialog is opened.
+
+ For selecting a folder, use FolderDialog instead.
+
+ \sa FolderDialog
+*/
+QUrl QQuickFileDialog::currentFolder() const
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle()))
+ return fileDialog->directory();
+
+ // If we're not using a native file dialog and the folder is invalid,
+ // return the current directory.
+ if (!m_options->initialDirectory().isValid())
+ return QUrl::fromLocalFile(QDir::currentPath());
+
+ return m_options->initialDirectory();
+}
+
+void QQuickFileDialog::setCurrentFolder(const QUrl &currentFolder)
+{
+ qCDebug(lcFileDialog) << "setCurrentFolder called with" << currentFolder;
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle()))
+ fileDialog->setDirectory(currentFolder);
+ m_options->setInitialDirectory(currentFolder);
+}
+
+/*!
+ \qmlproperty flags QtQuick.Dialogs::FileDialog::options
+
+ This property holds the various options that affect the look and feel of the dialog.
+
+ By default, all options are disabled.
+
+ Options should be set before showing the dialog. Setting them while the dialog is
+ visible is not guaranteed to have an immediate effect on the dialog (depending on
+ the option and on the platform).
+
+ Available options:
+ \value FileDialog.DontResolveSymlinks Don't resolve symlinks in the file dialog. By default symlinks are resolved.
+ \value FileDialog.DontConfirmOverwrite Don't ask for confirmation if an existing file is selected. By default confirmation is requested.
+ \value FileDialog.ReadOnly Indicates that the dialog doesn't allow creating directories.
+ \value FileDialog.HideNameFilterDetails Indicates if the file name filter details are hidden or not.
+*/
+QFileDialogOptions::FileDialogOptions QQuickFileDialog::options() const
+{
+ return m_options->options();
+}
+
+void QQuickFileDialog::setOptions(QFileDialogOptions::FileDialogOptions options)
+{
+ if (options == m_options->options())
+ return;
+
+ m_options->setOptions(options);
+ emit optionsChanged();
+}
+
+void QQuickFileDialog::resetOptions()
+{
+ setOptions({});
+}
+
+/*!
+ \qmlproperty list<string> QtQuick.Dialogs::FileDialog::nameFilters
+
+ This property holds the filters that restrict the types of files that
+ can be selected.
+
+ \code
+ FileDialog {
+ nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)"]
+ }
+ \endcode
+
+ Different platforms may restrict the files that can be selected in
+ different ways. For example, macOS will disable file entries that do not
+ match the filters, whereas Windows will hide them.
+
+ \note \b{*.*} is not a portable filter, because the historical assumption
+ that the file extension determines the file type is not consistent on every
+ operating system. It is possible to have a file with no dot in its name (for
+ example, \c Makefile). In a native Windows file dialog, \b{*.*} will match
+ such files, while in other types of file dialogs it may not. So it is better
+ to use \b{*} if you mean to select any file.
+
+ \sa selectedNameFilter
+*/
+QStringList QQuickFileDialog::nameFilters() const
+{
+ return m_options->nameFilters();
+}
+
+void QQuickFileDialog::setNameFilters(const QStringList &filters)
+{
+ qCDebug(lcFileDialog).nospace() << "setNameFilters called with " << filters
+ << " (old filters were: " << m_options->nameFilters() << ")";
+ if (filters == m_options->nameFilters())
+ 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();
+}
+
+void QQuickFileDialog::resetNameFilters()
+{
+ setNameFilters(QStringList());
+}
+
+/*!
+ \qmlproperty int QtQuick.Dialogs::FileDialog::selectedNameFilter.index
+ \qmlproperty string QtQuick.Dialogs::FileDialog::selectedNameFilter.name
+ \qmlproperty list<string> QtQuick.Dialogs::FileDialog::selectedNameFilter.extensions
+ \qmlproperty list<string> QtQuick.Dialogs::FileDialog::selectedNameFilter.globs
+
+ 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<string>
+ \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"]}.
+ \row
+ \li [read-only] \b globs : list<string>
+ \li This property holds the list of globs of the selected filter.
+ In the example below, the list of globs of the first filter is
+ \c {["*.txt"]} and the second is \c {["*.html", "*.htm"]}.
+
+ This property is useful in conjunction with \l {FolderListModel}'s
+ \l {FolderListModel::}{nameFilters} property, for example.
+ \endtable
+
+ \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
+*/
+QQuickFileNameFilter *QQuickFileDialog::selectedNameFilter() const
+{
+ if (!m_selectedNameFilter) {
+ QQuickFileDialog *that = const_cast<QQuickFileDialog *>(this);
+ m_selectedNameFilter = new QQuickFileNameFilter(that);
+ m_selectedNameFilter->setOptions(m_options);
+ }
+ return m_selectedNameFilter;
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::FileDialog::defaultSuffix
+
+ This property holds a suffix that is added to selected files that have
+ no suffix specified. The suffix is typically used to indicate the file
+ type (e.g. "txt" indicates a text file).
+
+ If the first character is a dot ('.'), it is removed.
+*/
+QString QQuickFileDialog::defaultSuffix() const
+{
+ return m_options->defaultSuffix();
+}
+
+void QQuickFileDialog::setDefaultSuffix(const QString &suffix)
+{
+ if (suffix == m_options->defaultSuffix())
+ return;
+
+ m_options->setDefaultSuffix(suffix);
+ emit defaultSuffixChanged();
+}
+
+void QQuickFileDialog::resetDefaultSuffix()
+{
+ setDefaultSuffix(QString());
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::FileDialog::acceptLabel
+
+ This property holds the label text shown on the button that accepts the dialog.
+
+ When set to an empty string, the default label of the underlying platform is used.
+ The default label is typically \uicontrol Open or \uicontrol Save depending on which
+ \l fileMode the dialog is used in.
+
+ The default value is an empty string.
+
+ \sa rejectLabel
+*/
+QString QQuickFileDialog::acceptLabel() const
+{
+ return m_options->labelText(QFileDialogOptions::Accept);
+}
+
+void QQuickFileDialog::setAcceptLabel(const QString &label)
+{
+ if (label == m_options->labelText(QFileDialogOptions::Accept))
+ return;
+
+ m_options->setLabelText(QFileDialogOptions::Accept, label);
+ emit acceptLabelChanged();
+}
+
+void QQuickFileDialog::resetAcceptLabel()
+{
+ setAcceptLabel(QString());
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::FileDialog::rejectLabel
+
+ This property holds the label text shown on the button that rejects the dialog.
+
+ When set to an empty string, the default label of the underlying platform is used.
+ The default label is typically \uicontrol Cancel.
+
+ The default value is an empty string.
+
+ \sa acceptLabel
+*/
+QString QQuickFileDialog::rejectLabel() const
+{
+ return m_options->labelText(QFileDialogOptions::Reject);
+}
+
+void QQuickFileDialog::setRejectLabel(const QString &label)
+{
+ if (label == m_options->labelText(QFileDialogOptions::Reject))
+ return;
+
+ m_options->setLabelText(QFileDialogOptions::Reject, label);
+ emit rejectLabelChanged();
+}
+
+void QQuickFileDialog::resetRejectLabel()
+{
+ setRejectLabel(QString());
+}
+
+bool QQuickFileDialog::useNativeDialog() const
+{
+ if (!QQuickAbstractDialog::useNativeDialog())
+ return false;
+
+ if (m_options->testOption(QFileDialogOptions::DontUseNativeDialog)) {
+ qCDebug(lcDialogs) << " - the FileDialog was told not to use a native dialog; not using native dialog";
+ return false;
+ }
+
+ return true;
+}
+
+void QQuickFileDialog::onCreate(QPlatformDialogHelper *dialog)
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) {
+ connect(fileDialog, &QPlatformFileDialogHelper::currentChanged, this, &QQuickFileDialog::currentFileChanged);
+ connect(fileDialog, &QPlatformFileDialogHelper::currentChanged, this, &QQuickFileDialog::currentFilesChanged);
+ connect(fileDialog, &QPlatformFileDialogHelper::directoryEntered, this, &QQuickFileDialog::currentFolderChanged);
+ fileDialog->setOptions(m_options);
+ }
+}
+
+void QQuickFileDialog::onShow(QPlatformDialogHelper *dialog)
+{
+ m_options->setWindowTitle(title());
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) {
+ // Ensure that a name filter is always selected.
+ int index = selectedNameFilter()->index();
+ if (index == -1)
+ index = 0;
+ const QString filter = m_options->nameFilters().value(index);
+ m_options->setInitiallySelectedNameFilter(filter);
+
+ fileDialog->setOptions(m_options); // setOptions only assigns a member and isn't virtual
+
+ connect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickFileNameFilter::update);
+ fileDialog->selectNameFilter(filter);
+
+ const QUrl initialDir = m_options->initialDirectory();
+ // If it's not valid, or it's a file and not a directory, we shouldn't set it.
+ if (m_firstShow && initialDir.isValid() && QDir(QQmlFile::urlToLocalFileOrQrc(initialDir)).exists())
+ fileDialog->setDirectory(m_options->initialDirectory());
+ }
+ if (m_firstShow)
+ m_firstShow = false;
+}
+
+void QQuickFileDialog::onHide(QPlatformDialogHelper *dialog)
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) {
+ if (m_selectedNameFilter)
+ disconnect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickFileNameFilter::update);
+ }
+}
+
+void QQuickFileDialog::accept()
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) {
+ // Take the currently selected files and make them the final set of files.
+ setSelectedFiles(fileDialog->selectedFiles());
+ }
+ QQuickAbstractDialog::accept();
+}
+
+QUrl QQuickFileDialog::addDefaultSuffix(const QUrl &file) const
+{
+ QUrl url = file;
+ const QString path = url.path();
+ const QString suffix = m_options->defaultSuffix();
+ if (!suffix.isEmpty() && !path.endsWith(QLatin1Char('/')) && path.lastIndexOf(QLatin1Char('.')) == -1)
+ url.setPath(path + QLatin1Char('.') + suffix);
+ return url;
+}
+
+QList<QUrl> QQuickFileDialog::addDefaultSuffixes(const QList<QUrl> &files) const
+{
+ QList<QUrl> urls;
+ urls.reserve(files.size());
+ for (const QUrl &file : files)
+ urls += addDefaultSuffix(file);
+ return urls;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
new file mode 100644
index 00000000..a0fbb686
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2/qquickfiledialog_p.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFILEDIALOG_P_H
+#define QQUICKFILEDIALOG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qurl.h>
+#include <QtQml/qqml.h>
+
+#include "qquickabstractdialog_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileNameFilter;
+
+class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFileDialog : public QQuickAbstractDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(FileMode fileMode READ fileMode WRITE setFileMode NOTIFY fileModeChanged FINAL)
+ Q_PROPERTY(QUrl selectedFile READ selectedFile NOTIFY selectedFileChanged FINAL)
+ Q_PROPERTY(QList<QUrl> selectedFiles READ selectedFiles NOTIFY selectedFilesChanged FINAL)
+ Q_PROPERTY(QUrl currentFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged FINAL)
+ Q_PROPERTY(QList<QUrl> currentFiles READ currentFiles WRITE setCurrentFiles NOTIFY currentFilesChanged FINAL)
+ Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged 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(QQuickFileNameFilter *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)
+ Q_FLAGS(QFileDialogOptions::FileDialogOptions)
+ QML_NAMED_ELEMENT(FileDialog)
+ QML_ADDED_IN_VERSION(6, 2)
+ Q_MOC_INCLUDE(<QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>)
+
+public:
+ explicit QQuickFileDialog(QObject *parent = nullptr);
+
+ enum FileMode {
+ OpenFile,
+ OpenFiles,
+ SaveFile
+ };
+ Q_ENUM(FileMode)
+
+ FileMode fileMode() const;
+ void setFileMode(FileMode fileMode);
+
+ QUrl selectedFile() const;
+
+ QList<QUrl> selectedFiles() const;
+
+ QUrl currentFile() const;
+ void setCurrentFile(const QUrl &file);
+
+ QList<QUrl> currentFiles() const;
+ void setCurrentFiles(const QList<QUrl> &currentFiles);
+
+ QUrl currentFolder() const;
+ void setCurrentFolder(const QUrl &currentFolder);
+
+ QFileDialogOptions::FileDialogOptions options() const;
+ void setOptions(QFileDialogOptions::FileDialogOptions options);
+ void resetOptions();
+
+ QStringList nameFilters() const;
+ void setNameFilters(const QStringList &filters);
+ void resetNameFilters();
+
+ QQuickFileNameFilter *selectedNameFilter() const;
+
+ QString defaultSuffix() const;
+ void setDefaultSuffix(const QString &suffix);
+ void resetDefaultSuffix();
+
+ QString acceptLabel() const;
+ void setAcceptLabel(const QString &label);
+ void resetAcceptLabel();
+
+ QString rejectLabel() const;
+ void setRejectLabel(const QString &label);
+ void resetRejectLabel();
+
+Q_SIGNALS:
+ void fileModeChanged();
+ void selectedFileChanged();
+ void selectedFilesChanged();
+ void currentFileChanged();
+ void currentFilesChanged();
+ void currentFolderChanged();
+ void optionsChanged();
+ void nameFiltersChanged();
+ void defaultSuffixChanged();
+ void acceptLabelChanged();
+ void rejectLabelChanged();
+
+protected:
+ bool useNativeDialog() const override;
+ void onCreate(QPlatformDialogHelper *dialog) override;
+ void onShow(QPlatformDialogHelper *dialog) override;
+ void onHide(QPlatformDialogHelper *dialog) override;
+ void accept() override;
+
+private:
+ QUrl addDefaultSuffix(const QUrl &file) const;
+ QList<QUrl> addDefaultSuffixes(const QList<QUrl> &files) const;
+
+ void setSelectedFiles(const QList<QUrl> &selectedFiles);
+
+ FileMode m_fileMode;
+ QList<QUrl> m_selectedFiles;
+ bool m_firstShow = true;
+ QSharedPointer<QFileDialogOptions> m_options;
+ mutable QQuickFileNameFilter *m_selectedNameFilter;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFileDialog)
+
+#endif // QQUICKFILEDIALOG_P_H
diff --git a/src/quickdialogs2/quickdialogs2/qtquickdialogs2foreign_p.h b/src/quickdialogs2/quickdialogs2/qtquickdialogs2foreign_p.h
new file mode 100644
index 00000000..fbab7ef7
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2/qtquickdialogs2foreign_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQUICKDIALOGS2FOREIGN_P_H
+#define QTQUICKDIALOGS2FOREIGN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpa/qplatformdialoghelper.h>
+#include <QtQml/qqml.h>
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QPlatformDialogHelperForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QPlatformDialogHelper)
+ QML_ADDED_IN_VERSION(6, 2)
+};
+
+struct QQuickFileNameFilterForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickFileNameFilter)
+ QML_ADDED_IN_VERSION(6, 2)
+};
+
+QT_END_NAMESPACE
+
+#endif // QTQUICKDIALOGS2FOREIGN_P_H
diff --git a/src/quickdialogs2/quickdialogs2/qtquickdialogs2global_p.h b/src/quickdialogs2/quickdialogs2/qtquickdialogs2global_p.h
new file mode 100644
index 00000000..87b8cdbf
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2/qtquickdialogs2global_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQUICKDIALOGS2GLOBAL_P_H
+#define QTQUICKDIALOGS2GLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtQml/private/qqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_STATIC
+# if defined(QT_BUILD_QUICKDIALOGS2_LIB)
+# define Q_QUICKDIALOGS2_PRIVATE_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QUICKDIALOGS2_PRIVATE_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_QUICKDIALOGS2_PRIVATE_EXPORT
+#endif
+
+QT_END_NAMESPACE
+
+Q_QUICKDIALOGS2_PRIVATE_EXPORT void qml_register_types_QtQuick_Dialogs();
+
+#endif // QTQUICKDIALOGS2GLOBAL_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt b/src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt
new file mode 100644
index 00000000..f5ad89e8
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/CMakeLists.txt
@@ -0,0 +1,134 @@
+#####################################################################
+## QuickDialogs2QuickImpl Module:
+#####################################################################
+
+qt_internal_add_module(QuickDialogs2QuickImpl
+ SOURCES
+ qquickdialogimplfactory.cpp
+ qquickdialogimplfactory_p.h
+ qquickfiledialogdelegate.cpp
+ qquickfiledialogdelegate_p.h
+ qquickfiledialogimpl.cpp
+ qquickfiledialogimpl_p.h
+ qquickfiledialogimpl_p_p.h
+ qquickfolderbreadcrumbbar.cpp
+ qquickfolderbreadcrumbbar_p.h
+ qquickfolderbreadcrumbbar_p_p.h
+ qquickplatformfiledialog.cpp
+ qquickplatformfiledialog_p.h
+ qtquickdialogs2quickimplforeign_p.h
+ qtquickdialogs2quickimplglobal_p.h
+ DEFINES
+ QT_BUILD_QUICKDIALOGS2QUICKIMPL_LIB
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickPrivate
+ Qt::QuickTemplates2Private
+ Qt::QuickDialogs2UtilsPrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+)
+
+# Resources:
+set(qtquickdialogs2quickimpl_resource_files
+ "images/crumb-separator-icon-round.png"
+ "images/crumb-separator-icon-round@2x.png"
+ "images/crumb-separator-icon-round@3x.png"
+ "images/crumb-separator-icon-round@4x.png"
+ "images/crumb-separator-icon-square.png"
+ "images/crumb-separator-icon-square@2x.png"
+ "images/crumb-separator-icon-square@3x.png"
+ "images/crumb-separator-icon-square@4x.png"
+ "images/file-icon-round.png"
+ "images/file-icon-round@2x.png"
+ "images/file-icon-round@3x.png"
+ "images/file-icon-round@4x.png"
+ "images/file-icon-square.png"
+ "images/file-icon-square@2x.png"
+ "images/file-icon-square@3x.png"
+ "images/file-icon-square@4x.png"
+ "images/folder-icon-round.png"
+ "images/folder-icon-round@2x.png"
+ "images/folder-icon-round@3x.png"
+ "images/folder-icon-round@4x.png"
+ "images/folder-icon-square.png"
+ "images/folder-icon-square@2x.png"
+ "images/folder-icon-square@3x.png"
+ "images/folder-icon-square@4x.png"
+ "images/imagine/filedialogdelegate-background@2x.9.png"
+ "images/imagine/filedialogdelegate-background@3x.9.png"
+ "images/imagine/filedialogdelegate-background@4x.9.png"
+ "images/imagine/filedialogdelegate-background.9.png"
+ "images/imagine/filedialogdelegate-background-disabled@2x.9.png"
+ "images/imagine/filedialogdelegate-background-disabled@3x.9.png"
+ "images/imagine/filedialogdelegate-background-disabled@4x.9.png"
+ "images/imagine/filedialogdelegate-background-disabled.9.png"
+ "images/imagine/filedialogdelegate-background-pressed@2x.9.png"
+ "images/imagine/filedialogdelegate-background-pressed@3x.9.png"
+ "images/imagine/filedialogdelegate-background-pressed@4x.9.png"
+ "images/imagine/filedialogdelegate-background-pressed.9.png"
+ "images/imagine/filedialogdelegate-background-focused@2x.9.png"
+ "images/imagine/filedialogdelegate-background-focused@3x.9.png"
+ "images/imagine/filedialogdelegate-background-focused@4x.9.png"
+ "images/imagine/filedialogdelegate-background-focused.9.png"
+ "images/imagine/filedialogdelegate-background-highlighted@2x.9.png"
+ "images/imagine/filedialogdelegate-background-highlighted@3x.9.png"
+ "images/imagine/filedialogdelegate-background-highlighted@4x.9.png"
+ "images/imagine/filedialogdelegate-background-highlighted.9.png"
+ "images/up-icon-round.png"
+ "images/up-icon-round@2x.png"
+ "images/up-icon-round@3x.png"
+ "images/up-icon-round@4x.png"
+ "images/up-icon-square.png"
+ "images/up-icon-square@2x.png"
+ "images/up-icon-square@3x.png"
+ "images/up-icon-square@4x.png"
+ "images/up-icon-thick-square.png"
+ "images/up-icon-thick-square@2x.png"
+ "images/up-icon-thick-square@3x.png"
+ "images/up-icon-thick-square@4x.png"
+ "qml/FileDialog.qml"
+ "qml/FileDialogDelegate.qml"
+ "qml/FileDialogDelegateLabel.qml"
+ "qml/FolderBreadcrumbBar.qml"
+ "qml/+Fusion/FileDialog.qml"
+ "qml/+Fusion/FileDialogDelegate.qml"
+ "qml/+Fusion/FolderBreadcrumbBar.qml"
+ "qml/+Imagine/FileDialog.qml"
+ "qml/+Imagine/FileDialogDelegate.qml"
+ "qml/+Imagine/FolderBreadcrumbBar.qml"
+ "qml/+Material/FileDialog.qml"
+ "qml/+Material/FileDialogDelegate.qml"
+ "qml/+Material/FolderBreadcrumbBar.qml"
+ "qml/+Universal/FileDialog.qml"
+ "qml/+Universal/FileDialogDelegate.qml"
+ "qml/+Universal/FolderBreadcrumbBar.qml"
+)
+
+qt_internal_add_resource(QuickDialogs2QuickImpl "QuickDialogs2QuickImpl"
+ PREFIX
+ "/qt-project.org/imports/QtQuick/Dialogs/quickimpl"
+ FILES
+ ${qtquickdialogs2quickimpl_resource_files}
+)
+
+## Scopes:
+#####################################################################
+
+set_target_properties(QuickDialogs2QuickImpl PROPERTIES
+ QT_QML_MODULE_INSTALL_QMLTYPES TRUE
+ QT_QML_MODULE_VERSION ${CMAKE_PROJECT_VERSION}
+ QT_QML_MODULE_URI QtQuick.Dialogs.quickimpl
+ QT_QMLTYPES_FILENAME plugins.qmltypes
+ QT_QML_MODULE_INSTALL_DIR "${INSTALL_QMLDIR}/QtQuick/Dialogs/quickimpl"
+)
+
+qt6_qml_type_registration(QuickDialogs2QuickImpl)
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.png
new file mode 100644
index 00000000..aaa5a3e4
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.svg
new file mode 100644
index 00000000..013cedef
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round.svg
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="8"
+ height="8"
+ viewBox="0 0 8 8"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="crumb-separator-icon-round.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8">
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect3421"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1367"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1365"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="16.000001"
+ inkscape:cx="6.7812496"
+ inkscape:cy="15.031249"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0"
+ height="14px">
+ <inkscape:grid
+ type="xygrid"
+ id="grid856" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="crumb-separator-icon-round"
+ transform="translate(2.046,3.0914383)">
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect2017"
+ width="8"
+ height="8"
+ x="-2.046"
+ y="-3.0914383" />
+ </g>
+ <path
+ id="rect858"
+ style="fill:#757575;stroke-width:2.43737;stroke-linejoin:round"
+ d="M 3.0219754,1.01117 6.416287,3.5317721 a 0.74259596,0.74259596 90 0 1 0,1.1923778 L 3.0219754,7.2447519 A 0.5029757,0.5029757 26.701285 0 1 2.2191312,6.8409408 l 0,-5.4259596 A 0.5029757,0.5029757 153.29872 0 1 3.0219754,1.01117 Z"
+ sodipodi:nodetypes="cccc"
+ inkscape:path-effect="#path-effect3421"
+ inkscape:original-d="m 2.2191312,0.41498117 5,3.71297983 -5,3.7129798 z" />
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect1449"
+ width="8"
+ height="8"
+ x="0"
+ y="0" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@2x.png
new file mode 100644
index 00000000..3f66fe17
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@3x.png
new file mode 100644
index 00000000..24d9f6a6
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@4x.png
new file mode 100644
index 00000000..1dc83ef0
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.png
new file mode 100644
index 00000000..1f7ac63e
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.svg
new file mode 100644
index 00000000..96636734
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square.svg
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="8"
+ height="8"
+ viewBox="0 0 8 8"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="crumb-separator-icon-square.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8">
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect3421"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1367"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1365"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="90.509672"
+ inkscape:cx="4.43599"
+ inkscape:cy="3.3311357"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0"
+ height="14px">
+ <inkscape:grid
+ type="xygrid"
+ id="grid856" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="crumb-separator-icon-square"
+ transform="translate(2.046,3.0914383)">
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect2017"
+ width="8"
+ height="8"
+ x="-2.046"
+ y="-3.0914383" />
+ <path
+ style="fill:#757575;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -0.046,-2.0914383 4,3 -4,3 z"
+ id="path1065"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect1449"
+ width="8"
+ height="8"
+ x="0"
+ y="0" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@2x.png
new file mode 100644
index 00000000..524872c1
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@3x.png
new file mode 100644
index 00000000..33bce847
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@4x.png
new file mode 100644
index 00000000..185c3eb7
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/crumb-separator-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.png
new file mode 100644
index 00000000..c2a4928d
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.svg
new file mode 100644
index 00000000..7ac6a23b
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round.svg
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="14"
+ height="18"
+ viewBox="0 0 14 18"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="file-icon-round.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path
+ style="fill:none;stroke:#757575;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 1.5003822,2.5063148 1.5000273,15.5 A 0.99997269,0.99997269 45.000782 0 0 2.5,16.5 h 9 a 1,1 135 0 0 1,-1 V 6.3440001 A 2.3479106,2.3479106 66.930332 0 0 11.778973,4.6510931 L 9.221027,2.192907 A 2.4805476,2.4805476 21.904486 0 0 7.5000004,1.5009022 l -4.9995913,0.00451 A 1.0009299,1.0009299 134.97494 0 0 1.5003822,2.5063148 Z"
+ id="path917"
+ sodipodi:nodetypes="cccccc"
+ inkscape:path-effect="#path-effect1367"
+ inkscape:original-d="M 1.5004095,1.5063148 1.5,16.5 h 11 V 5.3440001 L 8.5,1.5 Z" />
+ <path
+ id="path921"
+ style="fill:none;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 12.35,6.5 H 8.5 a 1,1 45 0 1 -1,-1 V 1.55"
+ inkscape:path-effect="#path-effect1365"
+ inkscape:original-d="M 12.35,6.5 H 7.5 V 1.55"
+ sodipodi:nodetypes="ccc" />
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8">
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1367"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1365"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="32.000001"
+ inkscape:cx="7.8906248"
+ inkscape:cy="9.7343747"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid856" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="file-icon-round"
+ transform="translate(2.046,3.0914383)" />
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect2270"
+ width="14"
+ height="18"
+ x="0"
+ y="0" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@2x.png
new file mode 100644
index 00000000..86af70d1
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@3x.png
new file mode 100644
index 00000000..06fea29e
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@4x.png
new file mode 100644
index 00000000..09f87873
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.png
new file mode 100644
index 00000000..9e8f3ddd
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.svg
new file mode 100644
index 00000000..107afa38
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square.svg
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="14"
+ height="18"
+ viewBox="0 0 14 18"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="file-icon-square.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path
+ style="fill:none;stroke:#757575;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 1.5004095,1.5063148 1.5,16.5 h 11 V 5.3440001 L 8.5,1.5 Z"
+ id="path917"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ style="fill:none;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 8.5,1.55 V 5.5"
+ id="path919"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#757575;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 12.45,5.5 H 8.5"
+ id="path921"
+ sodipodi:nodetypes="cc" />
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="22.627417"
+ inkscape:cx="0.022097087"
+ inkscape:cy="8.1538251"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid856" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="file-icon-square"
+ transform="translate(2.046,3.0914383)" />
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect1895"
+ width="14"
+ height="18"
+ x="0"
+ y="0" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@2x.png
new file mode 100644
index 00000000..8d330677
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@3x.png
new file mode 100644
index 00000000..2987d2ca
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@4x.png
new file mode 100644
index 00000000..69d76176
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/file-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.png
new file mode 100644
index 00000000..60bc6c72
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.svg
new file mode 100644
index 00000000..ba6b6276
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="18"
+ height="14"
+ viewBox="0 0 18 14"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="folder-icon-round.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path
+ style="fill:none;stroke:#757575;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 1.5003723,2.5063148 1.5000372,11.5 A 0.99996275,0.99996275 45.001067 0 0 2.5,12.5 h 13 a 1,1 135 0 0 1,-1 v -7 a 1,1 45 0 0 -1,-1 h -5 A 2.4142136,2.4142136 22.5 0 1 8.7928932,2.7928932 L 8.2071068,2.2071068 A 2.4106245,2.4106245 22.469847 0 0 6.5000006,1.5010525 l -3.9995917,0.00421 A 1.0010904,1.0010904 134.97091 0 0 1.5003723,2.5063148 Z"
+ id="path917"
+ sodipodi:nodetypes="ccccccc"
+ inkscape:path-effect="#path-effect1132"
+ inkscape:original-d="M 1.5004095,1.5063148 1.5,12.5 h 15 v -9 h -7 l -2,-2 z" />
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8">
+ <inkscape:path-effect
+ effect="fillet_chamfer"
+ id="path-effect1132"
+ is_visible="true"
+ lpeversion="1"
+ satellites_param="F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1 @ F,0,0,1,0,1,0,1"
+ unit="px"
+ method="auto"
+ mode="F"
+ radius="1"
+ chamfer_steps="1"
+ flexible="false"
+ use_knot_distance="true"
+ apply_no_radius="true"
+ apply_with_radius="true"
+ only_selected="false"
+ hide_knots="false" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="16"
+ inkscape:cx="-11.28125"
+ inkscape:cy="7.40625"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid856" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="folder-icon-round"
+ transform="translate(2.046,3.0914383)" />
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect2548"
+ width="18"
+ height="14"
+ x="0"
+ y="0" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@2x.png
new file mode 100644
index 00000000..e2873437
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@3x.png
new file mode 100644
index 00000000..5ee70a95
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@4x.png
new file mode 100644
index 00000000..47d28a4b
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.png
new file mode 100644
index 00000000..b84a90ae
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.svg
new file mode 100644
index 00000000..0002b5ac
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="18"
+ height="14"
+ viewBox="0 0 18 14"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="folder-icon-square.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path
+ style="fill:none;stroke:#757575;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 1.5004095,1.5063148 1.5,12.5 h 15 v -9 h -7 l -2,-2 z"
+ id="path917"
+ sodipodi:nodetypes="ccccccc" />
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="16"
+ inkscape:cx="-1.59375"
+ inkscape:cy="12.65625"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid856" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="folder-icon-square"
+ transform="translate(2.046,3.0914383)">
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect1423"
+ width="18"
+ height="14"
+ x="-2.046"
+ y="-3.0914383" />
+ </g>
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@2x.png
new file mode 100644
index 00000000..9bf320bb
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@3x.png
new file mode 100644
index 00000000..9f12edb6
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@4x.png
new file mode 100644
index 00000000..9ec5b389
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/folder-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled.9.png
new file mode 100644
index 00000000..ce48ee74
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.png
new file mode 100644
index 00000000..c7abb65c
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.png
new file mode 100644
index 00000000..46b84d7d
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.png
new file mode 100644
index 00000000..f4dfd338
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused.9.png
new file mode 100644
index 00000000..39fa8664
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.png
new file mode 100644
index 00000000..6b61562c
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.png
new file mode 100644
index 00000000..e46c0bf1
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.png
new file mode 100644
index 00000000..010444e8
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png
new file mode 100644
index 00000000..6f565e8b
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.png
new file mode 100644
index 00000000..9fd0a434
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.png
new file mode 100644
index 00000000..62b7435b
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.png
new file mode 100644
index 00000000..96444b89
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed.9.png
new file mode 100644
index 00000000..39fa8664
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.png
new file mode 100644
index 00000000..6b61562c
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.png
new file mode 100644
index 00000000..e46c0bf1
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.png
new file mode 100644
index 00000000..010444e8
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.9.png
new file mode 100644
index 00000000..cef1bafa
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.svg
new file mode 100644
index 00000000..acecfcc9
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background.svg
@@ -0,0 +1,358 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="210mm"
+ height="297mm"
+ viewBox="0 0 210 297"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="filedialogdelegate-background.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata122">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ objecttolerance="10.0"
+ gridtolerance="10.0"
+ guidetolerance="10.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:document-units="px"
+ showgrid="false"
+ inkscape:zoom="3.2715084"
+ inkscape:cx="24.912056"
+ inkscape:cy="54.867657"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g1413"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:document-rotation="0">
+ <sodipodi:guide
+ position="6.0854167,296.96676"
+ orientation="-1,0"
+ id="guide2310"
+ inkscape:label=""
+ inkscape:locked="true"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="3.363578,293.03125"
+ orientation="0,1"
+ id="guide2322"
+ inkscape:label=""
+ inkscape:locked="true"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="12.170833,297.68429"
+ orientation="-1,0"
+ id="guide2760"
+ inkscape:label=""
+ inkscape:locked="true"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="18.25625,297.91758"
+ orientation="-1,0"
+ id="guide949"
+ inkscape:label=""
+ inkscape:locked="true"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="24.341667,299.40187"
+ orientation="-1,0"
+ id="guide941"
+ inkscape:label=""
+ inkscape:locked="true"
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="30.427083,299.08734"
+ orientation="-1,0"
+ id="guide1165"
+ inkscape:label=""
+ inkscape:locked="false"
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="[9] filedialogdelegate-background-highlighted"
+ inkscape:groupmode="layer"
+ id="g1413"
+ style="display:inline"
+ sodipodi:insensitive="true">
+ <rect
+ style="fill:none;stroke-width:0.946642;stroke-linejoin:round"
+ id="rect1401"
+ width="6.0854168"
+ height="3.96875"
+ x="30.427084"
+ y="-6.9388939e-18" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:7.98967;stroke-linejoin:round"
+ id="rect1403"
+ width="0.26458332"
+ height="3.4395833"
+ x="30.427084"
+ y="0.26458332" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:10.1547;stroke-linejoin:round"
+ id="rect1405"
+ width="0.26458332"
+ height="5.5562401"
+ x="-0.26415125"
+ y="30.691679"
+ transform="matrix(3.6708238e-5,-1,1,1.4067378e-5,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect1407"
+ width="0.26458332"
+ height="0.26458287"
+ x="-3.9588995"
+ y="33.337505"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect1409"
+ width="0.26458332"
+ height="0.26458287"
+ x="-2.1059568"
+ y="36.247913"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#4fc1e9;fill-opacity:1;fill-rule:evenodd;stroke-width:0.263352"
+ id="rect1411"
+ width="5.5562501"
+ height="3.4395833"
+ x="30.691668"
+ y="0.26458332" />
+ </g>
+ <g
+ inkscape:label="[9] filedialogdelegate-background-focused"
+ inkscape:groupmode="layer"
+ id="g939"
+ style="display:inline"
+ sodipodi:insensitive="true">
+ <rect
+ style="fill:none;stroke-width:0.946642;stroke-linejoin:round"
+ id="rect927"
+ width="6.0854168"
+ height="3.96875"
+ x="18.25625"
+ y="-6.9388939e-18" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:7.98967;stroke-linejoin:round"
+ id="rect929"
+ width="0.26458332"
+ height="3.4395833"
+ x="18.25625"
+ y="0.26458332" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:10.1547;stroke-linejoin:round"
+ id="rect931"
+ width="0.26458332"
+ height="5.5562401"
+ x="-0.26432261"
+ y="18.520845"
+ transform="matrix(3.6708238e-5,-1,1,1.4067378e-5,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect933"
+ width="0.26458332"
+ height="0.26458287"
+ x="-3.9624968"
+ y="21.166674"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect935"
+ width="0.26458332"
+ height="0.26458287"
+ x="-2.1095541"
+ y="24.077082"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#e6e9ed;fill-opacity:1;fill-rule:evenodd;stroke-width:0.263352"
+ id="rect937"
+ width="5.5562501"
+ height="3.4395833"
+ x="18.520834"
+ y="0.26458332" />
+ </g>
+ <g
+ inkscape:label="[9] filedialogdelegate-background-pressed"
+ inkscape:groupmode="layer"
+ id="g867"
+ style="display:inline"
+ sodipodi:insensitive="true">
+ <rect
+ style="fill:none;stroke-width:0.946642;stroke-linejoin:round"
+ id="rect855"
+ width="6.0854168"
+ height="3.96875"
+ x="12.170834"
+ y="-6.9388939e-18" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:7.98967;stroke-linejoin:round"
+ id="rect857"
+ width="0.26458332"
+ height="3.4395833"
+ x="12.170834"
+ y="0.26458332" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:10.1547;stroke-linejoin:round"
+ id="rect859"
+ width="0.26458332"
+ height="5.5562401"
+ x="-0.26440823"
+ y="12.435429"
+ transform="matrix(3.6708238e-5,-1,1,1.4067378e-5,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect861"
+ width="0.26458332"
+ height="0.26458287"
+ x="-3.9642947"
+ y="15.081257"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect863"
+ width="0.26458332"
+ height="0.26458287"
+ x="-2.111352"
+ y="17.991665"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#e6e9ed;fill-opacity:1;fill-rule:evenodd;stroke-width:0.263352"
+ id="rect865"
+ width="5.5562501"
+ height="3.4395833"
+ x="12.435416"
+ y="0.26458332" />
+ </g>
+ <g
+ inkscape:label="[9] filedialogdelegate-background-disabled"
+ inkscape:groupmode="layer"
+ id="g2435"
+ style="display:inline"
+ sodipodi:insensitive="true">
+ <rect
+ style="fill:none;stroke-width:0.946642;stroke-linejoin:round"
+ id="rect2425"
+ width="6.0854168"
+ height="3.96875"
+ x="6.0854168"
+ y="-6.9388939e-18" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:7.98967;stroke-linejoin:round"
+ id="rect2427"
+ width="0.26458332"
+ height="3.4395833"
+ x="6.0854168"
+ y="0.26458332" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:10.1547;stroke-linejoin:round"
+ id="rect2429"
+ width="0.26458332"
+ height="5.5562401"
+ x="-0.264494"
+ y="6.3500099"
+ transform="matrix(3.6708238e-5,-1,1,1.4067378e-5,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect2431"
+ width="0.26458332"
+ height="0.26458287"
+ x="-3.9660921"
+ y="8.995841"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect2433"
+ width="0.26458332"
+ height="0.26458287"
+ x="-2.1131494"
+ y="11.906255"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#f5f7fa;fill-opacity:1;fill-rule:evenodd;stroke-width:0.263352"
+ id="rect124"
+ width="5.5562501"
+ height="3.4395833"
+ x="6.3499999"
+ y="0.26458332" />
+ </g>
+ <g
+ inkscape:label="[9] filedialogdelegate-background"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline"
+ sodipodi:insensitive="true">
+ <rect
+ style="fill:none;stroke-width:0.946642;stroke-linejoin:round"
+ id="rect846"
+ width="6.0854168"
+ height="3.96875"
+ x="0"
+ y="-6.9388939e-18" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:7.98967;stroke-linejoin:round"
+ id="rect1054"
+ width="0.26458332"
+ height="3.4395833"
+ x="0"
+ y="0.26458332" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:10.1547;stroke-linejoin:round"
+ id="rect1212"
+ width="0.26458332"
+ height="5.5562401"
+ x="-0.26457959"
+ y="0.26459303"
+ transform="matrix(3.6708238e-5,-1,1,1.4067378e-5,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect1505"
+ width="0.26458332"
+ height="0.26458287"
+ x="-3.9678898"
+ y="2.9104238"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.21594;stroke-linejoin:round"
+ id="rect1587"
+ width="0.26458332"
+ height="0.26458287"
+ x="-2.1149471"
+ y="5.8208375"
+ transform="matrix(1.7480114e-6,-1,0.99999996,2.9541491e-4,0,0)" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.0578;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3108"
+ width="5.5562501"
+ height="3.4395833"
+ x="0.26458332"
+ y="0.26458332" />
+ </g>
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@2x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@2x.9.png
new file mode 100644
index 00000000..5a136a0c
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@2x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@3x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@3x.9.png
new file mode 100644
index 00000000..f47a366b
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@3x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@4x.9.png b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@4x.9.png
new file mode 100644
index 00000000..9ecb680f
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/imagine/filedialogdelegate-background@4x.9.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.png
new file mode 100644
index 00000000..a4b7bc03
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.svg
new file mode 100644
index 00000000..fd053957
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round.svg
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ viewBox="0 0 16 16"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="up-icon-round.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="16"
+ inkscape:cx="-1.96875"
+ inkscape:cy="10.65625"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid1497" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="up-icon-round" />
+ <g
+ id="g1413"
+ transform="translate(0.3592822,-0.0034197)">
+ <path
+ style="fill:none;fill-opacity:1;stroke:#757575;stroke-width:2.23721;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 7.6407178,2.1165354 3e-7,11.7737686"
+ id="path20"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-opacity:1;stroke:#757575;stroke-width:2.23721;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 7.6407178,2.1165354 2.6616436,7.0956099"
+ id="path950"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-opacity:1;stroke:#757575;stroke-width:2.23721;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 7.6407178,2.1165354 12.619792,7.0956096"
+ id="path952"
+ sodipodi:nodetypes="cc" />
+ </g>
+ <rect
+ style="fill:none;stroke-width:5.33333;stroke-linejoin:round"
+ id="rect2897"
+ width="16"
+ height="16"
+ x="0"
+ y="0" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@2x.png
new file mode 100644
index 00000000..25ee970a
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@3x.png
new file mode 100644
index 00000000..486f2f46
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@4x.png
new file mode 100644
index 00000000..58540253
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.png
new file mode 100644
index 00000000..f7d4151a
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.svg
new file mode 100644
index 00000000..93fc45ba
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square.svg
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ viewBox="0 0 16 16"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="up-icon-square.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="11.313709"
+ inkscape:cx="-6.3197669"
+ inkscape:cy="28.416854"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid1497" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="up-icon-square" />
+ <path
+ style="fill:#757575;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 8.000315,1 -6,6 L 3,8 8,3 13,8 14,7 Z"
+ id="path928"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ style="fill:none;stroke-width:5.33333;stroke-linejoin:round"
+ id="rect2897"
+ width="16"
+ height="16"
+ x="0"
+ y="0" />
+ <rect
+ style="fill:#757575;fill-opacity:1;stroke:none;stroke-width:5.23075;stroke-linejoin:round"
+ id="rect4947"
+ width="1.5"
+ height="11"
+ x="7.2501574"
+ y="3" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@2x.png
new file mode 100644
index 00000000..2e2a3d94
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@3x.png
new file mode 100644
index 00000000..8965148c
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@4x.png
new file mode 100644
index 00000000..0a56bbcc
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.png
new file mode 100644
index 00000000..3f9f8762
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.svg b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.svg
new file mode 100644
index 00000000..dd20777b
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ viewBox="0 0 16 16"
+ version="1.1"
+ id="svg2"
+ inkscape:version="1.1-alpha (b0f32e08fc, 2021-03-07)"
+ sodipodi:docname="up-icon-thick-square.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1848"
+ inkscape:window-height="1016"
+ id="namedview6"
+ showgrid="true"
+ inkscape:zoom="22.627417"
+ inkscape:cx="2.3201941"
+ inkscape:cy="10.00998"
+ inkscape:window-x="72"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid1497" />
+ </sodipodi:namedview>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="up-icon-thick-square" />
+ <path
+ style="fill:#757575;fill-opacity:1;stroke:none;stroke-width:1.34164px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 2,8 6,-6 6,6 h -3 v 7 H 5 V 8 Z"
+ id="path1103"
+ sodipodi:nodetypes="cccccccc" />
+ <rect
+ style="fill:none;stroke-width:4;stroke-linejoin:round"
+ id="rect1748"
+ width="16"
+ height="16"
+ x="0"
+ y="0" />
+</svg>
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@2x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@2x.png
new file mode 100644
index 00000000..b4efe132
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@2x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@3x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@3x.png
new file mode 100644
index 00000000..2e8302b1
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@3x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@4x.png b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@4x.png
new file mode 100644
index 00000000..5cf05ab3
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/images/up-icon-thick-square@4x.png
Binary files differ
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialog.qml
new file mode 100644
index 00000000..59b15ee2
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialog.qml
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Fusion
+import QtQuick.Controls.Fusion.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+import "." as DialogsImpl
+
+FileDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitHeaderWidth,
+ implicitFooterWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ padding: 6
+ horizontalPadding: 12
+
+ standardButtons: T.Dialog.Open | T.Dialog.Cancel
+
+ /*
+ We use attached properties because we want to handle logic in C++, and:
+ - We can't assume the footer only contains a DialogButtonBox (which would allow us
+ to connect up to it in QQuickFileDialogImpl); it also needs to hold a ComboBox
+ and therefore the root footer item will be e.g. a layout item instead.
+ - We don't want to create our own "FileDialogButtonBox" (in order to be able to handle the logic
+ in C++) because we'd need to copy (and hence duplicate code in) DialogButtonBox.qml.
+ */
+ FileDialogImpl.buttonBox: buttonBox
+ FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
+ FileDialogImpl.fileDialogListView: fileDialogListView
+ FileDialogImpl.breadcrumbBar: breadcrumbBar
+
+ background: Rectangle {
+ implicitWidth: 600
+ implicitHeight: 400
+ color: control.palette.window
+ border.color: control.palette.mid
+ radius: 2
+
+ Rectangle {
+ z: -1
+ x: 1
+ y: 1
+ width: parent.width
+ height: parent.height
+ color: control.palette.shadow
+ opacity: 0.2
+ radius: 2
+ }
+ }
+
+ header: ColumnLayout {
+ spacing: 0
+
+ Label {
+ objectName: "dialogTitleBarLabel"
+ text: control.title
+ horizontalAlignment: Label.AlignHCenter
+ elide: Label.ElideRight
+ font.bold: true
+ padding: 6
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.topMargin: control.title.length > 0 ? 0 : 12
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 0
+ }
+
+ DialogsImpl.FolderBreadcrumbBar {
+ id: breadcrumbBar
+ fileDialog: control
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+
+ KeyNavigation.tab: fileDialogListView
+ }
+ }
+
+ contentItem: Frame {
+ padding: 0
+ verticalPadding: 1
+
+ ListView {
+ id: fileDialogListView
+ objectName: "fileDialogListView"
+ anchors.fill: parent
+ clip: true
+ focus: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ nameFilters: control.selectedNameFilter.globs
+ showDirsFirst: true
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ x: 1
+ width: ListView.view.width - 2
+ highlighted: ListView.isCurrentItem
+ fileDialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: nameFiltersComboBox
+ }
+ }
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 12
+
+ ComboBox {
+ // OK to use IDs here, since users shouldn't be overriding this stuff.
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ Layout.leftMargin: 12
+ Layout.fillWidth: true
+ Layout.bottomMargin: 12
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 6
+ horizontalPadding: 0
+ verticalPadding: 0
+ background: null
+
+ Layout.rightMargin: 12
+ Layout.bottomMargin: 12
+ }
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: Fusion.topShadow
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: Fusion.topShadow
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialogDelegate.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialogDelegate.qml
new file mode 100644
index 00000000..d4fd46b2
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FileDialogDelegate.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl as ControlsImpl
+import QtQuick.Controls.Fusion
+import QtQuick.Controls.Fusion.impl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FileDialogDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 6
+ spacing: 6
+
+ file: fileUrl
+
+ icon.width: 16
+ icon.height: 16
+ icon.color: highlighted ? palette.highlightedText : palette.text
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/"
+ + (fileIsDir ? "folder" : "file") + "-icon-round.png"
+
+ // We don't use index here, but in C++. Since we're using required
+ // properties, the index context property will not be injected, so we can't
+ // use its QQmlContext to access it.
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property int fileSize
+ required property date fileModified
+ required property bool fileIsDir
+
+ required property int fileDetailRowWidth
+
+ contentItem: FileDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.highlighted ? Fusion.highlightedText(control.palette) : control.palette.text
+ fileDetailRowWidth: control.fileDetailRowWidth
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 20
+ color: control.down ? Fusion.buttonColor(control.palette, false, true, true)
+ : control.highlighted ? Fusion.highlight(control.palette) : control.palette.base
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FolderBreadcrumbBar.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FolderBreadcrumbBar.qml
new file mode 100644
index 00000000..cafb975b
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Fusion/FolderBreadcrumbBar.qml
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FolderBreadcrumbBar {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + (upButton ? upButton.implicitWidth + upButtonSpacing : 0)
+ + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+ upButtonSpacing: 6
+
+ contentItem: ListView {
+ id: listView
+ currentIndex: control.currentIndex
+ model: control.contentModel
+ orientation: ListView.Horizontal
+ snapMode: ListView.SnapToItem
+ highlightMoveDuration: 0
+ interactive: false
+ clip: true
+
+ Rectangle {
+ anchors.fill: parent
+ color: control.palette.light
+ border.color: control.palette.mid
+ radius: 2
+ z: -1
+ }
+ }
+ buttonDelegate: Button {
+ id: buttonDelegateRoot
+ text: folderName
+ flat: true
+
+ // The default of 100 is a bit too wide for short directory names.
+ Binding {
+ target: buttonDelegateRoot.background
+ property: "implicitWidth"
+ value: 24
+ }
+
+ required property int index
+ required property string folderName
+ }
+ separatorDelegate: IconImage {
+ id: iconImage
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/crumb-separator-icon-round.png"
+ sourceSize: Qt.size(8, 8)
+ width: 8 + 6
+ height: control.contentItem.height
+ color: control.palette.dark
+ y: (control.height - height) / 2
+ }
+ upButton: Button {
+ x: control.leftPadding
+ y: control.topPadding
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/up-icon-round.png"
+ icon.width: 16
+ icon.height: 16
+ width: height
+ height: Math.max(implicitHeight, control.contentItem.height)
+ focusPolicy: Qt.TabFocus
+ }
+ textField: TextField {
+ text: control.fileDialog.selectedFile
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialog.qml
new file mode 100644
index 00000000..3afe1e7e
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialog.qml
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.Imagine
+import QtQuick.Controls.Imagine.impl
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+import "." as DialogsImpl
+
+FileDialogImpl {
+ id: control
+
+ // Can't set implicitWidth of the NinePatchImage background, so we do it here.
+ implicitWidth: Math.max(600,
+ implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitHeaderWidth,
+ implicitFooterWidth)
+ implicitHeight: Math.max(400,
+ implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ topPadding: background ? background.topPadding : 0
+ leftPadding: background ? background.leftPadding : 0
+ rightPadding: background ? background.rightPadding : 0
+ bottomPadding: background ? background.bottomPadding : 0
+
+ topInset: background ? -background.topInset || 0 : 0
+ leftInset: background ? -background.leftInset || 0 : 0
+ rightInset: background ? -background.rightInset || 0 : 0
+ bottomInset: background ? -background.bottomInset || 0 : 0
+
+ standardButtons: T.Dialog.Open | T.Dialog.Cancel
+
+ FileDialogImpl.buttonBox: buttonBox
+ FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
+ FileDialogImpl.fileDialogListView: fileDialogListView
+ FileDialogImpl.breadcrumbBar: breadcrumbBar
+
+ background: NinePatchImage {
+ source: Imagine.url + "dialog-background"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": control.modal},
+ {"dim": control.dim}
+ ]
+ }
+ }
+
+ header: ColumnLayout {
+ spacing: 12
+
+ Label {
+ text: control.title
+ elide: Label.ElideRight
+ font.bold: true
+
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.topMargin: 12
+ Layout.fillWidth: true
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 0
+
+ background: NinePatchImage {
+ width: parent.width
+ height: parent.height
+
+ source: Imagine.url + "dialog-title"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": control.modal},
+ {"dim": control.dim}
+ ]
+ }
+ }
+ }
+
+ DialogsImpl.FolderBreadcrumbBar {
+ id: breadcrumbBar
+ fileDialog: control
+
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.fillWidth: true
+ Layout.maximumWidth: parent.width - 28
+ }
+ }
+
+ contentItem: ListView {
+ id: fileDialogListView
+ objectName: "fileDialogListView"
+ clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ nameFilters: control.selectedNameFilter.globs
+ showDirsFirst: true
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ fileDialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+ }
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 20
+
+ ComboBox {
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ Layout.leftMargin: 16
+ Layout.bottomMargin: 16
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+
+ Layout.bottomMargin: 16
+ Layout.rightMargin: 16
+ }
+ }
+
+ T.Overlay.modal: NinePatchImage {
+ source: Imagine.url + "dialog-overlay"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": true}
+ ]
+ }
+ }
+
+ T.Overlay.modeless: NinePatchImage {
+ source: Imagine.url + "dialog-overlay"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": false}
+ ]
+ }
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialogDelegate.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialogDelegate.qml
new file mode 100644
index 00000000..1db2ab4e
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FileDialogDelegate.qml
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick.Controls
+import QtQuick.Controls.Imagine
+import QtQuick.Controls.Imagine.impl
+import QtQuick.Controls.impl as ControlsImpl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FileDialogDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ spacing: 12
+
+ topPadding: background ? background.topPadding : 0
+ leftPadding: background ? background.leftPadding : 0
+ rightPadding: background ? background.rightPadding : 0
+ bottomPadding: background ? background.bottomPadding : 0
+
+ topInset: background ? -background.topInset || 0 : 0
+ leftInset: background ? -background.leftInset || 0 : 0
+ rightInset: background ? -background.rightInset || 0 : 0
+ bottomInset: background ? -background.bottomInset || 0 : 0
+
+ file: fileUrl
+
+ icon.width: 16
+ icon.height: 16
+ icon.color: highlighted ? palette.highlightedText : palette.text
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/"
+ + (fileIsDir ? "folder" : "file") + "-icon-round.png"
+
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property int fileSize
+ required property date fileModified
+ required property bool fileIsDir
+
+ required property int fileDetailRowWidth
+
+ contentItem: FileDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.icon.color
+ fileDetailRowWidth: control.fileDetailRowWidth
+ }
+
+ background: NinePatchImage {
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/imagine/filedialogdelegate-background"
+ NinePatchImageSelector on source {
+ states: [
+ { "disabled": !control.enabled },
+ { "pressed": control.down },
+ { "focused": control.visualFocus },
+ { "highlighted": control.highlighted },
+ { "mirrored": control.mirrored },
+ { "hovered": control.hovered }
+ ]
+ }
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FolderBreadcrumbBar.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FolderBreadcrumbBar.qml
new file mode 100644
index 00000000..760d41c8
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Imagine/FolderBreadcrumbBar.qml
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FolderBreadcrumbBar {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + (upButton ? upButton.implicitWidth + upButtonSpacing : 0)
+ + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+ upButtonSpacing: 20
+ padding: 1
+
+ background: Rectangle {}
+ contentItem: ListView {
+ id: listView
+ currentIndex: control.currentIndex
+ model: control.contentModel
+ orientation: ListView.Horizontal
+ snapMode: ListView.SnapToItem
+ highlightMoveDuration: 0
+ interactive: false
+ clip: true
+ }
+ buttonDelegate: Button {
+ id: buttonDelegateRoot
+ text: folderName
+ flat: true
+
+ required property int index
+ required property string folderName
+ }
+ separatorDelegate: IconImage {
+ id: iconImage
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/crumb-separator-icon-round.png"
+ sourceSize: Qt.size(8, 8)
+ width: 8
+ height: control.contentItem.height
+ y: (control.height - height) / 2
+ }
+ upButton: ToolButton {
+ x: control.leftPadding
+ y: control.topPadding
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/up-icon-thick-square.png"
+ icon.width: 16
+ icon.height: 16
+ focusPolicy: Qt.TabFocus
+ }
+ textField: TextField {
+ text: control.fileDialog.selectedFile
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialog.qml
new file mode 100644
index 00000000..27599d77
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialog.qml
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Material
+import QtQuick.Controls.Material.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+import "." as DialogsImpl
+
+FileDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitFooterWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ leftPadding: 24
+ rightPadding: 24
+
+ standardButtons: T.Dialog.Open | T.Dialog.Cancel
+
+ Material.elevation: 24
+
+ FileDialogImpl.buttonBox: buttonBox
+ FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
+ FileDialogImpl.fileDialogListView: fileDialogListView
+ FileDialogImpl.breadcrumbBar: breadcrumbBar
+
+ background: Rectangle {
+ implicitWidth: 600
+ implicitHeight: 400
+ radius: 2
+ color: control.Material.dialogColor
+
+ layer.enabled: control.Material.elevation > 0
+ layer.effect: ElevationEffect {
+ elevation: control.Material.elevation
+ }
+ }
+
+ header: ColumnLayout {
+ spacing: 12
+
+ Label {
+ text: control.title
+ visible: control.title.length > 0
+ elide: Label.ElideRight
+ font.bold: true
+ font.pixelSize: 16
+
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ Layout.topMargin: 24
+ Layout.fillWidth: true
+ }
+
+ DialogsImpl.FolderBreadcrumbBar {
+ id: breadcrumbBar
+ fileDialog: control
+
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ Layout.fillWidth: true
+ Layout.maximumWidth: parent.width - 48
+ }
+ }
+
+ contentItem: ListView {
+ id: fileDialogListView
+ objectName: "fileDialogListView"
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ nameFilters: control.selectedNameFilter.globs
+ showDirsFirst: true
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ fileDialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+ }
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 20
+
+ ComboBox {
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ Layout.leftMargin: 20
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ horizontalPadding: 0
+ verticalPadding: 20
+
+ Layout.rightMargin: 20
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.5)
+ }
+
+ Overlay.modeless: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.12)
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialogDelegate.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialogDelegate.qml
new file mode 100644
index 00000000..747165bf
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FileDialogDelegate.qml
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Controls.Material
+import QtQuick.Controls.Material.impl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FileDialogDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 16
+ verticalPadding: 8
+ spacing: 16
+
+ icon.width: 16
+ icon.height: 16
+ icon.color: enabled ? Material.foreground : Material.hintTextColor
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/"
+ + (fileIsDir ? "folder" : "file") + "-icon-square.png"
+
+ file: fileUrl
+
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property int fileSize
+ required property date fileModified
+ required property bool fileIsDir
+
+ required property int fileDetailRowWidth
+
+ contentItem: FileDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.Material.hintTextColor
+ fileDetailRowWidth: control.fileDetailRowWidth
+ }
+
+ background: Rectangle {
+ implicitHeight: control.Material.delegateHeight
+
+ color: control.highlighted ? Color.transparent(control.Material.accentColor, 0.08) : "transparent"
+
+ Ripple {
+ width: parent.width
+ height: parent.height
+
+ clip: visible
+ pressed: control.pressed
+ anchor: control
+ active: control.down || control.visualFocus || control.hovered
+ color: control.highlighted ? control.Material.highlightedRippleColor : control.Material.rippleColor
+ }
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FolderBreadcrumbBar.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FolderBreadcrumbBar.qml
new file mode 100644
index 00000000..a686f39e
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Material/FolderBreadcrumbBar.qml
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Material
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FolderBreadcrumbBar {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + (upButton ? upButton.implicitWidth + upButtonSpacing : 0)
+ + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+ upButtonSpacing: 20
+ padding: 1
+
+ background: Rectangle {}
+ contentItem: ListView {
+ id: listView
+ currentIndex: control.currentIndex
+ model: control.contentModel
+ orientation: ListView.Horizontal
+ snapMode: ListView.SnapToItem
+ highlightMoveDuration: 0
+ interactive: false
+ clip: true
+ }
+ buttonDelegate: Button {
+ id: buttonDelegateRoot
+ text: folderName
+ flat: true
+ font.capitalization: Font.MixedCase
+
+ // The default of 100 is a bit too wide for short directory names.
+ Binding {
+ target: buttonDelegateRoot.background
+ property: "implicitWidth"
+ value: control.Material.buttonHeight
+ }
+
+ required property int index
+ required property string folderName
+ }
+ separatorDelegate: IconImage {
+ id: iconImage
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/crumb-separator-icon-square.png"
+ sourceSize: Qt.size(8, 8)
+ // The image is 8x8, and add 2 px padding on each side.
+ width: 8 + 4
+ height: control.contentItem.height
+ color: control.Material.hintTextColor
+ y: (control.height - height) / 2
+ }
+ upButton: ToolButton {
+ x: control.leftPadding
+ y: control.topPadding
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/up-icon-thick-square.png"
+ icon.width: 16
+ icon.height: 16
+ width: height
+ focusPolicy: Qt.TabFocus
+ }
+ textField: TextField {
+ text: control.fileDialog.selectedFile
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialog.qml
new file mode 100644
index 00000000..7698089e
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialog.qml
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Universal
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+import "." as DialogsImpl
+
+FileDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitHeaderWidth,
+ implicitFooterWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ padding: 24
+ verticalPadding: 18
+
+ standardButtons: T.Dialog.Open | T.Dialog.Cancel
+
+ FileDialogImpl.buttonBox: buttonBox
+ FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
+ FileDialogImpl.fileDialogListView: fileDialogListView
+ FileDialogImpl.breadcrumbBar: breadcrumbBar
+
+ background: Rectangle {
+ implicitWidth: 600
+ implicitHeight: 400
+ color: control.Universal.chromeMediumLowColor
+ border.color: control.Universal.chromeHighColor
+ border.width: 1 // FlyoutBorderThemeThickness
+ }
+
+ header: ColumnLayout {
+ spacing: 12
+
+ Label {
+ text: control.title
+ elide: Label.ElideRight
+ // TODO: QPlatformTheme::TitleBarFont
+ font.pixelSize: 20
+
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ Layout.topMargin: 18
+ Layout.fillWidth: true
+ Layout.preferredHeight: control.title.length > 0 ? implicitHeight : 0
+
+ background: Rectangle {
+ x: 1; y: 1 // // FlyoutBorderThemeThickness
+ color: control.Universal.chromeMediumLowColor
+ width: parent.width - 2
+ height: parent.height - 1
+ }
+ }
+
+ DialogsImpl.FolderBreadcrumbBar {
+ id: breadcrumbBar
+ fileDialog: control
+
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ Layout.fillWidth: true
+ Layout.maximumWidth: parent.width - 48
+ }
+ }
+
+ contentItem: ListView {
+ id: fileDialogListView
+ objectName: "fileDialogListView"
+ clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ nameFilters: control.selectedNameFilter.globs
+ showDirsFirst: true
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ fileDialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+ }
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 24
+
+ ComboBox {
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ Layout.leftMargin: 24
+ Layout.fillWidth: true
+ Layout.topMargin: 6
+ Layout.bottomMargin: 24
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ horizontalPadding: 0
+
+ Layout.rightMargin: 24
+ }
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialogDelegate.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialogDelegate.qml
new file mode 100644
index 00000000..6f8bd441
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FileDialogDelegate.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Controls.Universal
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FileDialogDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ spacing: 12
+
+ padding: 12
+ topPadding: padding - 1
+ bottomPadding: padding + 1
+
+ icon.width: 20
+ icon.height: 20
+ icon.color: Color.transparent(Universal.foreground, enabled ? 1.0 : 0.2)
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/"
+ + (fileIsDir ? "folder" : "file") + "-icon-square.png"
+
+ file: fileUrl
+
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property int fileSize
+ required property date fileModified
+ required property bool fileIsDir
+
+ required property int fileDetailRowWidth
+
+ contentItem: FileDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.icon.color
+ fileDetailRowWidth: control.fileDetailRowWidth
+ }
+
+ background: Rectangle {
+ visible: control.down || control.highlighted || control.visualFocus || control.hovered
+ color: control.down ? control.Universal.listMediumColor :
+ control.hovered ? control.Universal.listLowColor : control.Universal.altMediumLowColor
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ visible: control.visualFocus || control.highlighted
+ color: control.Universal.accent
+ opacity: control.Universal.theme === Universal.Light ? 0.4 : 0.6
+ }
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FolderBreadcrumbBar.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FolderBreadcrumbBar.qml
new file mode 100644
index 00000000..1ea32f61
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/+Universal/FolderBreadcrumbBar.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Universal
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FolderBreadcrumbBar {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + (upButton ? upButton.implicitWidth + upButtonSpacing : 0)
+ + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+ upButtonSpacing: 20
+ padding: 1
+
+ background: Rectangle {}
+ contentItem: ListView {
+ id: listView
+ currentIndex: control.currentIndex
+ model: control.contentModel
+ orientation: ListView.Horizontal
+ snapMode: ListView.SnapToItem
+ highlightMoveDuration: 0
+ interactive: false
+ clip: true
+ }
+ buttonDelegate: ToolButton {
+ id: buttonDelegateRoot
+ text: folderName
+
+ // The default is a bit too wide for short directory names.
+ Binding {
+ target: buttonDelegateRoot.background
+ property: "implicitWidth"
+ value: 48
+ }
+
+ required property int index
+ required property string folderName
+ }
+ separatorDelegate: IconImage {
+ id: iconImage
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/crumb-separator-icon-square.png"
+ sourceSize: Qt.size(8, 8)
+ // The image is 8x8, and add 2 px padding on each side.
+ width: 8 + 4
+ height: control.contentItem.height
+ color: Color.transparent(control.Universal.foreground, enabled ? 1.0 : 0.2)
+ y: (control.height - height) / 2
+ }
+ upButton: ToolButton {
+ x: control.leftPadding
+ y: control.topPadding
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/up-icon-square.png"
+ icon.width: 16
+ icon.height: 16
+ width: height
+ focusPolicy: Qt.TabFocus
+ }
+ textField: TextField {
+ text: control.fileDialog.selectedFile
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialog.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialog.qml
new file mode 100644
index 00000000..946a55fa
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialog.qml
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+import "." as DialogsImpl
+
+FileDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitHeaderWidth,
+ implicitFooterWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ leftPadding: 20
+ rightPadding: 20
+ // Ensure that the background's border is visible.
+ leftInset: -1
+ rightInset: -1
+ topInset: -1
+ bottomInset: -1
+
+ standardButtons: T.Dialog.Open | T.Dialog.Cancel
+
+ /*
+ We use attached properties because we want to handle logic in C++, and:
+ - We can't assume the footer only contains a DialogButtonBox (which would allow us
+ to connect up to it in QQuickFileDialogImpl); it also needs to hold a ComboBox
+ and therefore the root footer item will be e.g. a layout item instead.
+ - We don't want to create our own "FileDialogButtonBox" (in order to be able to handle the logic
+ in C++) because we'd need to copy (and hence duplicate code in) DialogButtonBox.qml.
+ */
+ FileDialogImpl.buttonBox: buttonBox
+ FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox
+ FileDialogImpl.fileDialogListView: fileDialogListView
+ FileDialogImpl.breadcrumbBar: breadcrumbBar
+
+ background: Rectangle {
+ implicitWidth: 600
+ implicitHeight: 400
+ color: control.palette.window
+ border.color: control.palette.dark
+ }
+
+ header: Pane {
+ palette.window: control.palette.light
+ padding: 20
+
+ contentItem: Column {
+ spacing: 12
+
+ Label {
+ objectName: "dialogTitleBarLabel"
+ width: parent.width
+ text: control.title
+ visible: control.title.length > 0
+ horizontalAlignment: Label.AlignHCenter
+ elide: Label.ElideRight
+ font.bold: true
+ }
+
+ DialogsImpl.FolderBreadcrumbBar {
+ id: breadcrumbBar
+ width: parent.width
+ fileDialog: control
+
+ KeyNavigation.tab: fileDialogListView
+ }
+ }
+ }
+
+ contentItem: ListView {
+ id: fileDialogListView
+ objectName: "fileDialogListView"
+ clip: true
+ focus: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ nameFilters: control.selectedNameFilter.globs
+ showDirsFirst: true
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ fileDialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: nameFiltersComboBox
+ }
+ }
+
+ footer: Rectangle {
+ color: control.palette.light
+ implicitWidth: rowLayout.implicitWidth
+ implicitHeight: rowLayout.implicitHeight
+
+ RowLayout {
+ id: rowLayout
+ width: parent.width
+ height: parent.height
+ spacing: 20
+
+ ComboBox {
+ // OK to use IDs here, since users shouldn't be overriding this stuff.
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ Layout.leftMargin: 20
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ palette.window: control.palette.light
+ spacing: 12
+ horizontalPadding: 0
+ verticalPadding: 20
+
+ Layout.rightMargin: 20
+ }
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.5)
+ }
+
+ Overlay.modeless: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.12)
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegate.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegate.qml
new file mode 100644
index 00000000..5692d132
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegate.qml
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl as ControlsImpl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FileDialogDelegate {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ implicitIndicatorHeight + topPadding + bottomPadding)
+
+ padding: 12
+ spacing: 8
+ topPadding: 0
+ bottomPadding: 0
+
+ file: fileUrl
+
+ icon.width: 16
+ icon.height: 16
+ icon.color: highlighted ? palette.highlightedText : palette.text
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/"
+ + (fileIsDir ? "folder" : "file") + "-icon-round.png"
+
+ // We don't use index here, but in C++. Since we're using required
+ // properties, the index context property will not be injected, so we can't
+ // use its QQmlContext to access it.
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property int fileSize
+ required property date fileModified
+ required property bool fileIsDir
+
+ required property int fileDetailRowWidth
+
+ contentItem: FileDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.icon.color
+ fileDetailRowWidth: control.fileDetailRowWidth
+ }
+
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 40
+ visible: control.down || control.highlighted || control.visualFocus
+ color: Color.blend(control.down ? control.palette.midlight : control.palette.light,
+ control.palette.highlight, control.highlighted ? 0.15 : 0.0)
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegateLabel.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegateLabel.qml
new file mode 100644
index 00000000..56b031d1
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/FileDialogDelegateLabel.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+/*
+ Most of the elements in here are the same between styles, so we
+ have a reusable component for it and provide some properties to enable style-specific tweaks.
+*/
+Item {
+ id: root
+ implicitWidth: column.implicitWidth
+ implicitHeight: column.implicitHeight
+
+ required property DialogsQuickImpl.FileDialogDelegate delegate
+ required property int fileDetailRowWidth
+
+ property color fileDetailRowTextColor
+
+ Column {
+ id: column
+ y: (parent.height - height) / 2
+
+ Row {
+ spacing: root.delegate.spacing
+
+ IconImage {
+ id: iconImage
+ source: root.delegate.icon.source
+ sourceSize: Qt.size(root.delegate.icon.width, root.delegate.icon.height)
+ width: root.delegate.icon.width
+ height: root.delegate.icon.height
+ color: root.delegate.icon.color
+ y: (parent.height - height) / 2
+ }
+ Label {
+ text: root.delegate.fileName
+ color: root.delegate.icon.color
+ y: (parent.height - height) / 2
+ }
+ }
+
+ Item {
+ id: fileDetailRow
+ x: iconImage.width + root.delegate.spacing
+ width: fileDetailRowWidth - x - root.delegate.leftPadding
+ implicitHeight: childrenRect.height
+
+ Label {
+ text: locale.formattedDataSize(root.delegate.fileSize)
+ font.pixelSize: root.delegate.font.pixelSize * 0.75
+ color: root.fileDetailRowTextColor
+ }
+ Label {
+ text: Qt.formatDateTime(root.delegate.fileModified)
+ font.pixelSize: root.delegate.font.pixelSize * 0.75
+ color: root.fileDetailRowTextColor
+ x: parent.width - width
+ }
+ }
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qml/FolderBreadcrumbBar.qml b/src/quickdialogs2/quickdialogs2quickimpl/qml/FolderBreadcrumbBar.qml
new file mode 100644
index 00000000..25e11e71
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qml/FolderBreadcrumbBar.qml
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs.quickimpl as DialogsQuickImpl
+
+DialogsQuickImpl.FolderBreadcrumbBar {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + (upButton ? upButton.implicitWidth + upButtonSpacing : 0)
+ + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+ upButtonSpacing: 20
+ padding: 1
+
+ background: Rectangle {
+ border.color: control.palette.button
+ }
+ contentItem: ListView {
+ id: listView
+ currentIndex: control.currentIndex
+ model: control.contentModel
+ orientation: ListView.Horizontal
+ snapMode: ListView.SnapToItem
+ highlightMoveDuration: 0
+ interactive: false
+ clip: true
+ }
+ buttonDelegate: Button {
+ id: buttonDelegateRoot
+ text: folderName
+ flat: true
+
+ // The default of 100 is a bit too wide for short directory names.
+ Binding {
+ target: buttonDelegateRoot.background
+ property: "implicitWidth"
+ value: 40
+ }
+
+ required property int index
+ required property string folderName
+ }
+ separatorDelegate: IconImage {
+ id: iconImage
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/crumb-separator-icon-round.png"
+ sourceSize: Qt.size(8, 8)
+ width: 8
+ height: control.contentItem.height
+ color: control.palette.button
+ y: (control.height - height) / 2
+ }
+ upButton: ToolButton {
+ x: control.leftPadding
+ y: control.topPadding
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/up-icon-round.png"
+ icon.width: 16
+ icon.height: 16
+ width: height
+ focusPolicy: Qt.TabFocus
+ }
+ textField: TextField {
+ text: control.fileDialog.selectedFile
+ }
+}
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
new file mode 100644
index 00000000..f56466e0
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickdialogimplfactory_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qquickplatformfiledialog_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ Creates concrete QML-based dialogs.
+*/
+
+Q_LOGGING_CATEGORY(lcQuickDialogImplFactory, "qt.quick.dialogs.quickdialogimplfactory")
+
+QPlatformDialogHelper *QQuickDialogImplFactory::createPlatformDialogHelper(
+ QPlatformTheme::DialogType type, QObject *parent)
+{
+ switch (type) {
+ case QPlatformTheme::FileDialog: {
+ auto dialog = new QQuickPlatformFileDialog(parent);
+ // If the QML file failed to load, we need to handle it gracefully.
+ if (!dialog->isValid()) {
+ delete dialog;
+ return nullptr;
+ }
+
+ return dialog;
+ } default:
+ break;
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory_p.h
new file mode 100644
index 00000000..a4ca1cca
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickdialogimplfactory_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKQMLDIALOGFACTORY_P_H
+#define QQUICKQMLDIALOGFACTORY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtGui/qpa/qplatformtheme.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickDialogImplFactory
+{
+public:
+ static QPlatformDialogHelper *createPlatformDialogHelper(QPlatformTheme::DialogType type,
+ QObject *parent);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKQMLDIALOGFACTORY_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate.cpp
new file mode 100644
index 00000000..8964772c
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickfiledialogdelegate_p.h"
+
+#include <QtCore/qfileinfo.h>
+#include <QtGui/qpa/qplatformtheme.h>
+#include <QtQml/QQmlFile>
+#include <QtQml/qqmlexpression.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuickTemplates2/private/qquickitemdelegate_p_p.h>
+
+#include "qquickfiledialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileDialogDelegatePrivate : public QQuickItemDelegatePrivate
+{
+ Q_DECLARE_PUBLIC(QQuickFileDialogDelegate)
+
+public:
+ void highlightFile();
+ void chooseFile();
+
+ bool acceptKeyClick(Qt::Key key) const override;
+
+ QQuickFileDialogImpl *fileDialog = nullptr;
+ QUrl file;
+};
+
+void QQuickFileDialogDelegatePrivate::highlightFile()
+{
+ Q_Q(QQuickFileDialogDelegate);
+ QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickListView>(q));
+ if (!attached)
+ return;
+
+ QQmlContext *delegateContext = qmlContext(q);
+ if (!delegateContext)
+ return;
+
+ bool converted = false;
+ const int index = q->property("index").toInt(&converted);
+ if (converted) {
+ attached->view()->setCurrentIndex(index);
+ fileDialog->setCurrentFile(file);
+ }
+}
+
+void QQuickFileDialogDelegatePrivate::chooseFile()
+{
+ const QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(file));
+ if (fileInfo.isDir()) {
+ // If it's a directory, navigate to it.
+ fileDialog->setCurrentFolder(file);
+ } else {
+ // Otherwise it's a file, so select it and close the dialog.
+ fileDialog->setSelectedFile(file);
+ fileDialog->accept();
+ }
+}
+
+bool QQuickFileDialogDelegatePrivate::acceptKeyClick(Qt::Key key) const
+{
+ return key == Qt::Key_Return || key == Qt::Key_Enter;
+}
+
+QQuickFileDialogDelegate::QQuickFileDialogDelegate(QQuickItem *parent)
+ : QQuickItemDelegate(*(new QQuickFileDialogDelegatePrivate), parent)
+{
+ Q_D(QQuickFileDialogDelegate);
+ // Clicking and tabbing should result in it getting focus,
+ // as e.g. Ubuntu and Windows both allow tabbing through file dialogs.
+ setFocusPolicy(Qt::StrongFocus);
+ setCheckable(true);
+ QObjectPrivate::connect(this, &QQuickFileDialogDelegate::clicked,
+ d, &QQuickFileDialogDelegatePrivate::highlightFile);
+ QObjectPrivate::connect(this, &QQuickFileDialogDelegate::doubleClicked,
+ d, &QQuickFileDialogDelegatePrivate::chooseFile);
+}
+
+QQuickFileDialogImpl *QQuickFileDialogDelegate::fileDialog() const
+{
+ Q_D(const QQuickFileDialogDelegate);
+ return d->fileDialog;
+}
+
+void QQuickFileDialogDelegate::setFileDialog(QQuickFileDialogImpl *fileDialog)
+{
+ Q_D(QQuickFileDialogDelegate);
+ if (fileDialog == d->fileDialog)
+ return;
+
+ d->fileDialog = fileDialog;
+ emit fileDialogChanged();
+}
+
+QUrl QQuickFileDialogDelegate::file() const
+{
+ Q_D(const QQuickFileDialogDelegate);
+ return d->file;
+}
+
+void QQuickFileDialogDelegate::setFile(const QUrl &file)
+{
+ Q_D(QQuickFileDialogDelegate);
+ if (file == d->file)
+ return;
+
+ d->file = file;
+ emit fileChanged();
+}
+
+void QQuickFileDialogDelegate::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QQuickFileDialogDelegate);
+ // We need to respond to being triggered by enter being pressed,
+ // but we can't use event->isAccepted() to check, because events are pre-accepted.
+ auto connection = QObjectPrivate::connect(this, &QQuickFileDialogDelegate::clicked,
+ d, &QQuickFileDialogDelegatePrivate::chooseFile);
+
+ QQuickItemDelegate::keyReleaseEvent(event);
+
+ disconnect(connection);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickfiledialogdelegate_p.cpp"
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate_p.h
new file mode 100644
index 00000000..e1f6c169
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogdelegate_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFILEDIALOGDELEGATE_P_H
+#define QQUICKFILEDIALOGDELEGATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickTemplates2/private/qquickitemdelegate_p.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileDialogImpl;
+class QQuickFileDialogDelegatePrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogDelegate : public QQuickItemDelegate
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickFileDialogImpl *fileDialog READ fileDialog WRITE setFileDialog NOTIFY fileDialogChanged)
+ Q_PROPERTY(QUrl file READ file WRITE setFile NOTIFY fileChanged)
+ QML_NAMED_ELEMENT(FileDialogDelegate)
+ QML_ADDED_IN_VERSION(6, 2)
+
+public:
+ explicit QQuickFileDialogDelegate(QQuickItem *parent = nullptr);
+
+ QQuickFileDialogImpl *fileDialog() const;
+ void setFileDialog(QQuickFileDialogImpl *fileDialog);
+
+ QUrl file() const;
+ void setFile(const QUrl &file);
+
+Q_SIGNALS:
+ void fileDialogChanged();
+ void fileChanged();
+
+protected:
+ void keyReleaseEvent(QKeyEvent *event) override;
+
+private:
+ Q_DISABLE_COPY(QQuickFileDialogDelegate)
+ Q_DECLARE_PRIVATE(QQuickFileDialogDelegate)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFileDialogDelegate)
+
+#endif // QQUICKFILEDIALOGDELEGATE_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
new file mode 100644
index 00000000..e50305f6
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl.cpp
@@ -0,0 +1,564 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickfiledialogimpl_p.h"
+#include "qquickfiledialogimpl_p_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
+#include "qquickfiledialogdelegate_p.h"
+#include "qquickfolderbreadcrumbbar_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcCurrentFolder, "qt.quick.dialogs.quickfiledialogimpl.currentFolder")
+Q_LOGGING_CATEGORY(lcOptions, "qt.quick.dialogs.quickfiledialogimpl.options")
+Q_LOGGING_CATEGORY(lcNameFilters, "qt.quick.dialogs.quickfiledialogimpl.namefilters")
+Q_LOGGING_CATEGORY(lcAttachedNameFilters, "qt.quick.dialogs.quickfiledialogimplattached.namefilters")
+
+QQuickFileDialogImplPrivate::QQuickFileDialogImplPrivate()
+{
+}
+
+void QQuickFileDialogImplPrivate::setNameFilters(const QStringList &filters)
+{
+ Q_Q(QQuickFileDialogImpl);
+ if (filters == nameFilters)
+ return;
+
+ nameFilters = filters;
+ emit q->nameFiltersChanged();
+}
+
+void QQuickFileDialogImplPrivate::updateEnabled()
+{
+ Q_Q(QQuickFileDialogImpl);
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ if (!attached)
+ return;
+
+ auto openButton = attached->buttonBox()->standardButton(QPlatformDialogHelper::Open);
+ if (!openButton) {
+ qmlWarning(q).nospace() << "Can't update Open button's enabled state because it wasn't found";
+ return;
+ }
+
+ openButton->setEnabled(!currentFile.isEmpty() && attached->breadcrumbBar()
+ && !attached->breadcrumbBar()->textField()->isVisible());
+}
+
+/*!
+ \internal
+
+ Ensures that a file is always selected after a change in \c folder.
+
+ \a oldFolderPath is the previous value of \c folder.
+*/
+void QQuickFileDialogImplPrivate::updateCurrentFile(const QString &oldFolderPath)
+{
+ Q_Q(QQuickFileDialogImpl);
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ if (!attached || !attached->fileDialogListView())
+ return;
+
+ QString newCurrentFilePath;
+ int newCurrentFileIndex = 0;
+ const QString newFolderPath = QQmlFile::urlToLocalFileOrQrc(currentFolder);
+ if (!oldFolderPath.isEmpty() && !newFolderPath.isEmpty()) {
+ // If the user went up a directory (or several), we should set
+ // currentFile to be the directory that we were in (or
+ // its closest ancestor that is a child of the new directory).
+ // E.g. if oldFolderPath is /foo/bar/baz/abc/xyz, and newFolderPath is /foo/bar,
+ // then we want to set currentFile to be /foo/bar/baz.
+ const int indexOfFolder = oldFolderPath.indexOf(newFolderPath);
+ if (indexOfFolder != -1) {
+ // [folder]
+ // [ oldFolderPath ]
+ // /foo/bar/baz/abc/xyz
+ // [rel...Paths]
+ QStringList relativePaths = oldFolderPath.mid(indexOfFolder + newFolderPath.size()).split(QLatin1Char('/'), Qt::SkipEmptyParts);
+ newCurrentFilePath = newFolderPath + QLatin1Char('/') + relativePaths.first();
+
+ // Now find the index of that directory so that we can set the ListView's currentIndex to it.
+ const QDir newFolderDir(newFolderPath);
+ // Just to be safe...
+ if (!newFolderDir.exists()) {
+ qmlWarning(q) << "Directory" << newCurrentFilePath << "doesn't exist; can't get a file entry list for it";
+ return;
+ }
+
+ const QFileInfoList dirs = newFolderDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::DirsFirst);
+ const QFileInfo newCurrentFileInfo(newCurrentFilePath);
+ // The directory can contain files, but since we put dirs first, that should never affect the indices.
+ newCurrentFileIndex = dirs.indexOf(newCurrentFileInfo);
+ }
+ }
+
+ if (newCurrentFilePath.isEmpty()) {
+ // When entering into a directory that isn't a parent of the old one, the first
+ // file delegate should be selected.
+ // TODO: is there a cheaper way to do this? QDirIterator doesn't support sorting,
+ // so we can't use that. QQuickFolderListModel uses threads to fetch its data,
+ // so should be considered asynchronous. We might be able to use it, but it would
+ // complicate the code even more...
+ QDir newFolderDir(newFolderPath);
+ if (newFolderDir.exists()) {
+ const QFileInfoList files = newFolderDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::DirsFirst);
+ if (!files.isEmpty())
+ newCurrentFilePath = files.first().absoluteFilePath();
+ }
+ }
+
+ if (!newCurrentFilePath.isEmpty()) {
+ q->setCurrentFile(QUrl::fromLocalFile(newCurrentFilePath));
+ attached->fileDialogListView()->setCurrentIndex(newCurrentFileIndex);
+ if (QQuickItem *currentItem = attached->fileDialogListView()->currentItem())
+ currentItem->forceActiveFocus();
+ }
+}
+
+void QQuickFileDialogImplPrivate::handleAccept()
+{
+ // Let handleClick take care of calling accept().
+}
+
+void QQuickFileDialogImplPrivate::handleClick(QQuickAbstractButton *button)
+{
+ Q_Q(QQuickFileDialogImpl);
+ if (buttonRole(button) == QPlatformDialogHelper::AcceptRole && currentFile.isValid()) {
+ // The "Open" button was clicked, so we need to set the file to the current file, if any.
+ const QFileInfo fileInfo(currentFile.toLocalFile());
+ if (fileInfo.isDir()) {
+ // If it's a directory, navigate to it.
+ q->setCurrentFolder(currentFile);
+ // Don't call accept(), because selecting a folder != accepting the dialog.
+ } else {
+ // Otherwise it's a file, so select it and close the dialog.
+ q->setSelectedFile(currentFile);
+ q->accept();
+ QQuickDialogPrivate::handleClick(button);
+ emit q->fileSelected(currentFile);
+ }
+ }
+}
+
+QQuickFileDialogImpl::QQuickFileDialogImpl(QObject *parent)
+ : QQuickDialog(*(new QQuickFileDialogImplPrivate), parent)
+{
+}
+
+QQuickFileDialogImplAttached *QQuickFileDialogImpl::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickFileDialogImplAttached(object);
+}
+
+QUrl QQuickFileDialogImpl::currentFolder() const
+{
+ Q_D(const QQuickFileDialogImpl);
+ return d->currentFolder;
+}
+
+void QQuickFileDialogImpl::setCurrentFolder(const QUrl &currentFolder)
+{
+ qCDebug(lcCurrentFolder) << "setCurrentFolder called with" << currentFolder;
+ Q_D(QQuickFileDialogImpl);
+ if (currentFolder == d->currentFolder)
+ return;
+
+ const QString oldFolderPath = QQmlFile::urlToLocalFileOrQrc(d->currentFolder);
+
+ d->currentFolder = currentFolder;
+ // Since the directory changed, the old file can no longer be selected.
+ setCurrentFile(QUrl());
+ d->updateCurrentFile(oldFolderPath);
+ emit currentFolderChanged(d->currentFolder);
+}
+
+QUrl QQuickFileDialogImpl::selectedFile() const
+{
+ Q_D(const QQuickFileDialogImpl);
+ return d->selectedFile;
+}
+
+void QQuickFileDialogImpl::setSelectedFile(const QUrl &selectedFile)
+{
+ Q_D(QQuickFileDialogImpl);
+ if (selectedFile == d->selectedFile)
+ return;
+
+ d->selectedFile = selectedFile;
+ emit selectedFileChanged();
+}
+
+QUrl QQuickFileDialogImpl::currentFile() const
+{
+ Q_D(const QQuickFileDialogImpl);
+ return d->currentFile;
+}
+
+void QQuickFileDialogImpl::setCurrentFile(const QUrl &currentFile)
+{
+ Q_D(QQuickFileDialogImpl);
+ if (currentFile == d->currentFile)
+ return;
+
+ d->currentFile = currentFile;
+ d->updateEnabled();
+ emit currentFileChanged(d->currentFile);
+}
+
+QSharedPointer<QFileDialogOptions> QQuickFileDialogImpl::options() const
+{
+ Q_D(const QQuickFileDialogImpl);
+ return d->options;
+}
+
+void QQuickFileDialogImpl::setOptions(const QSharedPointer<QFileDialogOptions> &options)
+{
+ qCDebug(lcOptions).nospace() << "setOptions called with:"
+ << " acceptMode=" << options->acceptMode()
+ << " fileMode=" << options->fileMode()
+ << " initialDirectory=" << options->initialDirectory()
+ << " nameFilters=" << options->nameFilters()
+ << " initiallySelectedNameFilter=" << options->initiallySelectedNameFilter();
+
+ Q_D(QQuickFileDialogImpl);
+ d->options = options;
+
+ if (d->options) {
+ d->selectedNameFilter->setOptions(options);
+ d->setNameFilters(options->nameFilters());
+ }
+}
+
+/*!
+ \internal
+
+ The list of user-facing strings describing the available file filters.
+*/
+QStringList QQuickFileDialogImpl::nameFilters() const
+{
+ Q_D(const QQuickFileDialogImpl);
+ return d->options ? d->options->nameFilters() : QStringList();
+}
+
+void QQuickFileDialogImpl::resetNameFilters()
+{
+ Q_D(QQuickFileDialogImpl);
+ d->setNameFilters(QStringList());
+}
+
+QQuickFileNameFilter *QQuickFileDialogImpl::selectedNameFilter() const
+{
+ Q_D(const QQuickFileDialogImpl);
+ if (!d->selectedNameFilter) {
+ QQuickFileDialogImpl *that = const_cast<QQuickFileDialogImpl *>(this);
+ d->selectedNameFilter = new QQuickFileNameFilter(that);
+ if (d->options)
+ d->selectedNameFilter->setOptions(d->options);
+ }
+ return d->selectedNameFilter;
+}
+
+/*!
+ \internal
+
+ These allow QQuickPlatformFileDialog::show() to set custom labels on the
+ dialog buttons without having to know about/go through QQuickFileDialogImplAttached
+ and QQuickDialogButtonBox.
+*/
+void QQuickFileDialogImpl::setAcceptLabel(const QString &label)
+{
+ Q_D(QQuickFileDialogImpl);
+ d->acceptLabel = label;
+ QQuickFileDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ auto acceptButton = attached->buttonBox()->standardButton(QPlatformDialogHelper::Open);
+ if (!acceptButton) {
+ qmlWarning(this).nospace() << "Can't set accept label to " << label
+ << "; failed to find Open button in DialogButtonBox of " << this;
+ return;
+ }
+
+ acceptButton->setText(!label.isEmpty()
+ ? label : QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::Open));
+}
+
+void QQuickFileDialogImpl::setRejectLabel(const QString &label)
+{
+ Q_D(QQuickFileDialogImpl);
+ d->rejectLabel = label;
+ QQuickFileDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ auto rejectButton = attached->buttonBox()->standardButton(QPlatformDialogHelper::Cancel);
+ if (!rejectButton) {
+ qmlWarning(this).nospace() << "Can't set reject label to " << label
+ << "; failed to find Open button in DialogButtonBox of " << this;
+ return;
+ }
+
+ rejectButton->setText(!label.isEmpty()
+ ? label : QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::Cancel));
+}
+
+void QQuickFileDialogImpl::selectNameFilter(const QString &filter)
+{
+ qCDebug(lcNameFilters) << "selectNameFilter called with" << filter;
+ Q_D(QQuickFileDialogImpl);
+ d->selectedNameFilter->update(filter);
+ emit filterSelected(filter);
+}
+
+void QQuickFileDialogImpl::componentComplete()
+{
+ Q_D(QQuickFileDialogImpl);
+ QQuickDialog::componentComplete();
+
+ // Find the right-most button and set its key navigation so that
+ // tab moves focus to the breadcrumb bar's up button. I tried
+ // doing this via KeyNavigation on the DialogButtonBox in QML,
+ // but it didn't work (probably because it's not the right item).
+ QQuickFileDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ const int buttonCount = attached->buttonBox()->count();
+ if (buttonCount == 0)
+ return;
+
+ QQuickAbstractButton *rightMostButton = qobject_cast<QQuickAbstractButton *>(
+ attached->buttonBox()->itemAt(buttonCount - 1));
+ if (!rightMostButton) {
+ qmlWarning(this) << "Can't find right-most button in DialogButtonBox";
+ return;
+ }
+
+ auto keyNavigationAttached = QQuickKeyNavigationAttached::qmlAttachedProperties(rightMostButton);
+ if (!keyNavigationAttached) {
+ qmlWarning(this) << "Can't create attached KeyNavigation object on" << QDebug::toString(rightMostButton);
+ return;
+ }
+
+ keyNavigationAttached->setTab(attached->breadcrumbBar()->upButton());
+}
+
+void QQuickFileDialogImpl::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ Q_D(QQuickFileDialogImpl);
+ QQuickDialog::itemChange(change, data);
+
+ if (change != QQuickItem::ItemVisibleHasChanged || !isComponentComplete() || !data.boolValue)
+ return;
+
+ QQuickFileDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ attached->fileDialogListView()->forceActiveFocus();
+ d->updateEnabled();
+}
+
+QQuickFileDialogImplAttached *QQuickFileDialogImplPrivate::attachedOrWarn()
+{
+ Q_Q(QQuickFileDialogImpl);
+ QQuickFileDialogImplAttached *attached = static_cast<QQuickFileDialogImplAttached*>(
+ qmlAttachedPropertiesObject<QQuickFileDialogImpl>(q));
+ if (!attached)
+ qmlWarning(q) << "Expected FileDialogImpl attached object to be present on" << this;
+ return attached;
+}
+
+void QQuickFileDialogImplAttachedPrivate::nameFiltersComboBoxItemActivated(int index)
+{
+ qCDebug(lcAttachedNameFilters) << "nameFiltersComboBoxItemActivated called with" << index;
+ auto fileDialogImpl = qobject_cast<QQuickFileDialogImpl*>(parent);
+ if (!fileDialogImpl)
+ return;
+
+ fileDialogImpl->selectNameFilter(nameFiltersComboBox->textAt(index));
+}
+
+void QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged()
+{
+ auto fileDialogImpl = qobject_cast<QQuickFileDialogImpl*>(parent);
+ if (!fileDialogImpl)
+ return;
+
+ auto fileDialogDelegate = qobject_cast<QQuickFileDialogDelegate*>(fileDialogListView->currentItem());
+ if (!fileDialogDelegate)
+ return;
+
+ fileDialogImpl->setCurrentFile(fileDialogDelegate->file());
+}
+
+QQuickFileDialogImplAttached::QQuickFileDialogImplAttached(QObject *parent)
+ : QObject(*(new QQuickFileDialogImplAttachedPrivate), parent)
+{
+ if (!qobject_cast<QQuickFileDialogImpl*>(parent)) {
+ qmlWarning(this) << "FileDialogImpl attached properties should only be "
+ << "accessed through the root FileDialogImpl instance";
+ }
+}
+
+QQuickDialogButtonBox *QQuickFileDialogImplAttached::buttonBox() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->buttonBox;
+}
+
+void QQuickFileDialogImplAttached::setButtonBox(QQuickDialogButtonBox *buttonBox)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ if (buttonBox == d->buttonBox)
+ return;
+
+ if (d->buttonBox) {
+ QQuickFileDialogImpl *fileDialogImpl = qobject_cast<QQuickFileDialogImpl*>(parent());
+ if (fileDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(fileDialogImpl);
+ QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::accepted,
+ dialogPrivate, &QQuickDialogPrivate::handleAccept);
+ QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::rejected,
+ dialogPrivate, &QQuickDialogPrivate::handleReject);
+ QObjectPrivate::disconnect(d->buttonBox, &QQuickDialogButtonBox::clicked,
+ dialogPrivate, &QQuickDialogPrivate::handleClick);
+ }
+ }
+
+ d->buttonBox = buttonBox;
+
+ if (buttonBox) {
+ QQuickFileDialogImpl *fileDialogImpl = qobject_cast<QQuickFileDialogImpl*>(parent());
+ if (fileDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(fileDialogImpl);
+ QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::accepted,
+ dialogPrivate, &QQuickDialogPrivate::handleAccept);
+ QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::rejected,
+ dialogPrivate, &QQuickDialogPrivate::handleReject);
+ QObjectPrivate::connect(d->buttonBox, &QQuickDialogButtonBox::clicked,
+ dialogPrivate, &QQuickDialogPrivate::handleClick);
+ }
+ }
+
+ emit buttonBoxChanged();
+}
+
+QQuickComboBox *QQuickFileDialogImplAttached::nameFiltersComboBox() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->nameFiltersComboBox;
+}
+
+void QQuickFileDialogImplAttached::setNameFiltersComboBox(QQuickComboBox *nameFiltersComboBox)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ if (nameFiltersComboBox == d->nameFiltersComboBox)
+ return;
+
+ d->nameFiltersComboBox = nameFiltersComboBox;
+
+ QObjectPrivate::connect(d->nameFiltersComboBox, &QQuickComboBox::activated,
+ d, &QQuickFileDialogImplAttachedPrivate::nameFiltersComboBoxItemActivated);
+
+ emit nameFiltersComboBoxChanged();
+}
+
+QString QQuickFileDialogImplAttached::selectedNameFilter() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->nameFiltersComboBox ? d->nameFiltersComboBox->currentText() : QString();
+}
+
+void QQuickFileDialogImplAttached::selectNameFilter(const QString &filter)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ qCDebug(lcAttachedNameFilters) << "selectNameFilter called with" << filter;
+ if (!d->nameFiltersComboBox)
+ return;
+
+ const int indexInComboBox = d->nameFiltersComboBox->find(filter);
+ if (indexInComboBox == -1)
+ return;
+
+ qCDebug(lcAttachedNameFilters) << "setting ComboBox's currentIndex to" << indexInComboBox;
+ d->nameFiltersComboBox->setCurrentIndex(indexInComboBox);
+}
+
+QQuickListView *QQuickFileDialogImplAttached::fileDialogListView() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->fileDialogListView;
+}
+
+void QQuickFileDialogImplAttached::setFileDialogListView(QQuickListView *fileDialogListView)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ if (fileDialogListView == d->fileDialogListView)
+ return;
+
+ d->fileDialogListView = fileDialogListView;
+
+ QObjectPrivate::connect(d->fileDialogListView, &QQuickListView::currentIndexChanged,
+ d, &QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged);
+
+ emit fileDialogListViewChanged();
+}
+
+QQuickFolderBreadcrumbBar *QQuickFileDialogImplAttached::breadcrumbBar() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->breadcrumbBar;
+}
+
+void QQuickFileDialogImplAttached::setBreadcrumbBar(QQuickFolderBreadcrumbBar *breadcrumbBar)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ if (breadcrumbBar == d->breadcrumbBar)
+ return;
+
+ d->breadcrumbBar = breadcrumbBar;
+ emit breadcrumbBarChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h
new file mode 100644
index 00000000..754d2c30
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFILEDIALOGIMPL_P_H
+#define QQUICKFILEDIALOGIMPL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickComboBox;
+class QQuickDialogButtonBox;
+
+class QQuickFileDialogImplAttached;
+class QQuickFileDialogImplAttachedPrivate;
+class QQuickFileDialogImplPrivate;
+class QQuickFileNameFilter;
+class QQuickFolderBreadcrumbBar;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImpl : public QQuickDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL)
+ Q_PROPERTY(QUrl selectedFile READ selectedFile WRITE setSelectedFile NOTIFY selectedFileChanged FINAL)
+ Q_PROPERTY(QUrl currentFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged FINAL)
+ Q_PROPERTY(QStringList nameFilters READ nameFilters NOTIFY nameFiltersChanged FINAL)
+ Q_PROPERTY(QQuickFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT)
+ QML_NAMED_ELEMENT(FileDialogImpl)
+ QML_ATTACHED(QQuickFileDialogImplAttached)
+ QML_ADDED_IN_VERSION(6, 2)
+ Q_MOC_INCLUDE(<QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>)
+ Q_MOC_INCLUDE(<QtQuickDialogs2QuickImpl/private/qquickfolderbreadcrumbbar_p.h>)
+
+public:
+ explicit QQuickFileDialogImpl(QObject *parent = nullptr);
+
+ static QQuickFileDialogImplAttached *qmlAttachedProperties(QObject *object);
+
+ QUrl currentFolder() const;
+ void setCurrentFolder(const QUrl &currentFolder);
+
+ QUrl selectedFile() const;
+ void setSelectedFile(const QUrl &file);
+
+ QUrl currentFile() const;
+ void setCurrentFile(const QUrl &currentFile);
+
+ QSharedPointer<QFileDialogOptions> options() const;
+ void setOptions(const QSharedPointer<QFileDialogOptions> &options);
+
+ QStringList nameFilters() const;
+ void resetNameFilters();
+
+ QQuickFileNameFilter *selectedNameFilter() const;
+
+ void setAcceptLabel(const QString &label);
+ void setRejectLabel(const QString &label);
+
+public Q_SLOTS:
+ void selectNameFilter(const QString &filter);
+
+Q_SIGNALS:
+ void currentFolderChanged(const QUrl &folderUrl);
+ void selectedFileChanged();
+ void currentFileChanged(const QUrl &currentFileUrl);
+ void nameFiltersChanged();
+ void fileSelected(const QUrl &fileUrl);
+ void filterSelected(const QString &filter);
+
+private:
+ void componentComplete() override;
+ void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override;
+
+ Q_DISABLE_COPY(QQuickFileDialogImpl)
+ Q_DECLARE_PRIVATE(QQuickFileDialogImpl)
+};
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImplAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox WRITE setButtonBox NOTIFY buttonBoxChanged FINAL)
+ Q_PROPERTY(QQuickComboBox *nameFiltersComboBox READ nameFiltersComboBox WRITE setNameFiltersComboBox NOTIFY nameFiltersComboBoxChanged)
+ Q_PROPERTY(QQuickListView *fileDialogListView READ fileDialogListView WRITE setFileDialogListView NOTIFY fileDialogListViewChanged)
+ Q_PROPERTY(QQuickFolderBreadcrumbBar *breadcrumbBar READ breadcrumbBar WRITE setBreadcrumbBar NOTIFY breadcrumbBarChanged)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickcombobox_p.h>)
+
+public:
+ explicit QQuickFileDialogImplAttached(QObject *parent = nullptr);
+
+ QQuickDialogButtonBox *buttonBox() const;
+ void setButtonBox(QQuickDialogButtonBox *buttonBox);
+
+ QQuickComboBox *nameFiltersComboBox() const;
+ void setNameFiltersComboBox(QQuickComboBox *nameFiltersComboBox);
+
+ QString selectedNameFilter() const;
+ void selectNameFilter(const QString &filter);
+
+ QQuickListView *fileDialogListView() const;
+ void setFileDialogListView(QQuickListView *fileDialogListView);
+
+ QQuickFolderBreadcrumbBar *breadcrumbBar() const;
+ void setBreadcrumbBar(QQuickFolderBreadcrumbBar *breadcrumbBar);
+
+Q_SIGNALS:
+ void buttonBoxChanged();
+ void nameFiltersComboBoxChanged();
+ void fileDialogListViewChanged();
+ void breadcrumbBarChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickFileDialogImplAttached)
+ Q_DECLARE_PRIVATE(QQuickFileDialogImplAttached)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFileDialogImpl)
+
+#endif // QQUICKFILEDIALOGIMPL_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h
new file mode 100644
index 00000000..4acce71d
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfiledialogimpl_p_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFILEDIALOG_P_P_H
+#define QQUICKFILEDIALOG_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickTemplates2/private/qquickcombobox_p.h>
+#include <QtQuickTemplates2/private/qquickdialog_p_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>
+
+#include "qquickfiledialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileNameFilter;
+
+class QQuickFileDialogImplPrivate : public QQuickDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickFileDialogImpl)
+
+public:
+ QQuickFileDialogImplPrivate();
+
+ static QQuickFileDialogImplPrivate *get(QQuickFileDialogImpl *dialog)
+ {
+ return dialog->d_func();
+ }
+
+ QQuickFileDialogImplAttached *attachedOrWarn();
+
+ void setNameFilters(const QStringList &filters);
+
+ void updateEnabled();
+ void updateCurrentFile(const QString &oldFolderPath);
+
+ void handleAccept() override;
+ void handleClick(QQuickAbstractButton *button) override;
+
+ QSharedPointer<QFileDialogOptions> options;
+ QUrl currentFolder;
+ QUrl selectedFile;
+ QUrl currentFile;
+ QStringList nameFilters;
+ mutable QQuickFileNameFilter *selectedNameFilter = nullptr;
+ QString acceptLabel;
+ QString rejectLabel;
+};
+
+class QQuickFileDialogImplAttachedPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickFileDialogImplAttached)
+
+ void nameFiltersComboBoxItemActivated(int index);
+ void fileDialogListViewCurrentIndexChanged();
+
+public:
+ QPointer<QQuickDialogButtonBox> buttonBox;
+ QPointer<QQuickComboBox> nameFiltersComboBox;
+ QPointer<QQuickListView> fileDialogListView;
+ QPointer<QQuickFolderBreadcrumbBar> breadcrumbBar;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFILEDIALOG_P_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar.cpp
new file mode 100644
index 00000000..65417a67
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar.cpp
@@ -0,0 +1,784 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickfolderbreadcrumbbar_p.h"
+#include "qquickfolderbreadcrumbbar_p_p.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qloggingcategory.h>
+#if QT_CONFIG(shortcut)
+#include <QtGui/private/qshortcutmap_p.h>
+#endif
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtQml/QQmlFile>
+#include <QtQuick/private/qquicktextinput_p.h>
+#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
+#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
+#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
+
+#include "qquickfiledialogimpl_p.h"
+#include "qquickfiledialogimpl_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcFolderBreadcrumbBar, "qt.quick.dialogs.folderbreadcrumbbar")
+Q_LOGGING_CATEGORY(lcContentSize, "qt.quick.dialogs.folderbreadcrumbbar.contentsize")
+Q_LOGGING_CATEGORY(lcDelegates, "qt.quick.dialogs.folderbreadcrumbbar.delegates")
+Q_LOGGING_CATEGORY(lcShortcuts, "qt.quick.dialogs.folderbreadcrumbbar.shortcuts")
+Q_LOGGING_CATEGORY(lcTextInput, "qt.quick.dialogs.folderbreadcrumbbar.textinput")
+Q_LOGGING_CATEGORY(lcCurrentItem, "qt.quick.dialogs.folderbreadcrumbbar.currentitem")
+
+QQuickItem *QQuickFolderBreadcrumbBarPrivate::createDelegateItem(QQmlComponent *component, const QVariantMap &initialProperties)
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ // If we don't use the correct context, it won't be possible to refer to
+ // the control's id from within the delegates.
+ QQmlContext *creationContext = component->creationContext();
+ // The component might not have been created in QML, in which case
+ // the creation context will be null and we have to create it ourselves.
+ if (!creationContext)
+ creationContext = qmlContext(q);
+ QQmlContext *context = new QQmlContext(creationContext, q);
+ context->setContextObject(q);
+ QQuickItem *item = qobject_cast<QQuickItem*>(component->createWithInitialProperties(initialProperties, context));
+ if (item)
+ QQml_setParent_noEvent(item, q);
+ qCDebug(lcDelegates) << "- created delegate item" << item << "with initialProperties" << initialProperties;
+ return item;
+}
+
+QString QQuickFolderBreadcrumbBarPrivate::folderBaseName(const QString &folderPath)
+{
+ if (folderPath == QLatin1String("/")) {
+ // Unix root.
+ return folderPath;
+ } else if (folderPath.endsWith(QLatin1String(":/"))) {
+ // Windows drive.
+ return folderPath.mid(0, folderPath.size() - 1);
+ }
+ const QString baseName = folderPath.mid(folderPath.lastIndexOf(QLatin1Char('/')) + 1);
+ return baseName;
+}
+
+/*!
+ \internal
+
+ Returns \c { "/foo", "/foo/bar", "/foo/bar/baz" } if \a folder is \c "/foo/bar/baz".
+*/
+QStringList QQuickFolderBreadcrumbBarPrivate::crumbPathsForFolder(const QUrl &folder)
+{
+ const QString folderPath = QDir::fromNativeSeparators(QQmlFile::urlToLocalFileOrQrc(folder));
+ QDir dir(folderPath);
+ // In order to collect the paths for each breadcrumb, we need to work backwards, so we prepend.
+ QStringList paths;
+ do {
+ paths.prepend(dir.absolutePath());
+ } while (dir.cdUp());
+ return paths;
+}
+
+void QQuickFolderBreadcrumbBarPrivate::repopulate()
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ qCDebug(lcDelegates) << "attemping to repopulate breadcrumb bar using folder...";
+
+ if (repopulating)
+ return;
+
+ if (!buttonDelegate || !separatorDelegate || !q->contentItem()) {
+ qCWarning(lcDelegates) << "Both delegates and contentItem must be set before repopulating";
+ return;
+ }
+
+ QBoolBlocker repopulateGuard(repopulating);
+
+ auto failureCleanup = [=](){
+ folderPaths.clear();
+ while (q->count() > 0)
+ q->removeItem(q->itemAt(0));
+ };
+
+ qCDebug(lcDelegates) << "- getting paths for directory" << fileDialog->currentFolder();
+ folderPaths = crumbPathsForFolder(fileDialog->currentFolder());
+
+ while (q->count() > 0)
+ q->removeItem(q->itemAt(0));
+
+ for (int i = 0; i < folderPaths.size(); ++i) {
+ const QString &folderPath = folderPaths.at(i);
+
+ QVariantMap initialProperties = {
+ { QStringLiteral("index"), QVariant::fromValue(i) },
+ { QStringLiteral("folderName"), QVariant::fromValue(folderBaseName(folderPath)) }
+ };
+ QQuickItem *buttonItem = createDelegateItem(buttonDelegate, initialProperties);
+ if (!buttonItem) {
+ qCWarning(lcDelegates) << "Failed creating breadcrumb buttonDelegate item:\n" << buttonDelegate->errorString();
+ failureCleanup();
+ break;
+ }
+ if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(buttonItem)) {
+ QObjectPrivate::connect(button, &QQuickAbstractButton::clicked,
+ this, &QQuickFolderBreadcrumbBarPrivate::crumbClicked);
+ }
+ insertItem(q->count(), buttonItem);
+
+ // Don't add a separator for the last button.
+ if (i < folderPaths.size() - 1) {
+ initialProperties = {};
+ QQuickItem *separatorItem = createDelegateItem(separatorDelegate, initialProperties);
+ if (!separatorItem) {
+ qCWarning(lcDelegates) << "Failed creating breadcrumb separatorDelegate item:\n" << buttonDelegate->errorString();
+ failureCleanup();
+ break;
+ }
+ insertItem(q->count(), separatorItem);
+ }
+ }
+
+ const int finalCount = q->count();
+ // We would do - 2, since separators are included in the count,
+ // but as we don't add a separator for the last button, we only need to subtract 1.
+ const int newCurrentIndex = finalCount > 2 ? finalCount - 1 : -1;
+ qCDebug(lcDelegates) << "- setting currentIndex to" << newCurrentIndex;
+ q->setCurrentIndex(newCurrentIndex);
+
+ updateImplicitContentSize();
+
+ qCDebug(lcDelegates) << "... bar now contains" << q->count()
+ << "buttons and separators in total, for the following paths:" << folderPaths;
+}
+
+void QQuickFolderBreadcrumbBarPrivate::crumbClicked()
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ qCDebug(lcCurrentItem) << "updateCurrentIndex called by sender" << q->sender();
+ QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(q->sender());
+ if (button) {
+ const int buttonIndex = contentModel->indexOf(button, nullptr);
+ q->setCurrentIndex(buttonIndex);
+ const QUrl folderUrl = QUrl::fromLocalFile(folderPaths.at(buttonIndex / 2));
+ // TODO: don't repopulate the whole model when clicking on crumbs
+ qCDebug(lcCurrentItem) << "setting file dialog's folder to" << folderUrl;
+ fileDialog->setCurrentFolder(folderUrl);
+ }
+}
+
+void QQuickFolderBreadcrumbBarPrivate::folderChanged()
+{
+ if (componentComplete)
+ repopulate();
+}
+
+static inline QString upButtonName()
+{
+ return QStringLiteral("upButton");
+}
+
+void QQuickFolderBreadcrumbBarPrivate::cancelUpButton()
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ quickCancelDeferred(q, upButtonName());
+}
+
+void QQuickFolderBreadcrumbBarPrivate::executeUpButton(bool complete)
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ if (upButton.wasExecuted())
+ return;
+
+ if (!upButton || complete)
+ quickBeginDeferred(q, upButtonName(), upButton);
+ if (complete)
+ quickCompleteDeferred(q, upButtonName(), upButton);
+}
+
+void QQuickFolderBreadcrumbBarPrivate::goUp()
+{
+ QDir dir(QQmlFile::urlToLocalFileOrQrc(fileDialog->currentFolder()));
+ dir.cdUp();
+ fileDialog->setCurrentFolder(QUrl::fromLocalFile(dir.absolutePath()));
+}
+
+static inline QString textFieldName()
+{
+ return QStringLiteral("textField");
+}
+
+void QQuickFolderBreadcrumbBarPrivate::cancelTextField()
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ quickCancelDeferred(q, textFieldName());
+}
+
+void QQuickFolderBreadcrumbBarPrivate::executeTextField(bool complete)
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ if (textField.wasExecuted())
+ return;
+
+ if (!textField || complete)
+ quickBeginDeferred(q, textFieldName(), textField);
+ if (complete)
+ quickCompleteDeferred(q, textFieldName(), textField);
+}
+
+void QQuickFolderBreadcrumbBarPrivate::toggleTextFieldVisibility()
+{
+ const QUrl url = !fileDialog->selectedFile().isEmpty() ? fileDialog->selectedFile() : fileDialog->currentFolder();
+ textField->setText(QQmlFile::urlToLocalFileOrQrc(url));
+ textField->setVisible(!textField->isVisible());
+
+ qCDebug(lcTextInput) << "text field visibility changed to" << textField->isVisible();
+
+ if (textField->isVisible()) {
+ // The text field is now visible, so give it focus,
+ // select the text, and let it handle escape/back.
+ textField->forceActiveFocus(Qt::ShortcutFocusReason);
+ textField->selectAll();
+ }
+
+ // We connect to the TextField's visibleChanged signal, so textFieldVisibleChanged()
+ // will get called automatically and we don't need to call it here.
+
+ contentItem->setVisible(!textField->isVisible());
+
+ // When the TextField is visible, certain items in the dialog need to be disabled.
+ auto fileDialogPrivate = QQuickFileDialogImplPrivate::get(fileDialog);
+ fileDialogPrivate->updateEnabled();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::textFieldAccepted()
+{
+ const QUrl fileUrl = QUrl::fromLocalFile(textField->text());
+ const bool mustExist = fileDialog->options()->acceptMode() != QFileDialogOptions::AcceptSave;
+ const bool enteredPathIsValidUrl = fileUrl.isValid();
+ bool enteredPathExists = false;
+ bool enteredPathIsDir = false;
+ if (enteredPathIsValidUrl) {
+ const QFileInfo fileInfo(textField->text());
+ enteredPathExists = fileInfo.exists();
+ if (enteredPathExists)
+ enteredPathIsDir = fileInfo.isDir();
+ }
+
+ qCDebug(lcTextInput).nospace() << "text field accepted -"
+ << " text=" << textField->text()
+ << " fileUrl=" << fileUrl
+ << " mustExist=" << mustExist
+ << " enteredPathIsValidUrl=" << enteredPathIsValidUrl
+ << " enteredPathExists=" << enteredPathExists
+ << " enteredPathIsDir=" << enteredPathIsDir;
+
+ if (enteredPathIsDir && (enteredPathExists || !mustExist)) {
+ qCDebug(lcTextInput) << "path entered is a folder; setting folder";
+ fileDialog->setCurrentFolder(fileUrl);
+ } else if (!enteredPathIsDir && (enteredPathExists || !mustExist)) {
+ qCDebug(lcTextInput) << "path entered is a file; setting file and calling accept()";
+ // It's important that we set the currentFile here, as that's what
+ // QQuickPlatformFileDialog::selectedFiles() needs to return, and
+ // QQuickFileDialog::accept() sets its file property based on
+ // selectedFiles().
+ fileDialog->setCurrentFile(fileUrl);
+ fileDialog->setSelectedFile(fileUrl);
+ fileDialog->accept();
+ } else {
+ qCDebug(lcTextInput) << "path entered is not valid; not setting file/folder";
+ }
+
+ toggleTextFieldVisibility();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged()
+{
+ qCDebug(lcShortcuts) << "text field was either hidden or shown";
+
+ if (textField && textField->isVisible())
+ handleTextFieldShown();
+ else
+ handleTextFieldHidden();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged()
+{
+ // When the text field loses focus, it should be hidden.
+ if (!textField->hasActiveFocus() && textField->isVisible())
+ toggleTextFieldVisibility();
+}
+
+/*
+ When the text field is visible:
+
+ - Ctrl+L should do nothing (matches e.g. Ubuntu and Windows)
+ - Escape/back should hide it
+*/
+void QQuickFolderBreadcrumbBarPrivate::handleTextFieldShown()
+{
+#if QT_CONFIG(shortcut)
+ Q_Q(QQuickFolderBreadcrumbBar);
+ if (editPathToggleShortcutId == 0)
+ return;
+
+ QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
+ qCDebug(lcShortcuts) << "text field was shown; grabbing/ungrabbing relevant shortcuts...";
+
+ // Disable the back/escape shortcuts for QQuickPopup so that the TextField can get them.
+ auto popupItem = qobject_cast<QQuickPopupItem*>(fileDialog->popupItem());
+ popupItem->ungrabShortcut();
+
+ appPrivate->shortcutMap.removeShortcut(editPathToggleShortcutId, q);
+ editPathToggleShortcutId = 0;
+
+ editPathBackShortcutId = appPrivate->shortcutMap.addShortcut(
+ q, Qt::Key_Back, Qt::WindowShortcut, QQuickShortcutContext::matcher);
+ editPathEscapeShortcutId = appPrivate->shortcutMap.addShortcut(
+ q, Qt::Key_Escape, Qt::WindowShortcut, QQuickShortcutContext::matcher);
+
+ qCDebug(lcShortcuts).nospace() << "... shortcut IDs:"
+ << " editPathToggleShortcutId=" << editPathToggleShortcutId
+ << " editPathBackShortcutId=" << editPathBackShortcutId
+ << " editPathEscapeShortcutId=" << editPathEscapeShortcutId;
+#endif
+}
+
+/*
+ When the text field is not visible:
+
+ - Ctrl+L should make it visible
+ - Escape/back should close the dialog
+*/
+void QQuickFolderBreadcrumbBarPrivate::handleTextFieldHidden()
+{
+#if QT_CONFIG(shortcut)
+ Q_Q(QQuickFolderBreadcrumbBar);
+
+ QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
+ qCDebug(lcShortcuts) << "text field was hidden; grabbing/ungrabbing relevant shortcuts...";
+
+ if (editPathToggleShortcutId == 0) {
+ editPathToggleShortcutId = appPrivate->shortcutMap.addShortcut(
+ q, Qt::CTRL | Qt::Key_L, Qt::WindowShortcut, QQuickShortcutContext::matcher);
+ }
+
+ // When the bar is first completed, this function is called, since the text field starts off hidden.
+ // If removeShortcut is called with a zero id, all shortcuts for the given object will be removed,
+ // and we don't want that.
+ if (editPathBackShortcutId != 0) {
+ appPrivate->shortcutMap.removeShortcut(editPathBackShortcutId, q);
+ editPathBackShortcutId = 0;
+ }
+ if (editPathEscapeShortcutId != 0) {
+ appPrivate->shortcutMap.removeShortcut(editPathEscapeShortcutId, q);
+ editPathEscapeShortcutId = 0;
+ }
+
+ // Re-enable the back/escape shortcuts for QQuickPopup now that TextField no longer needs them.
+ auto popupItem = qobject_cast<QQuickPopupItem*>(fileDialog->popupItem());
+ if (popupItem)
+ popupItem->grabShortcut();
+
+ qCDebug(lcShortcuts).nospace() << "... shortcut IDs: "
+ << " editPathToggleShortcutId=" << editPathToggleShortcutId
+ << " editPathBackShortcutId=" << editPathBackShortcutId
+ << " editPathEscapeShortcutId=" << editPathEscapeShortcutId;
+#endif
+}
+
+void QQuickFolderBreadcrumbBarPrivate::ungrabEditPathShortcuts()
+{
+#if QT_CONFIG(shortcut)
+ Q_Q(QQuickFolderBreadcrumbBar);
+ QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
+ qCDebug(lcShortcuts) << "ungrabbing all edit path shortcuts";
+
+ if (editPathToggleShortcutId != 0) {
+ appPrivate->shortcutMap.removeShortcut(editPathToggleShortcutId, q);
+ editPathToggleShortcutId = 0;
+ }
+ if (editPathBackShortcutId != 0) {
+ appPrivate->shortcutMap.removeShortcut(editPathBackShortcutId, q);
+ editPathBackShortcutId = 0;
+ }
+ if (editPathEscapeShortcutId != 0) {
+ appPrivate->shortcutMap.removeShortcut(editPathEscapeShortcutId, q);
+ editPathEscapeShortcutId = 0;
+ }
+#endif
+}
+
+qreal QQuickFolderBreadcrumbBarPrivate::getContentWidth() const
+{
+ Q_Q(const QQuickFolderBreadcrumbBar);
+ const int count = contentModel->count();
+ qreal totalWidth = qMax(0, count - 1) * spacing;
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *item = q->itemAt(i);
+ if (item) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ if (!p->widthValid)
+ totalWidth += item->implicitWidth();
+ else
+ totalWidth += item->width();
+ }
+ }
+ qCDebug(lcContentSize) << "content width:" << totalWidth;
+ return totalWidth;
+}
+
+qreal QQuickFolderBreadcrumbBarPrivate::getContentHeight() const
+{
+ Q_Q(const QQuickFolderBreadcrumbBar);
+ const int count = contentModel->count();
+ qreal maxHeight = 0;
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *item = q->itemAt(i);
+ if (item)
+ maxHeight = qMax(maxHeight, item->implicitHeight());
+ }
+ qCDebug(lcContentSize) << "content height:" << maxHeight;
+ return maxHeight;
+}
+
+void QQuickFolderBreadcrumbBarPrivate::resizeContent()
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ if (contentItem) {
+ const int upButtonSpace = q->upButton() ? q->upButton()->width() + upButtonSpacing : 0;
+ contentItem->setPosition(QPointF(q->leftPadding() + upButtonSpace, q->topPadding()));
+ contentItem->setSize(QSizeF(q->availableWidth() - upButtonSpace, q->availableHeight()));
+
+ if (textField) {
+ textField->setPosition(contentItem->position());
+ textField->setSize(contentItem->size());
+ }
+ }
+}
+
+void QQuickFolderBreadcrumbBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
+{
+ QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
+ if (change.sizeChange())
+ updateImplicitContentSize();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
+{
+ QQuickContainerPrivate::itemImplicitWidthChanged(item);
+ if (item != contentItem)
+ updateImplicitContentWidth();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
+{
+ QQuickContainerPrivate::itemImplicitHeightChanged(item);
+ if (item != contentItem)
+ updateImplicitContentHeight();
+}
+
+/*!
+ \internal
+
+ Private class for breadcrumb navigation of a directory.
+
+ Given a FileDialog, FolderBreadCrumbbar creates breadcrumb buttons and
+ separators from the specified delegate components.
+*/
+
+QQuickFolderBreadcrumbBar::QQuickFolderBreadcrumbBar(QQuickItem *parent)
+ : QQuickContainer(*(new QQuickFolderBreadcrumbBarPrivate), parent)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ d->changeTypes |= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
+}
+
+QQuickFileDialogImpl *QQuickFolderBreadcrumbBar::fileDialog() const
+{
+ Q_D(const QQuickFolderBreadcrumbBar);
+ return d->fileDialog;
+}
+
+void QQuickFolderBreadcrumbBar::setFileDialog(QQuickFileDialogImpl *fileDialog)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ if (fileDialog == d->fileDialog)
+ return;
+
+ if (d->fileDialog)
+ QObjectPrivate::disconnect(d->fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
+
+ d->fileDialog = fileDialog;
+
+ if (d->fileDialog)
+ QObjectPrivate::connect(d->fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
+
+ emit fileDialogChanged();
+}
+
+QQmlComponent *QQuickFolderBreadcrumbBar::buttonDelegate()
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ return d->buttonDelegate;
+}
+
+void QQuickFolderBreadcrumbBar::setButtonDelegate(QQmlComponent *delegate)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ qCDebug(lcFolderBreadcrumbBar) << "setButtonDelegate called with" << delegate;
+ if (d->componentComplete) {
+ // Simplify the code by disallowing this.
+ qCWarning(lcFolderBreadcrumbBar) << "BreadcrumbBar does not support setting delegates after component completion";
+ return;
+ }
+
+ if (delegate == d->buttonDelegate)
+ return;
+
+ d->buttonDelegate = delegate;
+ emit buttonDelegateChanged();
+}
+
+QQmlComponent *QQuickFolderBreadcrumbBar::separatorDelegate()
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ return d->separatorDelegate;
+}
+
+void QQuickFolderBreadcrumbBar::setSeparatorDelegate(QQmlComponent *delegate)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ qCDebug(lcFolderBreadcrumbBar) << "setSeparatorDelegate called with" << delegate;
+ if (d->componentComplete) {
+ qCWarning(lcFolderBreadcrumbBar) << "BreadcrumbBar does not support setting delegates after component completion";
+ return;
+ }
+
+ if (delegate == d->separatorDelegate)
+ return;
+
+ d->separatorDelegate = delegate;
+ emit separatorDelegateChanged();
+}
+
+QQuickAbstractButton *QQuickFolderBreadcrumbBar::upButton()
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ if (!d->upButton)
+ d->executeUpButton();
+ return d->upButton;
+}
+
+void QQuickFolderBreadcrumbBar::setUpButton(QQuickAbstractButton *upButton)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ if (upButton == d->upButton)
+ return;
+
+ if (!d->upButton.isExecuting())
+ d->cancelUpButton();
+
+ if (d->upButton) {
+ QObjectPrivate::disconnect(d->upButton.data(), &QQuickAbstractButton::clicked,
+ d, &QQuickFolderBreadcrumbBarPrivate::goUp);
+ }
+
+ QQuickControlPrivate::hideOldItem(d->upButton);
+ d->upButton = upButton;
+ if (d->upButton) {
+ if (!d->upButton->parentItem())
+ d->upButton->setParentItem(this);
+
+ QObjectPrivate::connect(d->upButton.data(), &QQuickAbstractButton::clicked,
+ d, &QQuickFolderBreadcrumbBarPrivate::goUp);
+ }
+ if (!d->upButton.isExecuting())
+ emit upButtonChanged();
+}
+
+int QQuickFolderBreadcrumbBar::upButtonSpacing() const
+{
+ Q_D(const QQuickFolderBreadcrumbBar);
+ return d->upButtonSpacing;
+}
+
+void QQuickFolderBreadcrumbBar::setUpButtonSpacing(int upButtonSpacing)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ if (upButtonSpacing == d->upButtonSpacing)
+ return;
+
+ d->upButtonSpacing = upButtonSpacing;
+ emit upButtonSpacingChanged();
+}
+
+QQuickTextField *QQuickFolderBreadcrumbBar::textField()
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ return d->textField;
+}
+
+void QQuickFolderBreadcrumbBar::setTextField(QQuickTextField *textField)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ if (textField == d->textField)
+ return;
+
+ if (!d->textField.isExecuting())
+ d->cancelUpButton();
+
+ if (d->textField)
+ d->handleTextFieldHidden();
+
+ if (d->textField) {
+ QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::visibleChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged);
+ QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::activeFocusChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged);
+ QObjectPrivate::disconnect(d->textField.data(), &QQuickTextInput::accepted,
+ d, &QQuickFolderBreadcrumbBarPrivate::textFieldAccepted);
+ }
+
+ QQuickControlPrivate::hideOldItem(d->textField);
+ d->textField = textField;
+ if (d->textField) {
+ if (!d->textField->parentItem())
+ d->textField->setParentItem(this);
+
+ d->textField->setVisible(false);
+
+ QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::visibleChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged);
+ QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::activeFocusChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged);
+ QObjectPrivate::connect(d->textField.data(), &QQuickTextInput::accepted,
+ d, &QQuickFolderBreadcrumbBarPrivate::textFieldAccepted);
+ }
+ if (!d->textField.isExecuting())
+ emit textFieldChanged();
+}
+
+bool QQuickFolderBreadcrumbBar::event(QEvent *event)
+{
+#if QT_CONFIG(shortcut)
+ Q_D(QQuickFolderBreadcrumbBar);
+ if (event->type() == QEvent::Shortcut) {
+ QShortcutEvent *shortcutEvent = static_cast<QShortcutEvent *>(event);
+ if (shortcutEvent->shortcutId() == d->editPathToggleShortcutId
+ || shortcutEvent->shortcutId() == d->editPathBackShortcutId
+ || shortcutEvent->shortcutId() == d->editPathEscapeShortcutId) {
+ d->toggleTextFieldVisibility();
+ return true;
+ } else if (shortcutEvent->shortcutId() == d->goUpShortcutId) {
+ d->goUp();
+ }
+ }
+#endif
+ return QQuickItem::event(event);
+}
+
+void QQuickFolderBreadcrumbBar::componentComplete()
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ qCDebug(lcFolderBreadcrumbBar) << "componentComplete";
+ QQuickContainer::componentComplete();
+ d->repopulate();
+
+ if (d->textField) {
+ // Force it to be updated as setTextField() is too early to do it.
+ d->textFieldVisibleChanged();
+ }
+}
+
+void QQuickFolderBreadcrumbBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ QQuickContainer::itemChange(change, data);
+
+ if (change == QQuickItem::ItemVisibleHasChanged && isComponentComplete()) {
+ if (data.boolValue && d->fileDialog->isVisible()) {
+ // It's visible.
+ d->handleTextFieldHidden();
+
+ d->goUpShortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(
+ this, QKeySequence(Qt::ALT | Qt::Key_Up), Qt::WindowShortcut, QQuickShortcutContext::matcher);
+ } else {
+ // It's hidden.
+ // Hide the text field so that when the dialog gets opened again, it's not still visible.
+ if (d->textField)
+ d->textField->setVisible(false);
+
+ // Make the ListView visible again.
+ if (d->contentItem)
+ d->contentItem->setVisible(true);
+
+ // We also need to ungrab all shortcuts when we're not visible.
+ d->ungrabEditPathShortcuts();
+
+ if (d->goUpShortcutId != 0) {
+ QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->goUpShortcutId, this);
+ d->goUpShortcutId = 0;
+ }
+ }
+ }
+}
+
+bool QQuickFolderBreadcrumbBar::isContent(QQuickItem *item) const
+{
+ if (!qmlContext(item))
+ return false;
+
+ if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
+ return false;
+
+ return true;
+}
+
+QFont QQuickFolderBreadcrumbBar::defaultFont() const
+{
+ // TODO
+ return QQuickTheme::font(QQuickTheme::TabBar);
+}
+
+#if QT_CONFIG(accessibility)
+QAccessible::Role QQuickFolderBreadcrumbBar::accessibleRole() const
+{
+ // TODO
+ return QAccessible::PageTabList;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p.h
new file mode 100644
index 00000000..c04aa5a1
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFOLDERBREADCRUMBBAR_P_H
+#define QQUICKFOLDERBREADCRUMBBAR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
+
+#include "qquickfiledialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFolderBreadcrumbBarPrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderBreadcrumbBar : public QQuickContainer
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickFileDialogImpl *fileDialog READ fileDialog WRITE setFileDialog NOTIFY fileDialogChanged)
+ Q_PROPERTY(QQmlComponent *buttonDelegate READ buttonDelegate WRITE setButtonDelegate NOTIFY buttonDelegateChanged)
+ Q_PROPERTY(QQmlComponent *separatorDelegate READ separatorDelegate WRITE setSeparatorDelegate NOTIFY separatorDelegateChanged)
+ Q_PROPERTY(QQuickAbstractButton *upButton READ upButton WRITE setUpButton NOTIFY upButtonChanged)
+ Q_PROPERTY(QQuickTextField *textField READ textField WRITE setTextField NOTIFY textFieldChanged)
+ Q_PROPERTY(int upButtonSpacing READ upButtonSpacing WRITE setUpButtonSpacing NOTIFY upButtonSpacingChanged)
+ QML_NAMED_ELEMENT(FolderBreadcrumbBar)
+ QML_ADDED_IN_VERSION(6, 2)
+
+public:
+ explicit QQuickFolderBreadcrumbBar(QQuickItem *parent = nullptr);
+
+ QQuickFileDialogImpl *fileDialog() const;
+ void setFileDialog(QQuickFileDialogImpl *fileDialog);
+
+ QQmlComponent *buttonDelegate();
+ void setButtonDelegate(QQmlComponent *delegate);
+
+ QQmlComponent *separatorDelegate();
+ void setSeparatorDelegate(QQmlComponent *delegate);
+
+ QQuickAbstractButton *upButton();
+ void setUpButton(QQuickAbstractButton *upButton);
+
+ int upButtonSpacing() const;
+ void setUpButtonSpacing(int upButtonSpacing);
+
+ QQuickTextField *textField();
+ void setTextField(QQuickTextField *textField);
+
+Q_SIGNALS:
+ void fileDialogChanged();
+ void buttonDelegateChanged();
+ void separatorDelegateChanged();
+ void upButtonChanged();
+ void upButtonSpacingChanged();
+ void textFieldChanged();
+
+protected:
+ bool event(QEvent *event) override;
+
+ void componentComplete() override;
+
+ void itemChange(ItemChange change, const ItemChangeData &data) override;
+
+ bool isContent(QQuickItem *item) const override;
+
+ QFont defaultFont() const override;
+
+#if QT_CONFIG(accessibility)
+ QAccessible::Role accessibleRole() const override;
+#endif
+
+private:
+ Q_DISABLE_COPY(QQuickFolderBreadcrumbBar)
+ Q_DECLARE_PRIVATE(QQuickFolderBreadcrumbBar)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFolderBreadcrumbBar)
+
+#endif // QQUICKFOLDERBREADCRUMBBAR_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p_p.h
new file mode 100644
index 00000000..e4afc251
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickfolderbreadcrumbbar_p_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFOLDERBREADCRUMBBAR_P_P_H
+#define QQUICKFOLDERBREADCRUMBBAR_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickTemplates2/private/qquickcontainer_p_p.h>
+#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
+
+#include "qquickfiledialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAbstractButton;
+class QQuickTextField;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderBreadcrumbBarPrivate : public QQuickContainerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickFolderBreadcrumbBar)
+
+public:
+ QQuickItem *createDelegateItem(QQmlComponent *component, const QVariantMap &initialProperties);
+ static QString folderBaseName(const QString &folderPath);
+ static QStringList crumbPathsForFolder(const QUrl &folder);
+ void repopulate();
+ void crumbClicked();
+ void folderChanged();
+
+ void cancelUpButton();
+ void executeUpButton(bool complete = false);
+ void goUp();
+
+ void cancelTextField();
+ void executeTextField(bool complete = false);
+ void toggleTextFieldVisibility();
+ void textFieldAccepted();
+
+ void textFieldVisibleChanged();
+ void textFieldActiveFocusChanged();
+ void handleTextFieldShown();
+ void handleTextFieldHidden();
+ void ungrabEditPathShortcuts();
+
+ qreal getContentWidth() const override;
+ qreal getContentHeight() const override;
+ void resizeContent() override;
+
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
+ void itemImplicitWidthChanged(QQuickItem *item) override;
+ void itemImplicitHeightChanged(QQuickItem *item) override;
+
+private:
+ QQuickFileDialogImpl *fileDialog = nullptr;
+ QList<QString> folderPaths;
+ QQmlComponent *buttonDelegate = nullptr;
+ QQmlComponent *separatorDelegate = nullptr;
+ QQuickDeferredPointer<QQuickAbstractButton> upButton;
+ QQuickDeferredPointer<QQuickTextField> textField;
+ int editPathToggleShortcutId = 0;
+ int editPathBackShortcutId = 0;
+ int editPathEscapeShortcutId = 0;
+ int goUpShortcutId = 0;
+ int upButtonSpacing = 0;
+ bool repopulating = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFOLDERBREADCRUMBBAR_P_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp b/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp
new file mode 100644
index 00000000..530149a0
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickplatformfiledialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/qwindow.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupanchors_p.h>
+
+#include "qquickfiledialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQuickPlatformFileDialog, "qt.quick.dialogs.quickplatformfiledialog")
+
+/*!
+ \class QQuickPlatformFileDialog
+ \internal
+
+ An interface that QQuickFileDialog can use to access the non-native Qt Quick FileDialog.
+
+ Both this and the native implementations are created in QQuickAbstractDialog::create().
+*/
+QQuickPlatformFileDialog::QQuickPlatformFileDialog(QObject *parent)
+{
+ qCDebug(lcQuickPlatformFileDialog) << "creating non-native Qt Quick FileDialog with parent" << parent;
+
+ // Set a parent so that we get deleted if we can't be shown for whatever reason.
+ // Our eventual parent should be the window, though.
+ setParent(parent);
+
+ auto qmlContext = ::qmlContext(parent);
+ if (!qmlContext) {
+ qmlWarning(parent) << "No QQmlContext for QQuickPlatformFileDialog; can't create non-native FileDialog implementation";
+ return;
+ }
+
+ const auto dialogQmlUrl = QUrl(QStringLiteral("qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/qml/FileDialog.qml"));
+ QQmlComponent fileDialogComponent(qmlContext->engine(), dialogQmlUrl, parent);
+ if (!fileDialogComponent.isReady()) {
+ qmlWarning(parent) << "Failed to load non-native FileDialog implementation:\n" << fileDialogComponent.errorString();
+ return;
+ }
+ m_dialog = qobject_cast<QQuickFileDialogImpl*>(fileDialogComponent.create());
+ if (!m_dialog) {
+ qmlWarning(parent) << "Failed to create an instance of the non-native FileDialog:\n" << fileDialogComponent.errorString();
+ return;
+ }
+ // Give it a parent until it's parented to the window in show().
+ m_dialog->setParent(this);
+
+ connect(m_dialog, &QQuickDialog::accepted, this, &QPlatformDialogHelper::accept);
+ connect(m_dialog, &QQuickDialog::rejected, this, &QPlatformDialogHelper::reject);
+
+ connect(m_dialog, &QQuickFileDialogImpl::fileSelected, this, &QQuickPlatformFileDialog::fileSelected);
+ // TODO: add support for multiple file selection (QTBUG-92585)
+// connect(m_dialog, &QQuickFileDialogImpl::filesSelected, [this](const QList<QString> &files) {
+// QList<QUrl> urls;
+// urls.reserve(files.count());
+// for (const QString &file : files)
+// urls += QUrl::fromLocalFile(file);
+// emit filesSelected(urls);
+// });
+ connect(m_dialog, &QQuickFileDialogImpl::currentFileChanged, this, &QQuickPlatformFileDialog::currentChanged);
+ connect(m_dialog, &QQuickFileDialogImpl::currentFolderChanged, this, &QQuickPlatformFileDialog::directoryEntered);
+ connect(m_dialog, &QQuickFileDialogImpl::filterSelected, this, &QQuickPlatformFileDialog::filterSelected);
+
+ // We would do this in QQuickFileDialogImpl, but we need to ensure that folderChanged()
+ // is connected to directoryEntered() before setting it to ensure that the QQuickFileDialog is notified.
+ if (m_dialog->currentFolder().isEmpty())
+ m_dialog->setCurrentFolder(QUrl::fromLocalFile(QDir().absolutePath()));
+}
+
+bool QQuickPlatformFileDialog::isValid() const
+{
+ return m_dialog;
+}
+
+bool QQuickPlatformFileDialog::defaultNameFilterDisables() const
+{
+ return false;
+}
+
+void QQuickPlatformFileDialog::setDirectory(const QUrl &directory)
+{
+ if (!m_dialog)
+ return;
+
+ m_dialog->setCurrentFolder(directory);
+}
+
+QUrl QQuickPlatformFileDialog::directory() const
+{
+ if (!m_dialog)
+ return {};
+
+ return m_dialog->currentFolder();
+}
+
+void QQuickPlatformFileDialog::selectFile(const QUrl &file)
+{
+ if (!m_dialog)
+ return;
+
+ m_dialog->setSelectedFile(file);
+}
+
+QList<QUrl> QQuickPlatformFileDialog::selectedFiles() const
+{
+ // TODO: support for multiple selected files
+ return { m_dialog->currentFile() };
+}
+
+void QQuickPlatformFileDialog::setFilter()
+{
+}
+
+void QQuickPlatformFileDialog::selectNameFilter(const QString &filter)
+{
+ // There is a bit of a problem with order here - QQuickFileDialog::onShow()
+ // is called before our show(), but it needs to set the selected filter
+ // (which we can't do in our show() because we don't know about QQuickFileDialog).
+ // So, delay setting the filter until we're shown. This shouldn't be an issue
+ // in practice, since it doesn't make sense for the filter to programmatically
+ // change while the dialog is visible.
+ m_pendingNameFilter = filter;
+}
+
+QString QQuickPlatformFileDialog::selectedNameFilter() const
+{
+ return m_dialog->selectedNameFilter()->name();
+}
+
+void QQuickPlatformFileDialog::exec()
+{
+ qCWarning(lcQuickPlatformFileDialog) << "exec() is not supported for the Qt Quick FileDialog fallback";
+}
+
+bool QQuickPlatformFileDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
+{
+ qCDebug(lcQuickPlatformFileDialog) << "show called with flags" << flags <<
+ "modality" << modality << "parent" << parent;
+ if (!m_dialog)
+ return false;
+
+ if (!parent)
+ return false;
+
+ auto quickWindow = qobject_cast<QQuickWindow*>(parent);
+ if (!quickWindow) {
+ qmlInfo(this->parent()) << "Parent window (" << parent << ") of non-native dialog is not a QQuickWindow";
+ return false;
+ }
+ m_dialog->setParent(parent);
+ m_dialog->resetParentItem();
+
+ auto popupPrivate = QQuickPopupPrivate::get(m_dialog);
+ popupPrivate->getAnchors()->setCenterIn(m_dialog->parentItem());
+
+ QSharedPointer<QFileDialogOptions> options = QPlatformFileDialogHelper::options();
+ m_dialog->setTitle(options->windowTitle());
+ m_dialog->setOptions(options);
+ m_dialog->selectNameFilter(m_pendingNameFilter);
+ m_pendingNameFilter.clear();
+ m_dialog->setAcceptLabel(options->isLabelExplicitlySet(QFileDialogOptions::Accept)
+ ? options->labelText(QFileDialogOptions::Accept) : QString());
+ m_dialog->setRejectLabel(options->isLabelExplicitlySet(QFileDialogOptions::Reject)
+ ? options->labelText(QFileDialogOptions::Reject) : QString());
+
+ m_dialog->open();
+ return true;
+}
+
+void QQuickPlatformFileDialog::hide()
+{
+ if (!m_dialog)
+ return;
+
+ m_dialog->close();
+}
+
+QQuickFileDialogImpl *QQuickPlatformFileDialog::dialog() const
+{
+ return m_dialog;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog_p.h
new file mode 100644
index 00000000..80463531
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qquickplatformfiledialog_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPLATFORMFILEDIALOG_P_H
+#define QQUICKPLATFORMFILEDIALOG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpa/qplatformdialoghelper.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileDialogImpl;
+class QWindow;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformFileDialog : public QPlatformFileDialogHelper
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickPlatformFileDialog(QObject *parent);
+ ~QQuickPlatformFileDialog() = default;
+
+ bool isValid() const;
+ bool defaultNameFilterDisables() const override;
+ void setDirectory(const QUrl &directory) override;
+ QUrl directory() const override;
+ void selectFile(const QUrl &file) override;
+ QList<QUrl> selectedFiles() const override;
+ void setFilter() override;
+ void selectNameFilter(const QString &filter) override;
+ QString selectedNameFilter() const override;
+
+ void exec() override;
+ bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override;
+ void hide() override;
+
+ QQuickFileDialogImpl *dialog() const;
+
+private:
+ QQuickFileDialogImpl *m_dialog = nullptr;
+ QString m_pendingNameFilter;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPLATFORMFILEDIALOG_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplforeign_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplforeign_p.h
new file mode 100644
index 00000000..104199da
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplforeign_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQUICKDIALOGS2QUICKIMPLFOREIGN_P_H
+#define QTQUICKDIALOGS2QUICKIMPLFOREIGN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/qqml.h>
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
+#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquickicon_p.h>
+#include <QtQuickTemplates2/private/qquickpopup_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QQuickFileNameFilterForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickFileNameFilter)
+ QML_ADDED_IN_VERSION(6, 2)
+};
+
+// TODO: remove these ones when not needed (QTBUG-88179)
+
+// verticalPadding, etc.
+struct QQuickControlForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickControl)
+ QML_ADDED_IN_VERSION(2, 0)
+};
+
+struct QQuickAbstractButtonForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickAbstractButton)
+ QML_ADDED_IN_VERSION(2, 0)
+};
+
+struct QQuickIconForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickIcon)
+ QML_ADDED_IN_VERSION(6, 2)
+};
+
+// For leftInset, etc.
+struct QQuickPopupForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickPopup)
+ QML_ADDED_IN_VERSION(2, 0)
+};
+
+struct QQuickDialogForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickDialog)
+ QML_ADDED_IN_VERSION(2, 1)
+};
+
+QT_END_NAMESPACE
+
+#endif // QTQUICKDIALOGS2QUICKIMPLFOREIGN_P_H
diff --git a/src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplglobal_p.h b/src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplglobal_p.h
new file mode 100644
index 00000000..799c9cd0
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2quickimpl/qtquickdialogs2quickimplglobal_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQUICKDIALOGS2QUICKIMPLGLOBAL_P_H
+#define QTQUICKDIALOGS2QUICKIMPLGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtQml/private/qqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_STATIC
+# if defined(QT_BUILD_QUICKDIALOGS2QUICKIMPL_LIB)
+# define Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT
+#endif
+
+QT_END_NAMESPACE
+
+Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT void qml_register_types_QtQuick_Dialogs_quick_impl();
+
+#endif // QTQUICKDIALOGS2QUICKIMPLGLOBAL_P_H
diff --git a/src/quickdialogs2/quickdialogs2utils/CMakeLists.txt b/src/quickdialogs2/quickdialogs2utils/CMakeLists.txt
new file mode 100644
index 00000000..0242ffcf
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2utils/CMakeLists.txt
@@ -0,0 +1,23 @@
+# This library exists because QuickDialogs2 and QuickDialogs2QuickImpl
+# both need QQuickNameFilter. QQuickNameFilter was originally in
+# QuickDialogs2. Since QuickDialogs2 already links to
+# QuickDialogs2QuickImpl, making the latter link to the former (to get
+# access to QQuickNameFilter) would result in a circular dependency,
+# so we have this library as a result.
+
+qt_internal_add_module(QuickDialogs2Utils
+ SOURCES
+ qquickfilenamefilter.cpp
+ qquickfilenamefilter_p.h
+ qtquickdialogs2utilsglobal_p.h
+ DEFINES
+ QT_BUILD_QUICKDIALOGS2UTILS_LIB
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ LIBRARIES
+ Qt::GuiPrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+)
diff --git a/src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter.cpp b/src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter.cpp
new file mode 100644
index 00000000..8029977a
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickfilenamefilter_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcFileNameFilter, "qt.quick.dialogs.qquickfilenamefilter")
+
+QQuickFileNameFilter::QQuickFileNameFilter(QObject *parent)
+ : QObject(parent), m_index(-1)
+{
+}
+
+int QQuickFileNameFilter::index() const
+{
+ return m_index;
+}
+
+void QQuickFileNameFilter::setIndex(int index)
+{
+ if (m_index == index)
+ return;
+
+ m_index = index;
+ emit indexChanged(index);
+}
+
+QString QQuickFileNameFilter::name() const
+{
+ return m_name;
+}
+
+QStringList QQuickFileNameFilter::extensions() const
+{
+ return m_extensions;
+}
+
+QStringList QQuickFileNameFilter::globs() const
+{
+ return m_globs;
+}
+
+QSharedPointer<QFileDialogOptions> QQuickFileNameFilter::options() const
+{
+ return m_options;
+}
+
+void QQuickFileNameFilter::setOptions(const QSharedPointer<QFileDialogOptions> &options)
+{
+ m_options = options;
+}
+
+static QString extractName(const QString &filter)
+{
+ return filter.left(filter.indexOf(QLatin1Char('(')) - 1);
+}
+
+static QString extractExtension(QStringView filter)
+{
+ return filter.mid(filter.indexOf(QLatin1Char('.')) + 1).toString();
+}
+
+static void extractExtensionsAndGlobs(QStringView filter, QStringList &extensions, QStringList &globs)
+{
+ extensions.clear();
+ globs.clear();
+
+ const int from = filter.indexOf(QLatin1Char('('));
+ const int to = filter.lastIndexOf(QLatin1Char(')')) - 1;
+ if (from >= 0 && from < to) {
+ const QStringView ref = filter.mid(from + 1, to - from);
+ const QList<QStringView> exts = ref.split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ // For example, given the filter "HTML files (*.html *.htm)",
+ // "ref" would be "*.html" and "*.htm".
+ for (const QStringView &ref : exts) {
+ extensions += extractExtension(ref);
+ globs += ref.toString();
+ }
+ }
+}
+
+void QQuickFileNameFilter::update(const QString &filter)
+{
+ const QStringList filters = nameFilters();
+
+ const int oldIndex = m_index;
+ const QString oldName = m_name;
+ const QStringList oldExtensions = m_extensions;
+ const QStringList oldGlobs = m_globs;
+
+ m_index = filters.indexOf(filter);
+ m_name = extractName(filter);
+ extractExtensionsAndGlobs(filter, m_extensions, m_globs);
+
+ if (oldIndex != m_index)
+ emit indexChanged(m_index);
+ if (oldName != m_name)
+ emit nameChanged(m_name);
+ if (oldExtensions != m_extensions)
+ emit extensionsChanged(m_extensions);
+ if (oldGlobs != m_globs)
+ emit globsChanged(m_globs);
+
+ qCDebug(lcFileNameFilter).nospace() << "update called on " << this << " of " << parent()
+ << " with filter " << filter << " (current filters are " << filters << "):"
+ << "\n old index=" << oldIndex << "new index=" << m_index
+ << "\n old name=" << oldName << "new name=" << m_name
+ << "\n old extensions=" << oldExtensions << "new extensions=" << m_extensions
+ << "\n old glob=s" << oldGlobs << "new globs=" << m_globs;
+}
+
+QStringList QQuickFileNameFilter::nameFilters() const
+{
+ return m_options ? m_options->nameFilters() : QStringList();
+}
+
+QString QQuickFileNameFilter::nameFilter(int index) const
+{
+ return m_options ? m_options->nameFilters().value(index) : QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter_p.h b/src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter_p.h
new file mode 100644
index 00000000..61b9c07f
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2utils/qquickfilenamefilter_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Platform module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFILENAMEFILTER_P_H
+#define QQUICKFILENAMEFILTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qstringlist.h>
+#include <QtGui/qpa/qplatformdialoghelper.h>
+
+#include "qtquickdialogs2utilsglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2UTILS_PRIVATE_EXPORT QQuickFileNameFilter : 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)
+ Q_PROPERTY(QStringList globs READ globs NOTIFY globsChanged FINAL)
+
+public:
+ explicit QQuickFileNameFilter(QObject *parent = nullptr);
+
+ int index() const;
+ void setIndex(int index);
+
+ QString name() const;
+ QStringList extensions() const;
+ QStringList globs() const;
+
+ QSharedPointer<QFileDialogOptions> options() const;
+ void setOptions(const QSharedPointer<QFileDialogOptions> &options);
+
+ void update(const QString &filter);
+
+Q_SIGNALS:
+ void indexChanged(int index);
+ void nameChanged(const QString &name);
+ void extensionsChanged(const QStringList &extensions);
+ void globsChanged(const QStringList &globs);
+
+private:
+ QStringList nameFilters() const;
+ QString nameFilter(int index) const;
+
+ int m_index;
+ QString m_name;
+ QStringList m_extensions;
+ QStringList m_globs;
+ QSharedPointer<QFileDialogOptions> m_options;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFILENAMEFILTER_P_H
diff --git a/src/quickdialogs2/quickdialogs2utils/qtquickdialogs2utilsglobal_p.h b/src/quickdialogs2/quickdialogs2utils/qtquickdialogs2utilsglobal_p.h
new file mode 100644
index 00000000..7728db23
--- /dev/null
+++ b/src/quickdialogs2/quickdialogs2utils/qtquickdialogs2utilsglobal_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQUICKDIALOGS2UTILSGLOBAL_P_H
+#define QTQUICKDIALOGS2UTILSGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_STATIC
+# if defined(QT_BUILD_QUICKDIALOGS2UTILS_LIB)
+# define Q_QUICKDIALOGS2UTILS_PRIVATE_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QUICKDIALOGS2UTILS_PRIVATE_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_QUICKDIALOGS2UTILS_PRIVATE_EXPORT
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QTQUICKDIALOGS2UTILSUTILSGLOBAL_P_H
diff --git a/src/quicktemplates2/qquickdeferredexecute_p_p.h b/src/quicktemplates2/qquickdeferredexecute_p_p.h
index 29d95ee9..55750a6a 100644
--- a/src/quicktemplates2/qquickdeferredexecute_p_p.h
+++ b/src/quicktemplates2/qquickdeferredexecute_p_p.h
@@ -50,6 +50,7 @@
#include <QtCore/qglobal.h>
#include <QtQuickTemplates2/private/qquickdeferredpointer_p_p.h>
+#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h>
#include <QtQml/private/qqmlvme_p.h>
@@ -59,9 +60,9 @@ class QString;
class QObject;
namespace QtQuickPrivate {
- void beginDeferred(QObject *object, const QString &property);
- void cancelDeferred(QObject *object, const QString &property);
- void completeDeferred(QObject *object, const QString &property);
+ Q_QUICKTEMPLATES2_PRIVATE_EXPORT void beginDeferred(QObject *object, const QString &property);
+ Q_QUICKTEMPLATES2_PRIVATE_EXPORT void cancelDeferred(QObject *object, const QString &property);
+ Q_QUICKTEMPLATES2_PRIVATE_EXPORT void completeDeferred(QObject *object, const QString &property);
}
template<typename T>
diff --git a/src/quicktemplates2/qquickdialog.cpp b/src/quicktemplates2/qquickdialog.cpp
index 6d8bd2ad..a6cb0274 100644
--- a/src/quicktemplates2/qquickdialog.cpp
+++ b/src/quicktemplates2/qquickdialog.cpp
@@ -158,6 +158,18 @@ QPlatformDialogHelper::ButtonRole QQuickDialogPrivate::buttonRole(QQuickAbstract
return attached ? attached->buttonRole() : QPlatformDialogHelper::InvalidRole;
}
+void QQuickDialogPrivate::handleAccept()
+{
+ Q_Q(QQuickDialog);
+ q->accept();
+}
+
+void QQuickDialogPrivate::handleReject()
+{
+ Q_Q(QQuickDialog);
+ q->reject();
+}
+
void QQuickDialogPrivate::handleClick(QQuickAbstractButton *button)
{
Q_Q(QQuickDialog);
@@ -180,16 +192,21 @@ void QQuickDialogPrivate::handleClick(QQuickAbstractButton *button)
}
QQuickDialog::QQuickDialog(QObject *parent)
- : QQuickPopup(*(new QQuickDialogPrivate), parent)
+ : QQuickDialog(*(new QQuickDialogPrivate), parent)
+{
+}
+
+QQuickDialog::QQuickDialog(QQuickDialogPrivate &dd, QObject *parent)
+ : QQuickPopup(dd, parent)
{
Q_D(QQuickDialog);
- connect(d->popupItem, &QQuickPopupItem::titleChanged, this, &QQuickDialog::titleChanged);
- connect(d->popupItem, &QQuickPopupItem::headerChanged, this, &QQuickDialog::headerChanged);
- connect(d->popupItem, &QQuickPopupItem::footerChanged, this, &QQuickDialog::footerChanged);
- connect(d->popupItem, &QQuickPopupItem::implicitHeaderWidthChanged, this, &QQuickDialog::implicitHeaderWidthChanged);
- connect(d->popupItem, &QQuickPopupItem::implicitHeaderHeightChanged, this, &QQuickDialog::implicitHeaderHeightChanged);
- connect(d->popupItem, &QQuickPopupItem::implicitFooterWidthChanged, this, &QQuickDialog::implicitFooterWidthChanged);
- connect(d->popupItem, &QQuickPopupItem::implicitFooterHeightChanged, this, &QQuickDialog::implicitFooterHeightChanged);
+ QObject::connect(d->popupItem, &QQuickPopupItem::titleChanged, this, &QQuickDialog::titleChanged);
+ QObject::connect(d->popupItem, &QQuickPopupItem::headerChanged, this, &QQuickDialog::headerChanged);
+ QObject::connect(d->popupItem, &QQuickPopupItem::footerChanged, this, &QQuickDialog::footerChanged);
+ QObject::connect(d->popupItem, &QQuickPopupItem::implicitHeaderWidthChanged, this, &QQuickDialog::implicitHeaderWidthChanged);
+ QObject::connect(d->popupItem, &QQuickPopupItem::implicitHeaderHeightChanged, this, &QQuickDialog::implicitHeaderHeightChanged);
+ QObject::connect(d->popupItem, &QQuickPopupItem::implicitFooterWidthChanged, this, &QQuickDialog::implicitFooterWidthChanged);
+ QObject::connect(d->popupItem, &QQuickPopupItem::implicitFooterHeightChanged, this, &QQuickDialog::implicitFooterHeightChanged);
}
/*!
@@ -251,16 +268,16 @@ void QQuickDialog::setHeader(QQuickItem *header)
return;
if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(oldHeader)) {
- disconnect(buttonBox, &QQuickDialogButtonBox::accepted, this, &QQuickDialog::accept);
- disconnect(buttonBox, &QQuickDialogButtonBox::rejected, this, &QQuickDialog::reject);
+ QObjectPrivate::disconnect(buttonBox, &QQuickDialogButtonBox::accepted, d, &QQuickDialogPrivate::handleAccept);
+ QObjectPrivate::disconnect(buttonBox, &QQuickDialogButtonBox::rejected, d, &QQuickDialogPrivate::handleReject);
QObjectPrivate::disconnect(buttonBox, &QQuickDialogButtonBox::clicked, d, &QQuickDialogPrivate::handleClick);
if (d->buttonBox == buttonBox)
d->buttonBox = nullptr;
}
if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(header)) {
- connect(buttonBox, &QQuickDialogButtonBox::accepted, this, &QQuickDialog::accept);
- connect(buttonBox, &QQuickDialogButtonBox::rejected, this, &QQuickDialog::reject);
+ QObjectPrivate::connect(buttonBox, &QQuickDialogButtonBox::accepted, d, &QQuickDialogPrivate::handleAccept);
+ QObjectPrivate::connect(buttonBox, &QQuickDialogButtonBox::rejected, d, &QQuickDialogPrivate::handleReject);
QObjectPrivate::connect(buttonBox, &QQuickDialogButtonBox::clicked, d, &QQuickDialogPrivate::handleClick);
d->buttonBox = buttonBox;
buttonBox->setStandardButtons(d->standardButtons);
@@ -299,15 +316,15 @@ void QQuickDialog::setFooter(QQuickItem *footer)
return;
if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(oldFooter)) {
- disconnect(buttonBox, &QQuickDialogButtonBox::accepted, this, &QQuickDialog::accept);
- disconnect(buttonBox, &QQuickDialogButtonBox::rejected, this, &QQuickDialog::reject);
+ QObjectPrivate::disconnect(buttonBox, &QQuickDialogButtonBox::accepted, d, &QQuickDialogPrivate::handleAccept);
+ QObjectPrivate::disconnect(buttonBox, &QQuickDialogButtonBox::rejected, d, &QQuickDialogPrivate::handleReject);
QObjectPrivate::disconnect(buttonBox, &QQuickDialogButtonBox::clicked, d, &QQuickDialogPrivate::handleClick);
if (d->buttonBox == buttonBox)
d->buttonBox = nullptr;
}
if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(footer)) {
- connect(buttonBox, &QQuickDialogButtonBox::accepted, this, &QQuickDialog::accept);
- connect(buttonBox, &QQuickDialogButtonBox::rejected, this, &QQuickDialog::reject);
+ QObjectPrivate::connect(buttonBox, &QQuickDialogButtonBox::accepted, d, &QQuickDialogPrivate::handleAccept);
+ QObjectPrivate::connect(buttonBox, &QQuickDialogButtonBox::rejected, d, &QQuickDialogPrivate::handleReject);
QObjectPrivate::connect(buttonBox, &QQuickDialogButtonBox::clicked, d, &QQuickDialogPrivate::handleClick);
d->buttonBox = buttonBox;
buttonBox->setStandardButtons(d->standardButtons);
diff --git a/src/quicktemplates2/qquickdialog_p.h b/src/quicktemplates2/qquickdialog_p.h
index 660da081..b09375ce 100644
--- a/src/quicktemplates2/qquickdialog_p.h
+++ b/src/quicktemplates2/qquickdialog_p.h
@@ -129,6 +129,8 @@ Q_SIGNALS:
void implicitFooterHeightChanged();
protected:
+ QQuickDialog(QQuickDialogPrivate &dd, QObject *parent);
+
#if QT_CONFIG(accessibility)
QAccessible::Role accessibleRole() const override;
void accessibilityActiveChanged(bool active) override;
diff --git a/src/quicktemplates2/qquickdialog_p_p.h b/src/quicktemplates2/qquickdialog_p_p.h
index 20dce56c..eb40c918 100644
--- a/src/quicktemplates2/qquickdialog_p_p.h
+++ b/src/quicktemplates2/qquickdialog_p_p.h
@@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractButton;
class QQuickDialogButtonBox;
-class QQuickDialogPrivate : public QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogPrivate : public QQuickPopupPrivate
{
Q_DECLARE_PUBLIC(QQuickDialog)
@@ -69,7 +69,9 @@ public:
static QPlatformDialogHelper::ButtonRole buttonRole(QQuickAbstractButton *button);
- void handleClick(QQuickAbstractButton *button);
+ virtual void handleAccept();
+ virtual void handleReject();
+ virtual void handleClick(QQuickAbstractButton *button);
int result = 0;
QString title;
diff --git a/src/quicktemplates2/qquickdialogbuttonbox.cpp b/src/quicktemplates2/qquickdialogbuttonbox.cpp
index d6ed366f..447d3a2d 100644
--- a/src/quicktemplates2/qquickdialogbuttonbox.cpp
+++ b/src/quicktemplates2/qquickdialogbuttonbox.cpp
@@ -396,6 +396,11 @@ void QQuickDialogButtonBoxPrivate::handleClick()
}
}
+QString QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::StandardButton standardButton)
+{
+ return QPlatformTheme::removeMnemonics(QGuiApplicationPrivate::platformTheme()->standardButtonText(standardButton));
+}
+
QQuickAbstractButton *QQuickDialogButtonBoxPrivate::createStandardButton(QPlatformDialogHelper::StandardButton standardButton)
{
Q_Q(QQuickDialogButtonBox);
@@ -414,7 +419,7 @@ QQuickAbstractButton *QQuickDialogButtonBoxPrivate::createStandardButton(QPlatfo
QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(qmlAttachedPropertiesObject<QQuickDialogButtonBox>(button, true));
QQuickDialogButtonBoxAttachedPrivate::get(attached)->standardButton = standardButton;
attached->setButtonRole(QPlatformDialogHelper::buttonRole(standardButton));
- button->setText(QPlatformTheme::removeMnemonics(QGuiApplicationPrivate::platformTheme()->standardButtonText(standardButton)));
+ button->setText(buttonText(standardButton));
delegate->completeCreate();
return button;
}
@@ -457,8 +462,7 @@ void QQuickDialogButtonBoxPrivate::updateLanguage()
const QPlatformDialogHelper::StandardButton standardButton = boxAttachedPrivate->standardButton;
// The button might be a custom one with explicitly specified text, so we shouldn't change it in that case.
if (standardButton != QPlatformDialogHelper::NoButton) {
- const QString buttonText = QGuiApplicationPrivate::platformTheme()->standardButtonText(standardButton);
- button->setText(QPlatformTheme::removeMnemonics(buttonText));
+ button->setText(buttonText(standardButton));
}
}
--i;
diff --git a/src/quicktemplates2/qquickdialogbuttonbox_p_p.h b/src/quicktemplates2/qquickdialogbuttonbox_p_p.h
index 22da9e6d..5e08b2ad 100644
--- a/src/quicktemplates2/qquickdialogbuttonbox_p_p.h
+++ b/src/quicktemplates2/qquickdialogbuttonbox_p_p.h
@@ -53,7 +53,7 @@
QT_BEGIN_NAMESPACE
-class QQuickDialogButtonBoxPrivate : public QQuickContainerPrivate
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBoxPrivate : public QQuickContainerPrivate
{
Q_DECLARE_PUBLIC(QQuickDialogButtonBox)
@@ -75,6 +75,8 @@ public:
void handleClick();
+ static QString buttonText(QPlatformDialogHelper::StandardButton standardButton);
+
QQuickAbstractButton *createStandardButton(QPlatformDialogHelper::StandardButton button);
void removeStandardButtons();
diff --git a/src/quicktemplates2/qquickitemdelegate_p_p.h b/src/quicktemplates2/qquickitemdelegate_p_p.h
index f712a212..d7604fbe 100644
--- a/src/quicktemplates2/qquickitemdelegate_p_p.h
+++ b/src/quicktemplates2/qquickitemdelegate_p_p.h
@@ -52,7 +52,7 @@
QT_BEGIN_NAMESPACE
-class QQuickItemDelegatePrivate : public QQuickAbstractButtonPrivate
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickItemDelegatePrivate : public QQuickAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickItemDelegate)
diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp
index 292d52a3..44d0d8f4 100644
--- a/src/quicktemplates2/qquickpopup.cpp
+++ b/src/quicktemplates2/qquickpopup.cpp
@@ -44,6 +44,7 @@
#include "qquickcontrol_p_p.h"
#include "qquickdialog_p.h"
+#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlinfo.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquicktransition_p.h>
@@ -52,6 +53,7 @@
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcDimmer, "qt.quick.controls.popup.dimmer")
+Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
/*!
\qmltype Popup
@@ -2450,6 +2452,7 @@ void QQuickPopup::classBegin()
void QQuickPopup::componentComplete()
{
Q_D(QQuickPopup);
+ qCDebug(lcPopup) << "componentComplete";
if (!parentItem())
resetParentItem();
diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h
index 57183011..5ce1d291 100644
--- a/src/quicktemplates2/qquickpopup_p_p.h
+++ b/src/quicktemplates2/qquickpopup_p_p.h
@@ -68,7 +68,7 @@ class QQuickPopupItem;
class QQuickPopupPrivate;
class QQuickPopupPositioner;
-class QQuickPopupTransitionManager : public QQuickTransitionManager
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupTransitionManager : public QQuickTransitionManager
{
public:
QQuickPopupTransitionManager(QQuickPopupPrivate *popup);
@@ -83,7 +83,7 @@ private:
QQuickPopupPrivate *popup = nullptr;
};
-class Q_AUTOTEST_EXPORT QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupPrivate
: public QObjectPrivate
, public QQuickItemChangeListener
, public QQuickPaletteProviderPrivateBase<QQuickPopup, QQuickPopupPrivate>
diff --git a/src/quicktemplates2/qquickpopupitem_p.h b/src/quicktemplates2/qquickpopupitem_p.h
index db444ddc..2947a6e3 100644
--- a/src/quicktemplates2/qquickpopupitem_p.h
+++ b/src/quicktemplates2/qquickpopupitem_p.h
@@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE
class QQuickPopup;
class QQuickPopupItemPrivate;
-class QQuickPopupItem : public QQuickPage
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupItem : public QQuickPage
{
Q_OBJECT
diff --git a/src/quicktemplates2/qquickpopupitem_p_p.h b/src/quicktemplates2/qquickpopupitem_p_p.h
index b3224be7..48b35aa2 100644
--- a/src/quicktemplates2/qquickpopupitem_p_p.h
+++ b/src/quicktemplates2/qquickpopupitem_p_p.h
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
class QQuickPopup;
-class QQuickPopupItemPrivate : public QQuickPagePrivate
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupItemPrivate : public QQuickPagePrivate
{
Q_DECLARE_PUBLIC(QQuickPopupItem)
diff --git a/sync.profile b/sync.profile
index dc6ea940..cfc77d99 100644
--- a/sync.profile
+++ b/sync.profile
@@ -1,6 +1,9 @@
%modules = ( # path to module name map
"QtQuickControls2" => "$basedir/src/quickcontrols2",
"QtQuickControls2Impl" => "$basedir/src/quickcontrols2impl",
+ "QtQuickDialogs2" => "$basedir/src/quickdialogs2/quickdialogs2",
+ "QtQuickDialogs2Utils" => "$basedir/src/quickdialogs2/quickdialogs2utils",
+ "QtQuickDialogs2QuickImpl" => "$basedir/src/quickdialogs2/quickdialogs2quickimpl",
"QtQuickTemplates2" => "$basedir/src/quicktemplates2",
);
%moduleheaders = ( # restrict the module headers to those found in relative path
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index 5d7302c3..ef299bb7 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(controls)
add_subdirectory(cursor)
add_subdirectory(customization)
add_subdirectory(designer)
+add_subdirectory(dialogs)
add_subdirectory(focus)
add_subdirectory(font)
add_subdirectory(palette)
diff --git a/tests/auto/dialogs/CMakeLists.txt b/tests/auto/dialogs/CMakeLists.txt
new file mode 100644
index 00000000..26eb933f
--- /dev/null
+++ b/tests/auto/dialogs/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(qquickfiledialogimpl)
diff --git a/tests/auto/dialogs/dialogs.pro b/tests/auto/dialogs/dialogs.pro
new file mode 100644
index 00000000..004641ec
--- /dev/null
+++ b/tests/auto/dialogs/dialogs.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS += \
+ qquickfiledialogimpl
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/BLACKLIST b/tests/auto/dialogs/qquickfiledialogimpl/BLACKLIST
new file mode 100644
index 00000000..822b92dd
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/BLACKLIST
@@ -0,0 +1,7 @@
+# QTBUG-92094
+[tabFocusNavigation]
+*
+
+# QTBUG-92585
+[fileMode:OpenFiles]
+*
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/CMakeLists.txt b/tests/auto/dialogs/qquickfiledialogimpl/CMakeLists.txt
new file mode 100644
index 00000000..631cb9e8
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qquickfiledialogimpl
+ SOURCES
+ ../../shared/qtest_quickcontrols.h
+ ../../shared/util.cpp ../../shared/util.h
+ ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h
+ tst_qquickfiledialogimpl.cpp
+ DEFINES
+ QQC2_IMPORT_PATH=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/imports\\\"
+ PUBLIC_LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::QuickControls2
+ Qt::QuickControls2Private
+ Qt::QuickDialogs2Private
+ Qt::QuickDialogs2QuickImplPrivate
+ Qt::QuickDialogs2UtilsPrivate
+ Qt::QuickPrivate
+ Qt::QuickTemplates2Private
+ Qt::QuickTest
+ Qt::TestPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qquickfiledialogimpl CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\":/data\\\"
+)
+
+qt_internal_extend_target(tst_qquickfiledialogimpl CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
+)
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml b/tests/auto/dialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml
new file mode 100644
index 00000000..3cd526e3
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/data/acceptRejectLabel.qml
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ id: window
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+
+ FileDialog {
+ id: dialog
+ acceptLabel: "AcceptTest"
+ rejectLabel: "RejectTest"
+ }
+}
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml b/tests/auto/dialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml
new file mode 100644
index 00000000..5010c422
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/data/bindAllTxtHtmlNameFilters.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+
+ FileDialog {
+ id: dialog
+ objectName: "FileDialog"
+ nameFilters: ["All files (*)", "Text files (*.txt)", "HTML files (*.html)"]
+ }
+}
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml b/tests/auto/dialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml
new file mode 100644
index 00000000..03cd61bc
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/data/bindCurrentFolder.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ id: window
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+ required property url initialFolder
+
+ FileDialog {
+ id: dialog
+ objectName: "FileDialog"
+ currentFolder: window.initialFolder
+ }
+}
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/data/bindTitle.qml b/tests/auto/dialogs/qquickfiledialogimpl/data/bindTitle.qml
new file mode 100644
index 00000000..60657f58
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/data/bindTitle.qml
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+
+ FileDialog {
+ id: dialog
+ title: "Test Title"
+ }
+}
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml b/tests/auto/dialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml
new file mode 100644
index 00000000..1e312fbc
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/data/bindTxtHtmlNameFilters.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+
+ FileDialog {
+ id: dialog
+ objectName: "FileDialog"
+ nameFilters: ["Text files (*.txt)", "HTML files (*.html)"]
+ }
+}
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/data/fileDialog.qml b/tests/auto/dialogs/qquickfiledialogimpl/data/fileDialog.qml
new file mode 100644
index 00000000..febad45c
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/data/fileDialog.qml
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ width: 640
+ height: 480
+
+ property alias dialog: dialog
+
+ function doneAccepted() {
+ dialog.done(FileDialog.Accepted)
+ }
+
+ function doneRejected() {
+ dialog.done(FileDialog.Rejected)
+ }
+
+ FileDialog {
+ id: dialog
+ objectName: "FileDialog"
+ }
+}
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/qquickfiledialogimpl.pro b/tests/auto/dialogs/qquickfiledialogimpl/qquickfiledialogimpl.pro
new file mode 100644
index 00000000..3f6e83d7
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/qquickfiledialogimpl.pro
@@ -0,0 +1,14 @@
+CONFIG += testcase
+TARGET = tst_qquickfiledialogimpl
+SOURCES += tst_qquickfiledialogimpl.cpp
+
+macos:CONFIG -= app_bundle
+
+QT += core-private gui-private testlib qml-private quick-private qmltest quicktemplates2-private
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+OTHER_FILES += \
+ data/*.qml
diff --git a/tests/auto/dialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/dialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
new file mode 100644
index 00000000..a8fe4c15
--- /dev/null
+++ b/tests/auto/dialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp
@@ -0,0 +1,1349 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../../shared/dialogtestutil.h"
+#include "../../shared/util.h"
+#include "../../shared/visualtestutil.h"
+
+#include <QtTest/qtest.h>
+#include <QtTest/qsignalspy.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuickTest/quicktest.h>
+#include <QtQuickDialogs2/private/qquickfiledialog_p.h>
+#include <QtQuickDialogs2QuickImpl/private/qquickplatformfiledialog_p.h>
+#include <QtQuickDialogs2QuickImpl/private/qquickfiledialogdelegate_p.h>
+#include <QtQuickDialogs2QuickImpl/private/qquickfolderbreadcrumbbar_p.h>
+#include <QtQuickDialogs2QuickImpl/private/qquickfolderbreadcrumbbar_p_p.h>
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
+#include <QtQuickTemplates2/private/qquickcombobox_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
+#include <QtQuickTemplates2/private/qquicklabel_p.h>
+#include <QtQuickTemplates2/private/qquickoverlay_p.h>
+
+using namespace QQuickDialogTestUtil;
+using namespace QQuickVisualTestUtil;
+
+class tst_QQuickFileDialogImpl : public QQmlDataTest
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase() override;
+ void cleanupTestCase();
+
+ void defaults();
+ void chooseFileViaStandardButtons();
+ void chooseFileViaDoubleClick();
+ void chooseFileViaTextEdit();
+ void chooseFileViaEnter();
+ void bindCurrentFolder_data();
+ void bindCurrentFolder();
+ void changeFolderViaStandardButtons();
+ void changeFolderViaDoubleClick();
+ void chooseFolderViaTextEdit();
+ void chooseFolderViaEnter();
+ void cancelDialogWhileTextEditHasFocus();
+ void goUp();
+ void goUpWhileTextEditHasFocus();
+ void goIntoLargeFolder();
+ void keyAndShortcutHandling();
+ void bindNameFilters();
+ void changeNameFilters();
+ void changeNameFiltersAfterChangingFolder();
+ void tabFocusNavigation();
+ void acceptRejectLabel();
+ void bindTitle();
+ void itemsDisabledWhenNecessary();
+ void fileMode_data();
+ void fileMode();
+ void defaultSuffix_data();
+ void defaultSuffix();
+ void done_data();
+ void done();
+
+private:
+ QQuickAbstractButton *findDialogButton(QQuickDialogButtonBox *box, const QString &buttonText);
+ void enterText(QWindow *window, const QString &textToEnter);
+
+ QTemporaryDir tempDir;
+ QScopedPointer<QFile> tempFile1;
+ QScopedPointer<QFile> tempFile2;
+ QDir tempSubDir;
+ QDir tempSubSubDir;
+ QScopedPointer<QFile> tempSubFile1;
+ QScopedPointer<QFile> tempSubFile2;
+ QDir oldCurrentDir;
+};
+
+QQuickAbstractButton *tst_QQuickFileDialogImpl::findDialogButton(QQuickDialogButtonBox *box, const QString &buttonText)
+{
+ for (int i = 0; i < box->count(); ++i) {
+ auto button = qobject_cast<QQuickAbstractButton*>(box->itemAt(i));
+ if (button && button->text().toUpper() == buttonText.toUpper())
+ return button;
+ }
+ return nullptr;
+}
+
+void tst_QQuickFileDialogImpl::enterText(QWindow *window, const QString &textToEnter)
+{
+ for (int i = 0; i < textToEnter.size(); ++i) {
+ const QChar key = textToEnter.at(i);
+ QTest::keyClick(window, key.toLatin1());
+ }
+}
+
+void tst_QQuickFileDialogImpl::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+
+ QVERIFY(tempDir.isValid());
+ // QTEST_QUICKCONTROLS_MAIN constructs the test case object once,
+ // and then calls qRun() for each style, and qRun() calls initTestCase().
+ // So, we need to check if we've already made the temporary directory.
+ // Note that this is only necessary if the test is run with more than one style.
+ if (!QDir(tempDir.path()).isEmpty())
+ return;
+
+ /*
+ Create a couple of files within the temporary directory. The structure is:
+
+ [temp directory]
+ ├── sub-dir
+ │ ├── sub-sub-dir
+ │ ├── sub-file1.txt
+ │ └── sub-file2.txt
+ ├── file1.txt
+ └── file2.txt
+ */
+ tempSubDir = QDir(tempDir.path());
+ QVERIFY2(tempSubDir.mkdir("sub-dir"), qPrintable(QString::fromLatin1(
+ "Failed to make sub-directory \"sub-dir\" in %1. Permissions are: %2")
+ .arg(tempDir.path()).arg(QDebug::toString(QFileInfo(tempDir.path()).permissions()))));
+ QVERIFY(tempSubDir.cd("sub-dir"));
+
+ tempSubSubDir = QDir(tempSubDir.path());
+ QVERIFY2(tempSubSubDir.mkdir("sub-sub-dir"), qPrintable(QString::fromLatin1(
+ "Failed to make sub-directory \"sub-sub-dir\" in %1. Permissions are: %2")
+ .arg(tempSubDir.path()).arg(QDebug::toString(QFileInfo(tempSubDir.path()).permissions()))));
+ QVERIFY(tempSubSubDir.cd("sub-sub-dir"));
+
+ tempSubFile1.reset(new QFile(tempSubDir.path() + "/sub-file1.txt"));
+ QVERIFY(tempSubFile1->open(QIODevice::ReadWrite));
+
+ tempSubFile2.reset(new QFile(tempSubDir.path() + "/sub-file2.txt"));
+ QVERIFY(tempSubFile2->open(QIODevice::ReadWrite));
+
+ tempFile1.reset(new QFile(tempDir.path() + "/file1.txt"));
+ QVERIFY(tempFile1->open(QIODevice::ReadWrite));
+
+ tempFile2.reset(new QFile(tempDir.path() + "/file2.txt"));
+ QVERIFY(tempFile2->open(QIODevice::ReadWrite));
+
+ // Ensure that each test starts off in the temporary directory.
+ oldCurrentDir = QDir::current();
+ QDir::setCurrent(tempDir.path());
+}
+
+void tst_QQuickFileDialogImpl::cleanupTestCase()
+{
+ // Just in case...
+ QDir::setCurrent(oldCurrentDir.path());
+}
+
+bool verifyFileDialogDelegates(QQuickListView *fileDialogListView, const QStringList &expectedFiles,
+ QString &failureMessage)
+{
+ if (QQuickTest::qIsPolishScheduled(fileDialogListView)) {
+ if (!QQuickTest::qWaitForItemPolished(fileDialogListView)) {
+ failureMessage = QLatin1String("Failed to polish fileDialogListView");
+ return false;
+ }
+ }
+
+ QStringList actualFiles;
+ for (int i = 0; i < fileDialogListView->count(); ++i) {
+ auto delegate = qobject_cast<QQuickFileDialogDelegate*>(findViewDelegateItem(fileDialogListView, i));
+ if (!delegate) {
+ failureMessage = QString::fromLatin1("Delegate at index %1 is null").arg(i);
+ return false;
+ }
+
+ // Need to call absoluteFilePath on Windows; see comment in dialogtestutil.h.
+ actualFiles.append(QFileInfo(delegate->file().toLocalFile()).absoluteFilePath());
+ }
+
+ if (actualFiles != expectedFiles) {
+ failureMessage = QString::fromLatin1("Mismatch in actual vs expected "
+ "delegates in fileDialogListView:\n expected: %1\n actual: %2")
+ .arg(QDebug::toString(expectedFiles)).arg(QDebug::toString(actualFiles));
+ return false;
+ }
+
+ return true;
+}
+
+bool verifyBreadcrumbDelegates(QQuickFolderBreadcrumbBar *breadcrumbBar, const QUrl &expectedFolder,
+ QString &failureMessage)
+{
+ if (!breadcrumbBar) {
+ failureMessage = QLatin1String("breadcrumbBar is null");
+ return false;
+ }
+
+ auto breadcrumbBarListView = qobject_cast<QQuickListView*>(breadcrumbBar->contentItem());
+ if (!breadcrumbBarListView) {
+ failureMessage = QLatin1String("breadcrumbBar's ListView is null");
+ return false;
+ }
+
+ if (QQuickTest::qIsPolishScheduled(breadcrumbBarListView)) {
+ if (!QQuickTest::qWaitForItemPolished(breadcrumbBarListView)) {
+ failureMessage = QLatin1String("Failed to polish breadcrumbBarListView");
+ return false;
+ }
+ }
+
+ QStringList actualCrumbs;
+ for (int i = 0; i < breadcrumbBarListView->count(); ++i) {
+ auto delegate = qobject_cast<QQuickAbstractButton*>(findViewDelegateItem(breadcrumbBarListView, i));
+ if (!delegate) {
+ // It's a separator or some other non-crumb item.
+ continue;
+ }
+
+ actualCrumbs.append(delegate->text());
+ }
+
+ QStringList expectedCrumbs = QQuickFolderBreadcrumbBarPrivate::crumbPathsForFolder(expectedFolder);
+ for (int i = 0; i < expectedCrumbs.size(); ++i) {
+ QString &crumbPath = expectedCrumbs[i];
+ crumbPath = QQuickFolderBreadcrumbBarPrivate::folderBaseName(crumbPath);
+ }
+
+ if (actualCrumbs != expectedCrumbs) {
+ failureMessage = QString::fromLatin1("Mismatch in actual vs expected "
+ "delegates in breadcrumbBarListView:\n expected: %1\n actual: %2")
+ .arg(QDebug::toString(expectedCrumbs)).arg(QDebug::toString(actualCrumbs));
+ return false;
+ }
+
+ return true;
+}
+
+void tst_QQuickFileDialogImpl::defaults()
+{
+ QQuickApplicationHelper helper(this, "fileDialog.qml");
+ QVERIFY2(helper.ready, helper.failureMessage());
+
+ QQuickWindow *window = helper.window;
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ QQuickFileDialog *dialog = window->property("dialog").value<QQuickFileDialog*>();
+ QVERIFY(dialog);
+ COMPARE_URL(dialog->selectedFile(), QUrl());
+ // It should default to the current directory.
+ COMPARE_URL(dialog->currentFolder(), QUrl::fromLocalFile(QDir().absolutePath()));
+ // The first file in the directory should be selected, but not until the dialog is actually open,
+ // as QQuickFileDialogImpl hasn't been created yet.
+ COMPARE_URL(dialog->currentFile(), QUrl());
+ COMPARE_URLS(dialog->currentFiles(), {});
+ QCOMPARE(dialog->title(), QString());
+
+ dialog->open();
+ QQuickFileDialogImpl *quickDialog = window->findChild<QQuickFileDialogImpl*>();
+ QTRY_VERIFY(quickDialog->isOpened());
+ QVERIFY(quickDialog);
+ COMPARE_URL(quickDialog->selectedFile(), QUrl());
+ COMPARE_URL(quickDialog->currentFolder(), QUrl::fromLocalFile(QDir().absolutePath()));
+ COMPARE_URL(dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URLS(dialog->currentFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
+ COMPARE_URL(quickDialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
+ QCOMPARE(quickDialog->title(), QString());
+}
+
+void tst_QQuickFileDialogImpl::chooseFileViaStandardButtons()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Select the delegate by clicking once.
+ QSignalSpy dialogFileChangedSpy(dialogHelper.dialog, SIGNAL(selectedFileChanged()));
+ QVERIFY(dialogFileChangedSpy.isValid());
+ QSignalSpy dialogCurrentFileChangedSpy(dialogHelper.dialog, SIGNAL(currentFileChanged()));
+ QVERIFY(dialogCurrentFileChangedSpy.isValid());
+ QSignalSpy quickDialogCurrentFileChangedSpy(dialogHelper.quickDialog, SIGNAL(currentFileChanged(const QUrl &)));
+ QVERIFY(quickDialogCurrentFileChangedSpy.isValid());
+
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 2, delegate));
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()));
+ QVERIFY(clickButton(delegate));
+ COMPARE_URL(dialogHelper.quickDialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ // Only currentFile-related signals should be emitted.
+ QCOMPARE(dialogFileChangedSpy.count(), 0);
+ QCOMPARE(dialogCurrentFileChangedSpy.count(), 1);
+ QCOMPARE(quickDialogCurrentFileChangedSpy.count(), 1);
+
+ // Click the "Open" button.
+ QVERIFY(dialogHelper.quickDialog->footer());
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open");
+ QVERIFY(openButton);
+ QVERIFY(clickButton(openButton));
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ QCOMPARE(dialogFileChangedSpy.count(), 1);
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ QVERIFY(!dialogHelper.dialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::chooseFileViaDoubleClick()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Select the delegate by double-clicking.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 2, delegate));
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()))
+ QVERIFY(doubleClickButton(delegate));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()))
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) })
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()))
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) })
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::chooseFileViaTextEdit()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Get the text edit visible with Ctrl+L.
+ const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY(breadcrumbBar->textField()->isVisible());
+ QCOMPARE(breadcrumbBar->textField()->text(), dialogHelper.dialog->currentFolder().toLocalFile());
+ QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
+
+ // Enter the path to the file in the text edit.
+ enterText(dialogHelper.window(), tempFile2->fileName());
+ QCOMPARE(breadcrumbBar->textField()->text(), tempFile2->fileName());
+
+ // Hit enter to accept.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ COMPARE_URL(dialogHelper.quickDialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::chooseFileViaEnter()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Before moving down, the first delegate in the view should be selected and have focus.
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *subDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
+ QVERIFY(subDirDelegate->hasActiveFocus());
+
+ // Select the first file in the view by navigating with the down key.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Down);
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+
+ // Select the delegate by pressing enter.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(tempFile1->fileName()) });
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ QCOMPARE(dialogHelper.dialog->result(), QQuickFileDialog::Accepted);
+}
+
+void tst_QQuickFileDialogImpl::bindCurrentFolder_data()
+{
+ QTest::addColumn<QUrl>("initialFolder");
+ QTest::addColumn<QUrl>("expectedFolder");
+ QTest::addColumn<QStringList>("expectedVisibleFiles");
+
+ const auto currentDirUrl = QUrl::fromLocalFile(QDir::current().path());
+ const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir.path());
+ const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName());
+
+ const QStringList currentDirFiles = { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() };
+ const QStringList tempSubDirFiles = { tempSubSubDir.path(), tempSubFile1->fileName(), tempSubFile2->fileName() };
+
+ // Setting the folder to "sub-dir" should result in "sub-file1.txt" and "sub-file2.txt" being visible.
+ QTest::addRow("sub-dir") << tempSubDirUrl << tempSubDirUrl << tempSubDirFiles;
+ // Setting a file as the folder shouldn't work, and the dialog shouldn't change its folder.
+ QTest::addRow("sub-dir/sub-file1.txt") << tempSubFile1Url << currentDirUrl << currentDirFiles;
+}
+
+void tst_QQuickFileDialogImpl::bindCurrentFolder()
+{
+ QFETCH(QUrl, initialFolder);
+ QFETCH(QUrl, expectedFolder);
+ QFETCH(QStringList, expectedVisibleFiles);
+
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", initialFolder }});
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), expectedFolder);
+
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QString failureMessage;
+ // Even waiting for ListView polish and that the FolderListModel's status is ready aren't enough
+ // on Windows, apparently, as sometimes there just aren't any delegates by the time we do the check.
+ // So, we use QTRY_VERIFY2 each time we call this function just to be safe.
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
+
+ // Check that the breadcrumb bar is correct by constructing the expected files from the expectedFolder.
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY2(verifyBreadcrumbDelegates(breadcrumbBar, expectedFolder, failureMessage), qPrintable(failureMessage));
+}
+
+void tst_QQuickFileDialogImpl::changeFolderViaStandardButtons()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Select the delegate by clicking once.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, delegate));
+ COMPARE_URL(delegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
+ QVERIFY(clickButton(delegate));
+ // The selectedFile and currentFolder shouldn't change yet.
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl());
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), {});
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ // The currentFile should, though.
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
+
+ // Click the "Open" button. The dialog should navigate to that directory, but still be open.
+ QVERIFY(dialogHelper.quickDialog->footer());
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open");
+ QVERIFY(openButton);
+ QVERIFY(clickButton(openButton));
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl());
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ dialogHelper.dialog->close();
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::changeFolderViaDoubleClick()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Select the "sub-dir" delegate by double-clicking.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *subDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
+ QVERIFY(doubleClickButton(subDirDelegate));
+ // The first file in the directory should be selected, which is "sub-sub-dir".
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempSubSubDir.path()) });
+ QQuickFileDialogDelegate *subSubDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subSubDirDelegate));
+ QCOMPARE(subSubDirDelegate->isHighlighted(), true);
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl());
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), {});
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ // Since we only chose a folder, the dialog should still be open.
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ dialogHelper.dialog->close();
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::chooseFolderViaTextEdit()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Get the text edit visible with Ctrl+L.
+ const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY(breadcrumbBar->textField()->isVisible());
+ QCOMPARE(breadcrumbBar->textField()->text(), dialogHelper.dialog->currentFolder().toLocalFile());
+ QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
+
+ // Enter the path to the folder in the text edit.
+ enterText(dialogHelper.window(), tempSubDir.path());
+ QCOMPARE(breadcrumbBar->textField()->text(), tempSubDir.path());
+
+ // Hit enter to accept.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ // The first file in the directory should be selected, which is "sub-sub-dir".
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *subSubDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subSubDirDelegate));
+ QCOMPARE(subSubDirDelegate->isHighlighted(), true);
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl());
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), {});
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ QTRY_VERIFY(dialogHelper.dialog->isVisible());
+
+ dialogHelper.dialog->close();
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::chooseFolderViaEnter()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Fhe first delegate in the view should be selected and have focus.
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *subDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ COMPARE_URL(subDirDelegate->file(), QUrl::fromLocalFile(tempSubDir.path()));
+ QVERIFY(subDirDelegate->hasActiveFocus());
+
+ // Select the delegate by pressing enter.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ // The first file in the new directory should be selected, which is "sub-sub-dir".
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubSubDir.path()));
+ // Since we only chose a folder, the dialog should still be open.
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ dialogHelper.dialog->close();
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::cancelDialogWhileTextEditHasFocus()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Get the text edit visible with Ctrl+L.
+ const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY(breadcrumbBar->textField()->hasActiveFocus());
+
+ // Close it via the cancel button.
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ const QString cancelText = QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::Cancel);
+ auto cancelButton = findDialogButton(dialogButtonBox, cancelText);
+ QVERIFY(cancelButton);
+ QVERIFY(clickButton(cancelButton));
+
+ // Open it again. The text field should not be visible, but the breadcrumb bar itself should be.
+ dialogHelper.dialog->open();
+ QVERIFY(dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(dialogHelper.quickDialog->isOpened());
+ QVERIFY(breadcrumbBar->isVisible());
+ // The ListView that contains the breadcrumb delegates should be visible.
+ QVERIFY(breadcrumbBar->contentItem()->isVisible());
+ QVERIFY(!breadcrumbBar->textField()->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::goUp()
+{
+ // Open the dialog. Start off in "sub-dir".
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDir.path()) }});
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Go up a directory via the button next to the breadcrumb bar.
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ auto barListView = qobject_cast<QQuickListView*>(breadcrumbBar->contentItem());
+ QVERIFY(barListView);
+ if (QQuickTest::qIsPolishScheduled(barListView))
+ QVERIFY(QQuickTest::qWaitForItemPolished(barListView));
+ QVERIFY(clickButton(breadcrumbBar->upButton()));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ // The previous directory that we were in should now be selected (matches e.g. Windows and Ubuntu).
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempSubDir.path()) });
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *subDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ QCOMPARE(subDirDelegate->isHighlighted(), true);
+
+ // Go up a directory via the keyboard shortcut next to the breadcrumb bar.
+ const auto goUpKeySequence = QKeySequence(Qt::ALT | Qt::Key_Up);
+ QTest::keySequence(dialogHelper.window(), goUpKeySequence);
+ QDir tempParentDir(tempDir.path());
+ QVERIFY(tempParentDir.cdUp());
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempParentDir.path()));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempDir.path()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempDir.path()) });
+}
+
+void tst_QQuickFileDialogImpl::goUpWhileTextEditHasFocus()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubDir.path()) }});
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Get the text edit visible with Ctrl+L.
+ const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY(breadcrumbBar->textField()->hasActiveFocus());
+
+ // Go up a directory via the button next to the breadcrumb bar.
+ auto barListView = qobject_cast<QQuickListView*>(breadcrumbBar->contentItem());
+ QVERIFY(barListView);
+ if (QQuickTest::qIsPolishScheduled(barListView))
+ QVERIFY(QQuickTest::qWaitForItemPolished(barListView));
+ QVERIFY(clickButton(breadcrumbBar->upButton()));
+ // The path should have changed to the parent directory.
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ // The text edit should be hidden when it loses focus.
+ QVERIFY(!breadcrumbBar->textField()->hasActiveFocus());
+ QVERIFY(!breadcrumbBar->textField()->isVisible());
+ // The focus should be given to the first delegate.
+ QVERIFY(dialogHelper.window()->activeFocusItem());
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *firstDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, firstDelegate));
+ QCOMPARE(dialogHelper.window()->activeFocusItem(), firstDelegate);
+}
+
+void tst_QQuickFileDialogImpl::goIntoLargeFolder()
+{
+ // Create a throwaway directory with a lot of folders within it...
+ QTemporaryDir anotherTempDir;
+ QVERIFY(anotherTempDir.isValid());
+ for (int i = 0; i < 30; ++i) {
+ QDir newDir(anotherTempDir.path());
+ QVERIFY(newDir.exists());
+ // Pad with zeroes so that the directories are ordered as we expect.
+ QVERIFY(newDir.mkdir(QString::fromLatin1("dir%1").arg(i, 2, 10, QLatin1Char('0'))));
+ }
+
+ // ... and within one of those folders, more folders.
+ QDir dir20(anotherTempDir.path() + "/dir20");
+ QVERIFY(dir20.exists());
+ for (int i = 0; i < 30; ++i) {
+ QDir newDir(dir20.path());
+ QVERIFY(newDir.exists());
+ QVERIFY(newDir.mkdir(QString::fromLatin1("subdir%1").arg(i, 2, 10, QLatin1Char('0'))));
+ }
+
+ // Open the dialog. Start off in the throwaway directory.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", QUrl::fromLocalFile(anotherTempDir.path()) }});
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // If the screen is so tall that the contentItem is not vertically larger than the view,
+ // then the test makes no sense.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ if (QQuickTest::qIsPolishScheduled(fileDialogListView))
+ QVERIFY(QQuickTest::qWaitForItemPolished(fileDialogListView));
+ // Just to be safe, make sure it's at least twice as big.
+ if (fileDialogListView->contentItem()->height() < fileDialogListView->height() * 2) {
+ QSKIP(qPrintable(QString::fromLatin1("Expected height of fileDialogListView's contentItem (%1)" \
+ " to be at least twice as big as the ListView's height (%2)")
+ .arg(fileDialogListView->contentItem()->height()).arg(fileDialogListView->height())));
+ }
+
+ // Scroll down to dir20. The view should be somewhere past the middle.
+ QVERIFY(QMetaObject::invokeMethod(fileDialogListView, "positionViewAtIndex", Qt::DirectConnection,
+ Q_ARG(int, 20), Q_ARG(int, QQuickItemView::PositionMode::Center)));
+ QVERIFY(fileDialogListView->contentY() > 0);
+
+ // Go into it. The view should start at the top of the directory, not at the same contentY
+ // that it had in the previous directory.
+ QQuickFileDialogDelegate *dir20Delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 20, dir20Delegate));
+ COMPARE_URL(dir20Delegate->file(), QUrl::fromLocalFile(dir20.path()));
+ QVERIFY(doubleClickButton(dir20Delegate));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(dir20.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(dir20.path()));
+ QCOMPARE(fileDialogListView->contentY(), 0);
+}
+
+void tst_QQuickFileDialogImpl::keyAndShortcutHandling()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Get the text edit visible with Ctrl+L.
+ const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY(breadcrumbBar->textField()->isVisible());
+ QCOMPARE(breadcrumbBar->textField()->text(), dialogHelper.dialog->currentFolder().toLocalFile());
+ QCOMPARE(breadcrumbBar->textField()->selectedText(), breadcrumbBar->textField()->text());
+
+ // Ctrl+L shouldn't hide it.
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ QVERIFY(breadcrumbBar->textField()->isVisible());
+
+ // Cancel it with the escape key.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
+ QVERIFY(!breadcrumbBar->textField()->isVisible());
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ // Make it visible.
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ QVERIFY(breadcrumbBar->textField()->isVisible());
+
+ // Cancel it with the escape key again.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
+ QVERIFY(!breadcrumbBar->textField()->isVisible());
+ QVERIFY(dialogHelper.dialog->isVisible());
+
+ // Pressing escape now should close the dialog.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+}
+
+void tst_QQuickFileDialogImpl::bindNameFilters()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindTxtHtmlNameFilters.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Only "sub-dir", "text1.txt" and "text2.txt" should be visible, since *.txt is the first filter.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+}
+
+void tst_QQuickFileDialogImpl::changeNameFilters()
+{
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+
+ // Open the dialog and check that selectedNameFilter is correct.
+ // By default, QFileDialogOptions::defaultNameFilterString() is used.
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->name(), "All Files");
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->name(), "All Files");
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->index(), 0);
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->index(), 0);
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->extensions(), { "*" });
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->extensions(), { "*" });
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->globs(), { "*" });
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->globs(), { "*" });
+
+ // Close the dialog.
+ dialogHelper.dialog->close();
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+
+ // Set .txt and .html filters.
+ QSignalSpy nameFiltersChangedSpy(dialogHelper.dialog, SIGNAL(nameFiltersChanged()));
+ QVERIFY(nameFiltersChangedSpy.isValid());
+ const QStringList nameFilters = { "Text files (*.txt)", "HTML files (*.html)" };
+ dialogHelper.dialog->setNameFilters(nameFilters);
+ QCOMPARE(dialogHelper.dialog->nameFilters(), nameFilters);
+ QCOMPARE(nameFiltersChangedSpy.count(), 1);
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->name(), "Text files");
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->index(), 0);
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->extensions(), { "txt" });
+ QCOMPARE(dialogHelper.dialog->selectedNameFilter()->globs(), { "*.txt" });
+
+ // Re-open the dialog.
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ // QQuickFileDialogImpl's values only get set before opening.
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->name(), "Text files");
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->index(), 0);
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->extensions(), { "txt" });
+ QCOMPARE(dialogHelper.quickDialog->selectedNameFilter()->globs(), { "*.txt" });
+
+ // Only "sub-dir", "text1.txt" and "text2.txt" should be visible, since *.txt is the first filter.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+
+ // Open the ComboBox's popup.
+ const QQuickComboBox *comboBox = dialogHelper.quickDialog->findChild<QQuickComboBox*>();
+ QVERIFY(comboBox);
+ const QPoint comboBoxCenterPos = comboBox->mapToScene({ comboBox->width() / 2, comboBox->height() / 2 }).toPoint();
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, comboBoxCenterPos);
+ QTRY_VERIFY(comboBox->popup()->isOpened());
+
+ // Select the .html delegate and close the combobox popup. The only visible entry should be the sub-dir.
+ QQuickListView *comboBoxPopupListView = qobject_cast<QQuickListView*>(comboBox->popup()->contentItem());
+ QVERIFY(comboBoxPopupListView);
+ {
+ QQuickAbstractButton *htmlDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(comboBoxPopupListView, 1, htmlDelegate));
+ QVERIFY(clickButton(htmlDelegate));
+ }
+ QTRY_VERIFY(!comboBox->popup()->isVisible());
+ // Use QTRY_VERIFY2 here to fix a failure on QEMU armv7 (QT_QPA_PLATFORM=offscreen).
+ // Not sure why it's necessary.
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, { tempSubDir.path() }, failureMessage), qPrintable(failureMessage));
+
+ // Open the popup again.
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, comboBoxCenterPos);
+ QTRY_VERIFY(comboBox->popup()->isOpened());
+
+ // Select .txt and close the combobox popup. The original entries should be visible.
+ {
+ QQuickAbstractButton *txtDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(comboBoxPopupListView, 0, txtDelegate));
+ QCOMPARE(txtDelegate->text(), nameFilters.at(0));
+ QVERIFY(clickButton(txtDelegate));
+ }
+ QTRY_VERIFY(!comboBox->popup()->isVisible());
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+}
+
+void tst_QQuickFileDialogImpl::changeNameFiltersAfterChangingFolder()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindAllTxtHtmlNameFilters.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Go into the "sub-dir" folder.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QString failureMessage;
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView,
+ { tempSubDir.path(), tempFile1->fileName(), tempFile2->fileName() }, failureMessage), qPrintable(failureMessage));
+ QQuickFileDialogDelegate *subDirDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, subDirDelegate));
+ QVERIFY(doubleClickButton(subDirDelegate));
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(tempSubDir.path()));
+
+ // Open the ComboBox's popup.
+ const QQuickComboBox *comboBox = dialogHelper.quickDialog->findChild<QQuickComboBox*>();
+ QVERIFY(comboBox);
+ const QPoint comboBoxCenterPos = comboBox->mapToScene({ comboBox->width() / 2, comboBox->height() / 2 }).toPoint();
+ QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, comboBoxCenterPos);
+ QTRY_VERIFY(comboBox->popup()->isOpened());
+
+ // Select the .html delegate, close the combobox popup, and ensure that the change had an effect.
+ QQuickListView *comboBoxPopupListView = qobject_cast<QQuickListView*>(comboBox->popup()->contentItem());
+ QVERIFY(comboBoxPopupListView);
+ {
+ QQuickAbstractButton *htmlDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(comboBoxPopupListView, 2, htmlDelegate));
+ QVERIFY(clickButton(htmlDelegate));
+ }
+ QTRY_VERIFY(!comboBox->popup()->isVisible());
+ // There are no HTML files in "sub-dir", so there should only be the one "sub-sub-dir" delegate.
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, { tempSubSubDir.path() }, failureMessage), qPrintable(failureMessage));
+}
+
+void tst_QQuickFileDialogImpl::tabFocusNavigation()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindTxtHtmlNameFilters.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ QList<QQuickItem*> expectedFocusItems;
+
+ // The initial focus should be on the first delegate.
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *firstDelegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, firstDelegate));
+ expectedFocusItems << firstDelegate;
+
+ // Tab should move to the name filters combobox.
+ QQuickComboBox *comboBox = dialogHelper.quickDialog->findChild<QQuickComboBox*>();
+ QVERIFY(comboBox);
+ expectedFocusItems << comboBox;
+
+ // Next, the left-most dialog button.
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ QCOMPARE(dialogButtonBox->count(), 2);
+ auto leftMostButton = qobject_cast<QQuickAbstractButton*>(dialogButtonBox->itemAt(0));
+ QVERIFY(leftMostButton);
+ expectedFocusItems << leftMostButton;
+
+ // Then, the right-most dialog button.
+ auto rightMostButton = qobject_cast<QQuickAbstractButton*>(dialogButtonBox->itemAt(1));
+ QVERIFY(rightMostButton);
+ expectedFocusItems << rightMostButton;
+
+ // Then, the up button.
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ expectedFocusItems << breadcrumbBar->upButton();
+
+ // Finally, add each bread crumb delegate.
+ for (int i = 0; i < fileDialogListView->count(); ++i) {
+ QQuickFileDialogDelegate *delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, i, delegate));
+ expectedFocusItems << delegate;
+ }
+
+ // Tab through each item, checking the focus after each.
+ for (auto expectedFocusItem : qAsConst(expectedFocusItems)) {
+ // Check the focus item first so that we account for the first item.
+ // Print detailed failure message as workaround for QTBUG-92102.
+ QVERIFY2(dialogHelper.window()->activeFocusItem() == expectedFocusItem, qPrintable(QString::fromLatin1(
+ "\n Actual: %1\n Expected: %2").arg(QDebug::toString(dialogHelper.window()->activeFocusItem()))
+ .arg(QDebug::toString(expectedFocusItem))));
+
+ if (expectedFocusItem != expectedFocusItems.last())
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Tab);
+ }
+
+ // Ensure the order is reversed when shift-tabbing.
+ std::reverse(expectedFocusItems.begin(), expectedFocusItems.end());
+ // We know the first (last) item has focus already, so skip it.
+ expectedFocusItems.removeFirst();
+ for (auto expectedFocusItem : qAsConst(expectedFocusItems)) {
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Tab, Qt::ShiftModifier);
+
+ QCOMPARE(dialogHelper.window()->activeFocusItem(), expectedFocusItem);
+ }
+}
+
+void tst_QQuickFileDialogImpl::acceptRejectLabel()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "acceptRejectLabel.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Check that the accept and reject buttons' labels have changed.
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ QVERIFY(findDialogButton(dialogButtonBox, "AcceptTest"));
+ QVERIFY(findDialogButton(dialogButtonBox, "RejectTest"));
+
+ // Close the dialog.
+ dialogHelper.dialog->close();
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+
+ // Reset back to the default text.
+ dialogHelper.dialog->resetAcceptLabel();
+ dialogHelper.dialog->resetRejectLabel();
+
+ // Re-open the dialog.
+ dialogHelper.dialog->open();
+ QVERIFY(dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(dialogHelper.quickDialog->isOpened());
+
+ // Check that the defaults are back.
+ const QString openText = QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::Open);
+ QVERIFY2(findDialogButton(dialogButtonBox, openText), qPrintable(QString::fromLatin1(
+ "Failed to find dialog button with text \"%1\"").arg(openText)));
+ const QString cancelText = QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::Cancel);
+ QVERIFY2(findDialogButton(dialogButtonBox, cancelText), qPrintable(QString::fromLatin1(
+ "Failed to find dialog button with text \"%1\"").arg(cancelText)));
+}
+
+void tst_QQuickFileDialogImpl::bindTitle()
+{
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindTitle.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Open the dialog and check that the correct title is displayed.
+ QQuickFileDialog *dialog = dialogHelper.window()->property("dialog").value<QQuickFileDialog*>();
+ QVERIFY(dialog);
+ const QString expectedTitle = QLatin1String("Test Title");
+ QCOMPARE(dialogHelper.dialog->title(), expectedTitle);
+ QCOMPARE(dialogHelper.quickDialog->title(), expectedTitle);
+ auto header = dialogHelper.quickDialog->header();
+ QVERIFY(header);
+ auto dialogTitleBarLabel = dialogHelper.quickDialog->header()->findChild<QQuickLabel*>("dialogTitleBarLabel");
+ QVERIFY(dialogTitleBarLabel);
+ QCOMPARE(dialogTitleBarLabel->text(), expectedTitle);
+}
+
+void tst_QQuickFileDialogImpl::itemsDisabledWhenNecessary()
+{
+ QTemporaryDir anotherTempDir;
+ QVERIFY(anotherTempDir.isValid());
+ QDir subDir(anotherTempDir.path());
+ QVERIFY(subDir.mkdir("emptyDir"));
+ QVERIFY(subDir.cd("emptyDir"));
+
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", QUrl::fromLocalFile(subDir.path()) }});
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(subDir.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(subDir.path()));
+
+ // We opened it in a folder that has no files, so the Open button should be disabled.
+ QVERIFY(dialogHelper.quickDialog->footer());
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open");
+ QVERIFY(openButton);
+ QCOMPARE(openButton->isEnabled(), false);
+
+ // Now go up. The Open button should now be enabled.
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY(clickButton(breadcrumbBar->upButton()));
+ QCOMPARE(openButton->isEnabled(), true);
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(anotherTempDir.path()));
+ COMPARE_URL(dialogHelper.quickDialog->currentFolder(), QUrl::fromLocalFile(anotherTempDir.path()));
+
+ // Get the text edit visible with Ctrl+L. The Open button should now be disabled.
+ const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ QVERIFY(breadcrumbBar->textField()->isVisible());
+ QCOMPARE(openButton->isEnabled(), false);
+
+ // Hide it with the escape key. The Open button should now be enabled.
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Escape);
+ QVERIFY(!breadcrumbBar->textField()->isVisible());
+ QCOMPARE(openButton->isEnabled(), true);
+}
+
+void tst_QQuickFileDialogImpl::fileMode_data()
+{
+ QTest::addColumn<QQuickFileDialog::FileMode>("fileMode");
+
+ QTest::newRow("OpenFile") << QQuickFileDialog::OpenFile;
+ QTest::newRow("OpenFiles") << QQuickFileDialog::OpenFiles;
+ QTest::newRow("SaveFile") << QQuickFileDialog::SaveFile;
+}
+
+void tst_QQuickFileDialogImpl::fileMode()
+{
+ QFETCH(QQuickFileDialog::FileMode, fileMode);
+
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ dialogHelper.dialog->setFileMode(fileMode);
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ // Select the first file (not a directory).
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QQuickFileDialogDelegate *tempFile1Delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 1, tempFile1Delegate));
+ COMPARE_URL(tempFile1Delegate->file(), QUrl::fromLocalFile(tempFile1->fileName()));
+ QVERIFY(clickButton(tempFile1Delegate));
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile1->fileName()) });
+
+ // All modes should support opening an existing file, so the Open button should be enabled.
+ QVERIFY(dialogHelper.quickDialog->footer());
+ auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>();
+ QVERIFY(dialogButtonBox);
+ QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open");
+ QVERIFY(openButton);
+ QCOMPARE(openButton->isEnabled(), true);
+
+ // Only the OpenFiles mode should allow multiple files to be selected, however.
+ QQuickFileDialogDelegate *tempFile2Delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 2, tempFile2Delegate));
+ COMPARE_URL(tempFile2Delegate->file(), QUrl::fromLocalFile(tempFile2->fileName()));
+ QTest::keyPress(dialogHelper.window(), Qt::Key_Shift);
+ QVERIFY(clickButton(tempFile2Delegate));
+ QTest::keyRelease(dialogHelper.window(), Qt::Key_Shift);
+ if (fileMode == QQuickFileDialog::OpenFiles) {
+ // currentFile() always points to the first file in the list of selected files.
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+ const QList<QUrl> expectedSelectedFiles = {
+ QUrl::fromLocalFile(tempFile1->fileName()), QUrl::fromLocalFile(tempFile2->fileName()) };
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), expectedSelectedFiles);
+ } else {
+ // OpenFile and SaveFile dialogs should have tempFile2 selected since it was clicked,
+ // but the shift should do nothing, so tempFile1 should no longer be selected.
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ }
+
+ // Get the text edit visible with Ctrl+L.
+ const auto editPathKeySequence = QKeySequence(Qt::CTRL | Qt::Key_L);
+ QTest::keySequence(dialogHelper.window(), editPathKeySequence);
+ auto breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar*>();
+ QVERIFY(breadcrumbBar);
+ QVERIFY(breadcrumbBar->textField()->isVisible());
+
+ // Typing in the name of an non-existent file should only work for SaveFile.
+ const QString nonExistentFilePath = "/foo/bar.txt";
+ enterText(dialogHelper.window(), nonExistentFilePath);
+ QCOMPARE(breadcrumbBar->textField()->text(), nonExistentFilePath);
+ QTest::keyClick(dialogHelper.window(), Qt::Key_Return);
+ if (fileMode == QQuickFileDialog::SaveFile) {
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), QUrl::fromLocalFile(nonExistentFilePath));
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { QUrl::fromLocalFile(nonExistentFilePath) });
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ } else {
+ // For OpenFile(s), we do what Qt Quick Dialogs 1.x did, and restore the previous (valid) dir path.
+ // The currentFile(s) should remain unchanged too.
+ QVERIFY(dialogHelper.dialog->isVisible());
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempDir.path()));
+ QCOMPARE(breadcrumbBar->textField()->text(), tempDir.path());
+
+ // Should be unchanged from the last time.
+ if (fileMode == QQuickFileDialog::OpenFiles) {
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName()));
+ const QList<QUrl> expectedSelectedFiles = {
+ QUrl::fromLocalFile(tempFile1->fileName()), QUrl::fromLocalFile(tempFile2->fileName()) };
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), expectedSelectedFiles);
+ } else { // OpenFile
+ COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile2->fileName()));
+ COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile2->fileName()) });
+ }
+ }
+}
+
+void tst_QQuickFileDialogImpl::defaultSuffix_data()
+{
+ QTest::addColumn<QString>("defaultSuffix");
+
+ QTest::newRow("txt") << "txt";
+ QTest::newRow(".txt") << ".txt";
+}
+
+void tst_QQuickFileDialogImpl::defaultSuffix()
+{
+ QFETCH(QString, defaultSuffix);
+
+ // Simplify the test by using a directory with no files, and add a single file there.
+ QFile tempFile1(tempSubSubDir.path() + "/file1");
+ QVERIFY(tempFile1.open(QIODevice::ReadWrite));
+
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "bindCurrentFolder.qml", {},
+ {{ "initialFolder", QUrl::fromLocalFile(tempSubSubDir.path()) }});
+ dialogHelper.dialog->setDefaultSuffix(defaultSuffix);
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+ COMPARE_URL(dialogHelper.dialog->currentFolder(), QUrl::fromLocalFile(tempSubSubDir.path()));
+
+ // There should be one extension-less file: "file1".
+ auto fileDialogListView = dialogHelper.quickDialog->findChild<QQuickListView*>("fileDialogListView");
+ QVERIFY(fileDialogListView);
+ QString failureMessage;
+ const QStringList expectedVisibleFiles = { tempFile1.fileName() };
+ QTRY_VERIFY2(verifyFileDialogDelegates(fileDialogListView, expectedVisibleFiles, failureMessage), qPrintable(failureMessage));
+
+ // Choose the delegate. The suffix should be added to the delegates.
+ QQuickFileDialogDelegate *file1Delegate = nullptr;
+ QTRY_VERIFY(findViewDelegateItem(fileDialogListView, 0, file1Delegate));
+ COMPARE_URL(file1Delegate->file(), QUrl::fromLocalFile(tempFile1.fileName()));
+ QVERIFY(doubleClickButton(file1Delegate));
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ const QUrl fileUrlWithSuffix = QUrl::fromLocalFile(tempFile1.fileName() + ".txt");
+ COMPARE_URL(dialogHelper.dialog->selectedFile(), fileUrlWithSuffix);
+ COMPARE_URLS(dialogHelper.dialog->selectedFiles(), { fileUrlWithSuffix });
+}
+
+void tst_QQuickFileDialogImpl::done_data()
+{
+ QTest::addColumn<QQuickFileDialog::StandardCode>("result");
+
+ QTest::newRow("Accepted") << QQuickFileDialog::Accepted;
+ QTest::newRow("Rejected") << QQuickFileDialog::Rejected;
+}
+
+void tst_QQuickFileDialogImpl::done()
+{
+ QFETCH(QQuickFileDialog::StandardCode, result);
+
+ // Open the dialog.
+ DialogTestHelper<QQuickFileDialog, QQuickFileDialogImpl> dialogHelper(this, "fileDialog.qml");
+ QVERIFY2(dialogHelper.isWindowInitialized(), dialogHelper.failureMessage());
+ QVERIFY(dialogHelper.waitForWindowActive());
+ QVERIFY(dialogHelper.openDialog());
+ QTRY_VERIFY(dialogHelper.isQuickDialogOpen());
+
+ switch (result) {
+ case QQuickFileDialog::Accepted:
+ QVERIFY(QMetaObject::invokeMethod(dialogHelper.window(), "doneAccepted"));
+ break;
+ case QQuickFileDialog::Rejected:
+ QVERIFY(QMetaObject::invokeMethod(dialogHelper.window(), "doneRejected"));
+ break;
+ }
+
+ QVERIFY(!dialogHelper.dialog->isVisible());
+ QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
+ QCOMPARE(dialogHelper.dialog->result(), result);
+}
+
+int main(int argc, char *argv[])
+{
+ // We need to set this attribute, and this (defining main() ourselves and
+ // calling QTEST_MAIN_IMPL) seems to be the nicest way to do it without
+ // duplicating too much code.
+ // We also don't want to run this for every style, as each one will have
+ // different ways of implementing the dialogs.
+ QCoreApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
+ // For now we only test one style.
+ QQuickStyle::setStyle("Basic");
+ QTEST_MAIN_IMPL(tst_QQuickFileDialogImpl)
+}
+
+#include "tst_qquickfiledialogimpl.moc"
diff --git a/tests/auto/shared/dialogtestutil.h b/tests/auto/shared/dialogtestutil.h
new file mode 100644
index 00000000..c10c4001
--- /dev/null
+++ b/tests/auto/shared/dialogtestutil.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKDIALOGTESTUTIL_H
+#define QQUICKDIALOGTESTUTIL_H
+
+#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
+
+#include "util.h"
+#include "visualtestutil.h"
+
+// We need these for Windows, because FolderListModel returns a lowercase drive letter; e.g.:
+// "file:///c:/blah.txt", whereas other API returns "file:///C:/blah.txt".
+#define COMPARE_URL(url1, url2) \
+ QCOMPARE(QFileInfo(url1.toLocalFile()).absoluteFilePath(), QFileInfo(url2.toLocalFile()).absoluteFilePath());
+
+// Store a copy of the arguments in case { ... } list initializer syntax is used as an argument,
+// which could result in two different lists being created and passed to std::transform()
+// (and would also require it to be enclosed in parentheses everywhere it's used).
+#define COMPARE_URLS(actualUrls, expectedUrls) \
+{ \
+ const QList<QUrl> actualUrlsCopy = actualUrls; \
+ QList<QString> actualPaths; \
+ std::transform(actualUrlsCopy.begin(), actualUrlsCopy.end(), std::back_insert_iterator(actualPaths), \
+ [](const QUrl &url) { return QFileInfo(url.toLocalFile()).absoluteFilePath(); }); \
+ const QList<QUrl> expectedUrlsCopy = expectedUrls; \
+ QList<QString> expectedPaths; \
+ std::transform(expectedUrlsCopy.begin(), expectedUrlsCopy.end(), std::back_insert_iterator(expectedPaths), \
+ [](const QUrl &url) { return QFileInfo(url.toLocalFile()).absoluteFilePath(); }); \
+ QCOMPARE(actualPaths, expectedPaths); \
+}
+
+namespace QQuickDialogTestUtil
+{
+
+// Saves duplicating a bunch of code in every test.
+template<typename DialogType, typename QuickDialogType>
+class DialogTestHelper
+{
+public:
+ DialogTestHelper(QQmlDataTest *testCase, const QString &testFilePath,
+ const QStringList &qmlImportPaths = {}, const QVariantMap &initialProperties = {}) :
+ appHelper(testCase, testFilePath, qmlImportPaths, initialProperties)
+ {
+ if (!appHelper.ready)
+ return;
+
+ dialog = appHelper.window->property("dialog").value<DialogType*>();
+ if (!dialog) {
+ appHelper.errorMessage = "\"dialog\" property is not valid";
+ return;
+ }
+
+ appHelper.window->show();
+ appHelper.window->requestActivate();
+ }
+
+ Q_REQUIRED_RESULT bool isWindowInitialized() const
+ {
+ return appHelper.ready;
+ }
+
+ Q_REQUIRED_RESULT bool waitForWindowActive()
+ {
+ return QTest::qWaitForWindowActive(appHelper.window);
+ }
+
+ bool openDialog()
+ {
+ dialog->open();
+ if (!dialog->isVisible()) {
+ appHelper.errorMessage = "Dialog is not visible";
+ return false;
+ }
+
+ // We might want to call this function more than once,
+ // and we only need to get these members the first time.
+ if (!quickDialog) {
+ quickDialog = appHelper.window->findChild<QuickDialogType*>();
+ if (!quickDialog) {
+ appHelper.errorMessage = "Can't find Qt Quick-based dialog";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool isQuickDialogOpen() const
+ {
+ return quickDialog->isOpened();
+ }
+
+ QQuickWindow *window() const
+ {
+ return appHelper.window;
+ }
+
+ const char *failureMessage() const
+ {
+ return appHelper.errorMessage.constData();
+ }
+
+ QQuickVisualTestUtil::QQuickApplicationHelper appHelper;
+ DialogType *dialog = nullptr;
+ QuickDialogType *quickDialog = nullptr;
+};
+
+}
+
+#endif // QQUICKDIALOGTESTUTIL_H
diff --git a/tests/auto/shared/visualtestutil.h b/tests/auto/shared/visualtestutil.h
index 215a487b..76c44821 100644
--- a/tests/auto/shared/visualtestutil.h
+++ b/tests/auto/shared/visualtestutil.h
@@ -127,6 +127,20 @@ namespace QQuickVisualTestUtil
QQuickItem* findViewDelegateItem(QQuickItemView *itemView, int index,
FindViewDelegateItemFlags flags = FindViewDelegateItemFlag::PositionViewAtIndex);
+ /*!
+ \internal
+
+ Same as above except allows use in QTRY_* functions without having to call it again
+ afterwards to assign the delegate.
+ */
+ template<typename T>
+ bool findViewDelegateItem(QQuickItemView *itemView, int index, T &delegateItem,
+ FindViewDelegateItemFlags flags = FindViewDelegateItemFlag::PositionViewAtIndex)
+ {
+ delegateItem = qobject_cast<T>(findViewDelegateItem(itemView, index, flags));
+ return delegateItem != nullptr;
+ }
+
class QQuickApplicationHelper
{
public:
diff --git a/tests/manual/dialogs/CMakeLists.txt b/tests/manual/dialogs/CMakeLists.txt
new file mode 100644
index 00000000..d99d64b9
--- /dev/null
+++ b/tests/manual/dialogs/CMakeLists.txt
@@ -0,0 +1,31 @@
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(dialogs LANGUAGES C CXX ASM)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_manual_test(dialogs
+ GUI
+ SOURCES
+ dialogs.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::QuickControls2
+)
+
+# Resources:
+set(qmake_immediate_resource_files
+ "dialogs.qml"
+ "FileDialogPage.qml"
+ "StringListView.qml"
+ "qmldir"
+ "Theme.qml"
+)
+
+qt_internal_add_resource(dialogs "qmake_immediate"
+ PREFIX
+ "/"
+ FILES
+ ${qmake_immediate_resource_files}
+)
diff --git a/tests/manual/dialogs/FileDialogPage.qml b/tests/manual/dialogs/FileDialogPage.qml
new file mode 100644
index 00000000..9faad47e
--- /dev/null
+++ b/tests/manual/dialogs/FileDialogPage.qml
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+import QtQuick.Layouts
+
+import "."
+
+ColumnLayout {
+ property alias dialog: fileDialog
+
+ // Put it all in another ColumnLayout so we can easily add margins.
+ ColumnLayout {
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.topMargin: 12
+ Layout.bottomMargin: 12
+
+ GroupBox {
+ title: qsTr("Dialog properties")
+
+ Layout.fillWidth: true
+
+ GridLayout {
+ columns: 2
+ anchors.fill: parent
+
+ Label {
+ text: qsTr("modality")
+
+ Layout.alignment: Qt.AlignTop
+ Layout.minimumWidth: ApplicationWindow.window.width * 0.2
+ Layout.maximumWidth: ApplicationWindow.window.width * 0.2
+ }
+ ButtonGroup {
+ id: modalityButtonGroup
+ buttons: modalityColumnLayout.children
+ }
+ ColumnLayout {
+ id: modalityColumnLayout
+
+ RadioButton {
+ text: qsTr("Qt.NonModal")
+
+ readonly property int modality: Qt.NonModal
+ }
+ RadioButton {
+ text: qsTr("Qt.WindowModal")
+ checked: true
+
+ readonly property int modality: Qt.WindowModal
+ }
+ RadioButton {
+ text: qsTr("Qt.ApplicationModal")
+
+ readonly property int modality: Qt.ApplicationModal
+ }
+ }
+
+ Label {
+ text: qsTr("result")
+ }
+ TextField {
+ id: resultTextField
+ text: fileDialog.result === 1 ? qsTr("Accepted") : qsTr("Rejected")
+ readOnly: true
+ enabled: false
+ }
+
+ Label {
+ text: qsTr("title")
+ }
+ TextField {
+ id: titleTextField
+ text: qsTr("Choose a file")
+ }
+ }
+ }
+
+ GroupBox {
+ title: qsTr("FileDialog properties")
+
+ Layout.fillWidth: true
+
+ GridLayout {
+ columns: 2
+ anchors.fill: parent
+
+ Label {
+ text: qsTr("acceptLabel")
+
+ Layout.minimumWidth: ApplicationWindow.window.width * 0.2
+ Layout.maximumWidth: ApplicationWindow.window.width * 0.2
+ }
+ TextField {
+ id: acceptLabelTextField
+ text: qsTr("OK")
+ }
+
+ Label {
+ text: qsTr("currentFile")
+ }
+ TextField {
+ id: currentFileTextField
+ text: fileDialog.currentFile
+ readOnly: true
+ selectByMouse: true
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("currentFolder")
+ }
+ TextField {
+ id: currentFolderTextField
+ text: fileDialog.currentFolder
+ readOnly: true
+ selectByMouse: true
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("currentFiles")
+
+ Layout.alignment: Qt.AlignTop
+ }
+ StringListView {
+ id: currentFilesListView
+ // QTBUG-72906
+ model: [].concat(fileDialog.currentFiles)
+ }
+
+ Label {
+ text: qsTr("fileOptions")
+
+ Layout.alignment: Qt.AlignTop
+ }
+ ColumnLayout {
+ id: fileOptionsColumnLayout
+
+ CheckBox {
+ id: dontResolveSymlinksCheckBox
+ text: qsTr("DontResolveSymlinks")
+
+ readonly property int fileOption: checked ? FileDialog.DontResolveSymlinks : 0
+ }
+ CheckBox {
+ id: dontConfirmOverwriteCheckBox
+ text: qsTr("DontConfirmOverwrite")
+
+ readonly property int fileOption: checked ? FileDialog.DontConfirmOverwrite : 0
+ }
+ CheckBox {
+ id: readOnlyCheckBox
+ text: qsTr("ReadOnly")
+
+ readonly property int fileOption: checked ? FileDialog.ReadOnly : 0
+ }
+ CheckBox {
+ id: hideNameFilterDetailsCheckBox
+ text: qsTr("HideNameFilterDetails")
+
+ readonly property int fileOption: checked ? FileDialog.HideNameFilterDetails : 0
+ }
+ }
+
+ Label {
+ text: qsTr("fileMode")
+
+ Layout.alignment: Qt.AlignTop
+ }
+ ButtonGroup {
+ id: fileModeButtonGroup
+ buttons: fileModeColumnLayout.children
+ }
+ ColumnLayout {
+ id: fileModeColumnLayout
+
+ RadioButton {
+ text: qsTr("OpenFile")
+
+ readonly property int fileMode: FileDialog.OpenFile
+ }
+ RadioButton {
+ text: qsTr("OpenFiles")
+ checked: true
+
+ readonly property int fileMode: FileDialog.OpenFiles
+ }
+ RadioButton {
+ text: qsTr("SaveFile")
+
+ readonly property int fileMode: FileDialog.SaveFile
+ }
+ }
+
+ Label {
+ text: qsTr("nameFilters")
+ }
+ TextField {
+ id: nameFiltersTextField
+ text: ["Text files (*.txt)", "HTML files (*.html)"].join(",")
+
+ Layout.fillWidth: true
+
+ ToolTip.text: qsTr("For this example, a comma-separated string")
+ ToolTip.visible: hovered
+ ToolTip.delay: Theme.toolTipDelay
+ }
+
+ Label {
+ text: qsTr("rejectLabel")
+ }
+ TextField {
+ id: rejectLabelTextField
+ text: qsTr("Cancel")
+ }
+
+ Label {
+ text: qsTr("selectedFile")
+ }
+ TextField {
+ id: selectedFileTextField
+ text: fileDialog.selectedFile
+ readOnly: true
+ selectByMouse: true
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("selectedFiles")
+
+ Layout.alignment: Qt.AlignTop
+ }
+ StringListView {
+ id: selectedFilesListView
+ // QTBUG-72906
+ model: [].concat(fileDialog.selectedFiles)
+ }
+
+ Label {
+ text: qsTr("selectedNameFilter.name")
+ }
+ TextField {
+ id: selectedNameFilterNameTextField
+ text: fileDialog.selectedNameFilter.name
+ readOnly: true
+ selectByMouse: true
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("selectedNameFilter.globs")
+
+ Layout.alignment: Qt.AlignTop
+ }
+ StringListView {
+ id: selectedNameFilterGlobsListView
+ // QTBUG-72906
+ model: [].concat(fileDialog.selectedNameFilter.globs)
+ }
+
+ Label {
+ text: qsTr("selectedNameFilter.index")
+ }
+ TextField {
+ id: selectedNameFilterIndexTextField
+ text: fileDialog.selectedNameFilter.index
+ readOnly: true
+ selectByMouse: true
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("selectedNameFilter.extensions")
+
+ Layout.alignment: Qt.AlignTop
+ }
+ StringListView {
+ id: selectedNameFilterExtensionsListView
+ // QTBUG-72906
+ model: [].concat(fileDialog.selectedNameFilter.extensions)
+ }
+ }
+ }
+
+ FileDialog {
+ id: fileDialog
+
+ modality: modalityButtonGroup.checkedButton.modality
+ title: titleTextField.text
+
+ acceptLabel: acceptLabelTextField.text
+ fileMode: fileModeButtonGroup.checkedButton.fileMode
+ options: dontResolveSymlinksCheckBox.fileOption
+ | dontConfirmOverwriteCheckBox.fileOption
+ | readOnlyCheckBox.fileOption
+ | hideNameFilterDetailsCheckBox.fileOption
+ nameFilters: nameFiltersTextField.text.split(",")
+ rejectLabel: rejectLabelTextField.text
+ }
+ }
+}
diff --git a/tests/manual/dialogs/StringListView.qml b/tests/manual/dialogs/StringListView.qml
new file mode 100644
index 00000000..4a229404
--- /dev/null
+++ b/tests/manual/dialogs/StringListView.qml
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import "."
+
+ListView {
+ id: root
+ clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: count > 0 ? 128 : noneLabel.implicitHeight
+
+ ScrollBar.vertical: ScrollBar {}
+
+ delegate: TextField {
+ width: root.width
+ text: modelData
+ readOnly: true
+ selectByMouse: true
+ }
+
+ Label {
+ id: noneLabel
+ text: qsTr("(None)")
+ visible: root.count === 0
+ }
+}
diff --git a/tests/manual/dialogs/Theme.qml b/tests/manual/dialogs/Theme.qml
new file mode 100644
index 00000000..8114f14e
--- /dev/null
+++ b/tests/manual/dialogs/Theme.qml
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+pragma Singleton
+
+import QtQml
+
+QtObject {
+ readonly property int toolTipDelay: 1000
+}
diff --git a/tests/manual/dialogs/dialogs.cpp b/tests/manual/dialogs/dialogs.cpp
new file mode 100644
index 00000000..8bab70e2
--- /dev/null
+++ b/tests/manual/dialogs/dialogs.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qsettings.h>
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtQuickControls2/qquickstyle.h>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("dialogs-manual-test");
+ QCoreApplication::setOrganizationName("QtProject");
+
+ // Use native dialogs by default unless the user asked us not to.
+ QSettings settings;
+ const QVariant useNativeDialogs = settings.value("useNativeDialogs");
+ if (useNativeDialogs.isValid()) {
+ if (!useNativeDialogs.toBool())
+ QCoreApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
+ } else {
+ // Set the default here so that we can use an alias in QML.
+ // Without this it defaults to CheckBox's checked default value, which is false.
+ settings.setValue("useNativeDialogs", true);
+ }
+
+ QQmlApplicationEngine engine;
+ engine.setInitialProperties({{ "style", QQuickStyle::name() }});
+ engine.load(QUrl(QStringLiteral("qrc:/dialogs.qml")));
+
+ return app.exec();
+}
diff --git a/tests/manual/dialogs/dialogs.pro b/tests/manual/dialogs/dialogs.pro
new file mode 100644
index 00000000..abb31492
--- /dev/null
+++ b/tests/manual/dialogs/dialogs.pro
@@ -0,0 +1,11 @@
+TEMPLATE = app
+TARGET = dialogs
+QT += qml quickcontrols2
+
+SOURCES += dialogs.cpp
+RESOURCES += \
+ dialogs.qml \
+ FileDialogPage.qml \
+ StringListView.qml \
+ qmldir \
+ Theme.qml
diff --git a/tests/manual/dialogs/dialogs.qml b/tests/manual/dialogs/dialogs.qml
new file mode 100644
index 00000000..d1f54c08
--- /dev/null
+++ b/tests/manual/dialogs/dialogs.qml
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import Qt.labs.settings
+
+ApplicationWindow {
+ id: window
+ width: 800
+ height: 600
+ title: "dialogs - style: " + style
+ visible: true
+
+ required property string style
+
+ Component.onCompleted: {
+ x = Screen.width / 2 - width / 2
+ y = Screen.height / 2 - height / 2
+ }
+
+ Settings {
+ id: settings
+
+ property alias useNativeDialogs: useNativeDialogsCheckBox.checked
+ }
+
+ Page {
+ anchors.fill: parent
+
+ header: TabBar {
+ id: tabBar
+
+ TabButton {
+ text: qsTr("FileDialog")
+ }
+ TabButton {
+ text: qsTr("Coming Soon...")
+ }
+ }
+
+ ScrollView {
+ id: scrollView
+ anchors.fill: parent
+ clip: true
+
+ StackLayout {
+ id: stackLayout
+ currentIndex: tabBar.currentIndex
+ width: scrollView.width
+
+ FileDialogPage {}
+ }
+ }
+ }
+
+ footer: ToolBar {
+ RowLayout {
+ anchors.fill: parent
+
+ CheckBox {
+ id: useNativeDialogsCheckBox
+ text: qsTr("Use Native Dialogs (requires restart)")
+ checked: settings.useNativeDialogs
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ Button {
+ text: qsTr("Open")
+
+ onClicked: stackLayout.children[stackLayout.currentIndex].dialog.open()
+ }
+ }
+ }
+}
diff --git a/tests/manual/dialogs/qmldir b/tests/manual/dialogs/qmldir
new file mode 100644
index 00000000..4f4a6b7b
--- /dev/null
+++ b/tests/manual/dialogs/qmldir
@@ -0,0 +1,3 @@
+module org.qtproject.examples.dialogs
+
+singleton Theme 1.0 Theme.qml