aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickdialogs
diff options
context:
space:
mode:
Diffstat (limited to 'src/quickdialogs')
-rw-r--r--src/quickdialogs/CMakeLists.txt6
-rw-r--r--src/quickdialogs/quickdialogs/CMakeLists.txt57
-rw-r--r--src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-colordialog-gtk.pngbin0 -> 9786 bytes
-rw-r--r--src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-filedialog-gtk.pngbin0 -> 39560 bytes
-rw-r--r--src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-folderdialog-gtk.pngbin0 -> 40008 bytes
-rw-r--r--src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-fontdialog-gtk.pngbin0 -> 32399 bytes
-rw-r--r--src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-android.pngbin0 -> 3741 bytes
-rw-r--r--src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-informative-android.pngbin0 -> 6737 bytes
-rw-r--r--src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf41
-rw-r--r--src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml34
-rw-r--r--src/quickdialogs/quickdialogs/doc/src/includes/fallback.qdocinc1
-rw-r--r--src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-index.qdoc39
-rw-r--r--src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-qmltypes.qdoc22
-rw-r--r--src/quickdialogs/quickdialogs/qquickabstractdialog.cpp481
-rw-r--r--src/quickdialogs/quickdialogs/qquickabstractdialog_p.h129
-rw-r--r--src/quickdialogs/quickdialogs/qquickcolordialog.cpp152
-rw-r--r--src/quickdialogs/quickdialogs/qquickcolordialog_p.h61
-rw-r--r--src/quickdialogs/quickdialogs/qquickfiledialog.cpp601
-rw-r--r--src/quickdialogs/quickdialogs/qquickfiledialog_p.h132
-rw-r--r--src/quickdialogs/quickdialogs/qquickfolderdialog.cpp264
-rw-r--r--src/quickdialogs/quickdialogs/qquickfolderdialog_p.h80
-rw-r--r--src/quickdialogs/quickdialogs/qquickfontdialog.cpp189
-rw-r--r--src/quickdialogs/quickdialogs/qquickfontdialog_p.h67
-rw-r--r--src/quickdialogs/quickdialogs/qquickmessagedialog.cpp244
-rw-r--r--src/quickdialogs/quickdialogs/qquickmessagedialog_p.h75
-rw-r--r--src/quickdialogs/quickdialogs/qtquickdialogs2foreign.cpp10
-rw-r--r--src/quickdialogs/quickdialogs/qtquickdialogs2foreign_p.h42
-rw-r--r--src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h22
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt215
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/checkers.pngbin0 -> 153 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.pngbin0 -> 119 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.svg136
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@2x.pngbin0 -> 268 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@3x.pngbin0 -> 360 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@4x.pngbin0 -> 422 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.pngbin0 -> 135 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.svg134
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@2x.pngbin0 -> 160 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@3x.pngbin0 -> 179 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@4x.pngbin0 -> 196 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.pngbin0 -> 437 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.svg61
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@2x.pngbin0 -> 655 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@3x.pngbin0 -> 825 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@4x.pngbin0 -> 955 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.pngbin0 -> 238 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.svg117
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@2x.pngbin0 -> 396 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@3x.pngbin0 -> 546 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@4x.pngbin0 -> 698 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.pngbin0 -> 193 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.svg83
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@2x.pngbin0 -> 294 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@3x.pngbin0 -> 395 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@4x.pngbin0 -> 474 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.pngbin0 -> 205 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.svg93
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@2x.pngbin0 -> 306 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@3x.pngbin0 -> 431 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@4x.pngbin0 -> 574 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.pngbin0 -> 165 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.svg74
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@2x.pngbin0 -> 194 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@3x.pngbin0 -> 241 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@4x.pngbin0 -> 256 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed.9.pngbin0 -> 114 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.9.pngbin0 -> 113 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.svg358
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@2x.9.pngbin0 -> 123 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@3x.9.pngbin0 -> 127 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@4x.9.pngbin0 -> 132 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.pngbin0 -> 281 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.svg86
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@2x.pngbin0 -> 355 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@3x.pngbin0 -> 453 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@4x.pngbin0 -> 569 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.pngbin0 -> 171 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.svg79
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@2x.pngbin0 -> 212 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@3x.pngbin0 -> 251 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@4x.pngbin0 -> 274 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.pngbin0 -> 138 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.svg72
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@2x.pngbin0 -> 163 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@3x.pngbin0 -> 183 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@4x.pngbin0 -> 204 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml256
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml194
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml55
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderBreadcrumbBar.qml74
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialog.qml136
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialogDelegate.qml49
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FontDialog.qml118
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml135
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml273
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml189
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml67
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderBreadcrumbBar.qml58
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialog.qml141
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialogDelegate.qml61
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FontDialog.qml134
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/MessageDialog.qml163
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml249
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml168
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml62
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderBreadcrumbBar.qml72
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialog.qml113
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialogDelegate.qml56
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FontDialog.qml109
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml135
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml257
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml161
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml60
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderBreadcrumbBar.qml70
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialog.qml119
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialogDelegate.qml54
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FontDialog.qml111
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml133
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml261
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/ColorInputs.qml260
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml181
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml56
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml65
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FolderBreadcrumbBar.qml69
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FolderDialog.qml114
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml50
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegateLabel.qml52
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FontDialog.qml115
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FontDialogContent.qml234
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/HueGradient.qml35
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/MessageDialog.qml136
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/PickerHandle.qml31
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/SaturationLightnessPicker.qml38
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp359
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h96
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p_p.h60
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp613
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h150
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h96
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils.cpp24
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils_p.h36
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs.cpp509
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h171
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp55
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h36
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp152
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h60
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp758
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h156
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h88
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar.cpp777
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h94
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h86
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl.cpp374
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h100
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h66
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl.cpp837
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h183
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h71
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp192
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h98
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h54
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog.cpp129
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h53
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog.cpp217
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h58
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog.cpp176
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h58
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog.cpp145
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h53
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog.cpp111
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h48
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker.cpp43
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h46
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign.cpp10
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign_p.h82
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h22
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag54
-rw-r--r--src/quickdialogs/quickdialogsutils/CMakeLists.txt29
-rw-r--r--src/quickdialogs/quickdialogsutils/qquickdialogtype_p.h35
-rw-r--r--src/quickdialogs/quickdialogsutils/qquickfilenamefilter.cpp127
-rw-r--r--src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h69
-rw-r--r--src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h21
198 files changed, 17923 insertions, 0 deletions
diff --git a/src/quickdialogs/CMakeLists.txt b/src/quickdialogs/CMakeLists.txt
new file mode 100644
index 0000000000..2cff983428
--- /dev/null
+++ b/src/quickdialogs/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(quickdialogsutils)
+add_subdirectory(quickdialogsquickimpl)
+add_subdirectory(quickdialogs)
diff --git a/src/quickdialogs/quickdialogs/CMakeLists.txt b/src/quickdialogs/quickdialogs/CMakeLists.txt
new file mode 100644
index 0000000000..529d021d3c
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/CMakeLists.txt
@@ -0,0 +1,57 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## QuickDialogs2 Module:
+#####################################################################
+
+qt_internal_add_qml_module(QuickDialogs2
+ URI "QtQuick.Dialogs"
+ VERSION "${PROJECT_VERSION}"
+ CLASS_NAME QtQuickDialogsPlugin
+ PLUGIN_TARGET qtquickdialogsplugin
+ DEPENDENCIES
+ QtQuick/auto
+ SOURCES
+ qquickabstractdialog.cpp
+ qquickabstractdialog_p.h
+ qquickcolordialog.cpp
+ qquickcolordialog_p.h
+ qquickfiledialog.cpp
+ qquickfiledialog_p.h
+ qquickfolderdialog.cpp
+ qquickfolderdialog_p.h
+ qquickfontdialog.cpp
+ qquickfontdialog_p.h
+ qquickmessagedialog.cpp
+ qquickmessagedialog_p.h
+ qtquickdialogs2foreign.cpp
+ 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::QuickControls2Impl
+ Qt::QuickDialogs2Utils
+ Qt::QuickDialogs2UtilsPrivate
+ Qt::QuickDialogs2QuickImpl
+ Qt::QuickDialogs2QuickImplPrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+ GENERATE_CPP_EXPORTS
+ GENERATE_PRIVATE_CPP_EXPORTS
+)
+
+qt_internal_add_docs(QuickDialogs2
+ doc/qtquickdialogs.qdocconf
+)
diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-colordialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-colordialog-gtk.png
new file mode 100644
index 0000000000..12197f7f74
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-colordialog-gtk.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-filedialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-filedialog-gtk.png
new file mode 100644
index 0000000000..9360d747a2
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-filedialog-gtk.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-folderdialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-folderdialog-gtk.png
new file mode 100644
index 0000000000..45f0585c5d
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-folderdialog-gtk.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-fontdialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-fontdialog-gtk.png
new file mode 100644
index 0000000000..0c6217bdb4
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-fontdialog-gtk.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-android.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-android.png
new file mode 100644
index 0000000000..3986694f7d
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-android.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-informative-android.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-informative-android.png
new file mode 100644
index 0000000000..b2d3cd37cb
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-informative-android.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf b/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf
new file mode 100644
index 0000000000..d4f7595f0f
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf
@@ -0,0 +1,41 @@
+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.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 qtqmlcore qtgui qtdoc qtqml qtquick qtquickcontrols qtlabsplatform
+
+# This module has no documented C++ types, clear the module header
+moduleheader =
+
+exampledirs += snippets
+
+headerdirs += ..
+sourcedirs += .. \
+ src
+
+imagedirs += images
+
+navigation.landingpage = "Qt Quick Dialogs"
+navigation.qmltypespage = "Qt Quick Dialogs QML Types"
+
+tagfile = qtquickdialogs.tags
+
+# Fail the documentation build if there are more warnings than the limit
+warninglimit = 0
diff --git a/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml b/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml
new file mode 100644
index 0000000000..ab3f33f910
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+//! [file]
+import QtCore
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ width: 640
+ height: 480
+ visible: true
+
+ header: ToolBar {
+ Button {
+ text: qsTr("Choose Image...")
+ onClicked: fileDialog.open()
+ }
+ }
+
+ Image {
+ id: image
+ anchors.fill: parent
+ fillMode: Image.PreserveAspectFit
+ }
+
+ FileDialog {
+ id: fileDialog
+ currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
+ onAccepted: image.source = selectedFile
+ }
+}
+//! [file]
diff --git a/src/quickdialogs/quickdialogs/doc/src/includes/fallback.qdocinc b/src/quickdialogs/quickdialogs/doc/src/includes/fallback.qdocinc
new file mode 100644
index 0000000000..f3a4a1805b
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/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/quickdialogs/quickdialogs/doc/src/qtquickdialogs-index.qdoc b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-index.qdoc
new file mode 100644
index 0000000000..bb9f9fc026
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-index.qdoc
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtquickdialogs-index.html
+ \title Qt Quick Dialogs
+
+ \brief Provides QML types for creating and interacting with system dialogs.
+
+ The Qt Quick Dialogs module allows you to create and interact with system
+ dialogs from QML. The module was introduced in Qt 6.2.
+
+ \section1 Using the Module
+
+ \include {module-use.qdocinc} {using the qml api} {QtQuick.Dialogs}
+
+ \section1 Reference
+
+ \list
+ \li \l{Qt Quick Dialogs QML Types}{QML Types}
+ \endlist
+
+ \section1 Related Modules
+
+ \list
+ \li \l{Qt Quick}
+ \li \l{Qt Quick Controls}
+ \li \l{Qt Quick Templates 2}
+ \li \l{Qt Labs Platform}
+ \endlist
+
+ \section1 License and Attributions
+
+ Qt Quick Dialogs is available under commercial licenses from \l{The Qt Company}.
+ In addition, it is available under the
+ \l{GNU Lesser General Public License, version 3}, or
+ the \l{GNU General Public License, version 2}.
+ See \l{Qt Licensing} for further details.
+*/
diff --git a/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-qmltypes.qdoc b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-qmltypes.qdoc
new file mode 100644
index 0000000000..422b824ec3
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-qmltypes.qdoc
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmlmodule QtQuick.Dialogs
+ \title Qt Quick Dialogs QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for creating and interacting with system dialogs.
+
+ The Qt Quick Dialogs module allows to create and interact with system dialogs
+ from QML. The module was introduced in Qt 6.2.
+
+ The QML types can be imported into your
+ application using the following import statement in your \c {.qml} file:
+
+ \qml
+ import QtQuick.Dialogs
+ \endqml
+
+ \section1 QML Types
+
+*/
diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
new file mode 100644
index 0000000000..857a75c7bd
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp
@@ -0,0 +1,481 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 QPlatformDialogHelper handle, which is created in create():
+
+ - First we attempt to create a native dialog (e.g. QWindowsFileDialogHelper) through
+ QGuiApplicationPrivate::platformTheme()->createPlatformDialogHelper().
+ - If that fails, we try to create the Qt Quick fallback dialog (e.g. QQuickPlatformFileDialog)
+ through QQuickDialogImplFactory::createPlatformDialogHelper().
+
+ The handle acts as an intermediary between the QML-facing dialog object
+ and the native/widget/quick implementation:
+
+ +---------------------------+
+ | FileDialog created in QML |
+ +---------------------------+
+ |
+ |
+ v +----------------------+
+ +------------------+ | attempt to create | +------+
+ |useNativeDialog()?|-----false---->| QQuickPlatformDialog |---->| done |
+ +------------------+ | instance and set | +------+
+ | | m_handle to it |
+ | +----------------------+
+ v ^
+ true |
+ | |
+ v |
+ +---------------------+ |
+ | attempt to create | |
+ | QWindowsFileDialog- | |
+ | Helper instance and | |
+ | set m_handle to it | |
+ +---------------------+ |
+ | |
+ v |
+ +-----------------+ |
+ | m_handle valid? |--------------------->false
+ +-----------------+
+ |
+ v
+ true
+ |
+ +------+
+ | done |
+ +------+
+
+ If QWindowsFileDialogHelper is created, it creates a native dialog.
+ If QQuickPlatformDialog is created, it creates a non-native 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.
+ For the non-native dialog, see \l [QML QtQuickControls]{Dialog}.
+
+ 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().
+
+ \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().
+
+ This signal is also emitted when closing the dialog with \l close().
+
+ \sa accepted()
+*/
+
+Q_DECLARE_LOGGING_CATEGORY(lcDialogs)
+
+QQuickAbstractDialog::QQuickAbstractDialog(QQuickDialogType type, QObject *parent)
+ : QObject(parent),
+ m_type(type)
+{
+}
+
+QQuickAbstractDialog::~QQuickAbstractDialog()
+{
+ destroy();
+}
+
+QPlatformDialogHelper *QQuickAbstractDialog::handle() const
+{
+ return m_handle.get();
+}
+
+/*!
+ \qmldefault
+ \qmlproperty list<QtObject> 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) {
+ m_result = Rejected; // in case an accepted dialog gets re-opened, then closed
+ emit visibleChanged();
+ }
+}
+
+/*!
+ \qmlmethod void QtQuick.Dialogs::Dialog::close()
+
+ Closes the dialog and emits either the \l accepted() or \l rejected()
+ signal.
+
+ \sa visible, open()
+*/
+void QQuickAbstractDialog::close()
+{
+ if (!m_handle || !m_visible)
+ return;
+
+ onHide(m_handle.get());
+ m_handle->hide();
+ m_visible = false;
+ emit visibleChanged();
+
+ if (m_result == Accepted)
+ emit accepted();
+ else // if (m_result == Rejected)
+ emit rejected();
+}
+
+/*!
+ \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)
+{
+ setResult(result);
+ close();
+}
+
+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");
+}
+
+QPlatformTheme::DialogType toPlatformDialogType(QQuickDialogType quickDialogType)
+{
+ return quickDialogType == QQuickDialogType::FolderDialog
+ ? QPlatformTheme::FileDialog : static_cast<QPlatformTheme::DialogType>(quickDialogType);
+}
+
+bool QQuickAbstractDialog::create()
+{
+ qCDebug(lcDialogs) << qmlTypeName(this) << "attempting to create dialog backend of type"
+ << int(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(
+ toPlatformDialogType(m_type)));
+ }
+
+ if (!m_handle) {
+ qCDebug(lcDialogs) << "- attempting to create a quick dialog";
+ m_handle = 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(toPlatformDialogType(m_type))) {
+ qCDebug(lcDialogs) << " - the platform theme told us a native dialog isn't available; not using native dialog";
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Called at the end of \l create().
+*/
+void QQuickAbstractDialog::onCreate(QPlatformDialogHelper *dialog)
+{
+ Q_UNUSED(dialog);
+}
+
+/*!
+ \internal
+
+ Called by \l open(), after the call to \l create() and before
+ the handle/helper's \c show function is called.
+*/
+void QQuickAbstractDialog::onShow(QPlatformDialogHelper *dialog)
+{
+ Q_UNUSED(dialog);
+ m_firstShow = false;
+}
+
+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
+
+#include "moc_qquickabstractdialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
new file mode 100644
index 0000000000..f1d046eb89
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h
@@ -0,0 +1,129 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 <QtQuickDialogs2Utils/private/qquickdialogtype_p.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(QQuickDialogType 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;
+
+ bool m_visibleRequested = false;
+ bool m_visible = false;
+ bool m_complete = false;
+ bool m_firstShow = true;
+ StandardCode m_result = Rejected;
+ QWindow *m_parentWindow = nullptr;
+ QString m_title;
+ Qt::WindowFlags m_flags = Qt::Dialog;
+ Qt::WindowModality m_modality = Qt::WindowModal;
+ QQuickDialogType m_type = QQuickDialogType::FileDialog;
+ 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/quickdialogs/quickdialogs/qquickcolordialog.cpp b/src/quickdialogs/quickdialogs/qquickcolordialog.cpp
new file mode 100644
index 0000000000..e91965e69d
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickcolordialog.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickcolordialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype ColorDialog
+ \inherits Dialog
+ \inqmlmodule QtQuick.Dialogs
+ \since 6.4
+ \brief A color dialog.
+
+ The ColorDialog type provides a QML API for color dialogs.
+
+ \image qtquickdialogs-colordialog-gtk.png
+
+ To show a color dialog, construct an instance of ColorDialog, set the
+ desired properties, and call \l {Dialog::}{open()}. The \l selectedColor
+ property can be used to determine the initially selected color in the
+ dialog.
+
+ \code
+ MenuItem {
+ text: qsTr("Color")
+ onTriggered: colorDialog.open()
+ }
+
+ ColorDialog {
+ id: colorDialog
+ selectedColor: document.color
+ onAccepted: document.color = selectedColor
+ }
+
+ MyDocument {
+ id: document
+ }
+ \endcode
+
+ \section2 Availability
+
+ A native platform color dialog is currently available on the following platforms:
+
+ \list
+ \li macOS
+ \li Linux (when running with the GTK+ platform theme)
+ \endlist
+
+ \include includes/fallback.qdocinc
+*/
+
+QQuickColorDialog::QQuickColorDialog(QObject *parent)
+ : QQuickAbstractDialog(QQuickDialogType::ColorDialog, parent),
+ m_options(QColorDialogOptions::create()),
+ m_selectedColor(QColorConstants::White)
+{
+}
+
+/*!
+ \qmlproperty color QtQuick.Dialogs::ColorDialog::selectedColor
+
+ This property holds the currently selected color in the dialog.
+
+ The \l {Dialog::}{accepted()} signal can be handled to get the final selection.
+ When the user has clicked \uicontrol Open to accept a color, a signal handler
+ for the \l {Dialog::}{accepted()} signal can query the selectedColor property to
+ get the final color that was selected by the user.
+
+ \sa {Dialog::}{accepted()}
+*/
+QColor QQuickColorDialog::selectedColor() const
+{
+ return m_selectedColor;
+}
+
+void QQuickColorDialog::setSelectedColor(const QColor &color)
+{
+ if (color == m_selectedColor)
+ return;
+
+ m_selectedColor = color;
+
+ emit selectedColorChanged();
+}
+
+/*!
+ \qmlproperty flags QtQuick.Dialogs::ColorDialog::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 ColorDialog.ShowAlphaChannel Show a slider and additional input fields for the alpha value.
+ \value ColorDialog.NoButtons Don't display \uicontrol Open and \uicontrol Cancel buttons (useful
+ for "live dialogs").
+ \value ColorDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation.
+*/
+
+QColorDialogOptions::ColorDialogOptions QQuickColorDialog::options() const
+{
+ return m_options->options();
+}
+
+void QQuickColorDialog::setOptions(QColorDialogOptions::ColorDialogOptions options)
+{
+ if (options == m_options->options())
+ return;
+
+ m_options->setOptions(options);
+ emit optionsChanged();
+}
+
+void QQuickColorDialog::resetOptions()
+{
+ setOptions({});
+}
+
+bool QQuickColorDialog::useNativeDialog() const
+{
+ return QQuickAbstractDialog::useNativeDialog()
+ && !(m_options->testOption(QColorDialogOptions::DontUseNativeDialog));
+}
+
+void QQuickColorDialog::onCreate(QPlatformDialogHelper *dialog)
+{
+ if (auto colorDialog = qobject_cast<QPlatformColorDialogHelper *>(dialog)) {
+ connect(colorDialog, &QPlatformColorDialogHelper::currentColorChanged, this,
+ [this, colorDialog]() { setSelectedColor(colorDialog->currentColor()); });
+ colorDialog->setOptions(m_options);
+ }
+}
+
+void QQuickColorDialog::onShow(QPlatformDialogHelper *dialog)
+{
+ m_options->setWindowTitle(title());
+ if (auto colorDialog = qobject_cast<QPlatformColorDialogHelper *>(dialog)) {
+ colorDialog->setOptions(m_options);
+ colorDialog->setCurrentColor(m_selectedColor);
+ }
+
+ QQuickAbstractDialog::onShow(dialog);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs/quickdialogs/qquickcolordialog_p.h b/src/quickdialogs/quickdialogs/qquickcolordialog_p.h
new file mode 100644
index 0000000000..9b424d6b7e
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickcolordialog_p.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKCOLORDIALOG_P_H
+#define QQUICKCOLORDIALOG_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 "qquickabstractdialog_p.h"
+
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickColorDialog : public QQuickAbstractDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged)
+ Q_PROPERTY(QColorDialogOptions::ColorDialogOptions options READ options WRITE setOptions RESET resetOptions NOTIFY optionsChanged)
+ Q_FLAGS(QColorDialogOptions::ColorDialogOptions)
+ QML_NAMED_ELEMENT(ColorDialog)
+ QML_ADDED_IN_VERSION(6, 4)
+
+public:
+ explicit QQuickColorDialog(QObject *parent = nullptr);
+
+ QColor selectedColor() const;
+ void setSelectedColor(const QColor &color);
+
+ QColorDialogOptions::ColorDialogOptions options() const;
+ void setOptions(QColorDialogOptions::ColorDialogOptions options);
+ void resetOptions();
+
+Q_SIGNALS:
+ void selectedColorChanged();
+ void optionsChanged();
+
+protected:
+ bool useNativeDialog() const override;
+ void onCreate(QPlatformDialogHelper *dialog) override;
+ void onShow(QPlatformDialogHelper *dialog) override;
+
+private:
+ QSharedPointer<QColorDialogOptions> m_options;
+ QColor m_selectedColor;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickColorDialog)
+
+#endif // QQUICKCOLORDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickfiledialog.cpp b/src/quickdialogs/quickdialogs/qquickfiledialog.cpp
new file mode 100644
index 0000000000..6b1cdb860c
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickfiledialog.cpp
@@ -0,0 +1,601 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickfiledialog_p.h"
+
+#include <QtCore/qlist.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_DECLARE_LOGGING_CATEGORY(lcDialogs)
+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.
+
+ \snippet qtquickdialogs-filedialog.qml file
+
+ \section2 Availability
+
+ A native platform file dialog is currently available on the following platforms:
+
+ \list
+ \li iOS
+ \li Android
+ \li Linux (when running with the GTK+ platform theme)
+ \li macOS
+ \li Windows
+ \endlist
+
+ \include includes/fallback.qdocinc
+
+ \sa FolderDialog, {QtCore::}{StandardPaths}
+*/
+
+QQuickFileDialog::QQuickFileDialog(QObject *parent)
+ : QQuickAbstractDialog(QQuickDialogType::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
+
+ This property holds the last file that was selected in the dialog.
+
+ It can be set to control the file that is selected when the dialog is
+ opened.
+
+ If there are multiple selected files, this property refers to the first
+ file.
+
+ The value of this property is updated each time the user selects a file in
+ the dialog, and when the dialog is accepted. Handle the
+ \l {Dialog::}{accepted()} signal to get the final selection.
+
+ \sa selectedFiles, {Dialog::}{accepted()}, currentFolder
+*/
+QUrl QQuickFileDialog::selectedFile() const
+{
+ return addDefaultSuffix(m_selectedFiles.value(0));
+}
+
+void QQuickFileDialog::setSelectedFile(const QUrl &selectedFile)
+{
+ setSelectedFiles({ selectedFile });
+}
+
+/*!
+ \qmlproperty list<url> QtQuick.Dialogs::FileDialog::selectedFiles
+
+ This property holds the last files that were selected in the dialog.
+
+ The value of this property is updated each time the user selects files in
+ the dialog, and when the dialog is accepted. Handle the
+ \l {Dialog::}{accepted()} signal to get the final selection.
+
+ \sa {Dialog::}{accepted()}, currentFolder
+*/
+QList<QUrl> QQuickFileDialog::selectedFiles() const
+{
+ return addDefaultSuffixes(m_selectedFiles);
+}
+
+void QQuickFileDialog::setSelectedFiles(const QList<QUrl> &selectedFiles)
+{
+ qCDebug(lcFileDialog) << "setSelectedFiles called with" << selectedFiles;
+ if (m_selectedFiles == selectedFiles)
+ return;
+
+ if (m_fileMode == SaveFile && selectedFiles.size() > 1) {
+ qmlWarning(this) << "Cannot set more than one selected file when fileMode is SaveFile";
+ return;
+ }
+
+ if (m_fileMode != SaveFile) {
+ for (const auto &selectedFile : selectedFiles) {
+ const QString selectedFilePath = QQmlFile::urlToLocalFileOrQrc(selectedFile);
+ if (!QFileInfo::exists(selectedFilePath)) {
+ qmlWarning(this) << "Cannot set " << selectedFilePath
+ << " as a selected file because it doesn't exist";
+ return;
+ }
+ }
+ }
+
+ const auto newFirstSelectedFile = selectedFiles.value(0);
+ const bool firstChanged = m_selectedFiles.value(0) != newFirstSelectedFile;
+ m_selectedFiles = selectedFiles;
+ m_options->setInitiallySelectedFiles(m_selectedFiles);
+ if (firstChanged) {
+ emit selectedFileChanged();
+ emit currentFileChanged();
+ }
+ emit selectedFilesChanged();
+ emit currentFilesChanged();
+}
+
+/*!
+ \qmlproperty url QtQuick.Dialogs::FileDialog::currentFile
+ \deprecated [6.3] Use \l selectedFile instead.
+
+ This property holds the currently selected file in the dialog.
+
+ \sa selectedFile, currentFiles, currentFolder
+*/
+QUrl QQuickFileDialog::currentFile() const
+{
+ return selectedFile();
+}
+
+void QQuickFileDialog::setCurrentFile(const QUrl &file)
+{
+ setSelectedFiles(QList<QUrl>() << file);
+}
+
+/*!
+ \qmlproperty list<url> QtQuick.Dialogs::FileDialog::currentFiles
+ \deprecated [6.3] Use \l selectedFiles instead.
+
+ This property holds the currently selected files in the dialog.
+
+ \sa selectedFiles, currentFile, currentFolder
+*/
+QList<QUrl> QQuickFileDialog::currentFiles() const
+{
+ return selectedFiles();
+}
+
+void QQuickFileDialog::setCurrentFiles(const QList<QUrl> &currentFiles)
+{
+ setSelectedFiles(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 \l FolderDialog instead.
+*/
+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.
+ \value FileDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation.
+*/
+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.size())
+ 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, [this, fileDialog](){ setSelectedFiles(fileDialog->selectedFiles()); });
+ connect(fileDialog, &QPlatformFileDialogHelper::directoryEntered, this, &QQuickFileDialog::currentFolderChanged);
+ fileDialog->setOptions(m_options);
+
+ // If the user didn't set an initial selectedFile, ensure that we are synced
+ // with the underlying dialog in case it has set an initially selected file
+ // (as QQuickFileDialogImplPrivate::updateSelectedFile does).
+ if (m_options->initiallySelectedFiles().isEmpty()) {
+ const auto selectedFiles = fileDialog->selectedFiles();
+ if (!selectedFiles.isEmpty())
+ setSelectedFiles(selectedFiles);
+ }
+ }
+}
+
+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);
+
+ // If both selectedFile and currentFolder are set, prefer the former.
+ if (!m_options->initiallySelectedFiles().isEmpty()) {
+ // The user set an initial selectedFile.
+ const QUrl selectedFile = m_options->initiallySelectedFiles().first();
+ fileDialog->selectFile(selectedFile);
+ } else {
+ // The user set an initial currentFolder.
+ 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());
+ }
+ }
+ QQuickAbstractDialog::onShow(dialog);
+}
+
+void QQuickFileDialog::onHide(QPlatformDialogHelper *dialog)
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) {
+ if (m_selectedNameFilter)
+ disconnect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickFileNameFilter::update);
+ }
+}
+
+QUrl QQuickFileDialog::addDefaultSuffix(const QUrl &file) const
+{
+ QUrl url = file;
+ const QString path = url.path();
+ const QString suffix = m_options->defaultSuffix();
+ // Urls with "content" scheme do not require suffixes. Such schemes are
+ // used on Android.
+ const bool isContentScheme = url.scheme() == u"content"_s;
+ if (!isContentScheme && !suffix.isEmpty() && !path.endsWith(QLatin1Char('/'))
+ && path.lastIndexOf(QLatin1Char('.')) == -1) {
+ url.setPath(path + QLatin1Char('.') + suffix);
+ }
+ return url;
+}
+
+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();
+}
+
+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
+
+#include "moc_qquickfiledialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickfiledialog_p.h b/src/quickdialogs/quickdialogs/qquickfiledialog_p.h
new file mode 100644
index 0000000000..7214c907f3
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickfiledialog_p.h
@@ -0,0 +1,132 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 WRITE setSelectedFile 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;
+ void setSelectedFile(const QUrl &selectedFile);
+
+ 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;
+ QSharedPointer<QFileDialogOptions> m_options;
+ mutable QQuickFileNameFilter *m_selectedNameFilter;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFileDialog)
+
+#endif // QQUICKFILEDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickfolderdialog.cpp b/src/quickdialogs/quickdialogs/qquickfolderdialog.cpp
new file mode 100644
index 0000000000..692a68c7a6
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickfolderdialog.cpp
@@ -0,0 +1,264 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickfolderdialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtQml/qqmlfile.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcDialogs)
+
+/*!
+ \qmltype FolderDialog
+ \inherits Dialog
+//! \instantiates QQuickFolderDialog
+ \inqmlmodule QtQuick.Dialogs
+ \since 6.3
+ \brief A native folder dialog.
+
+ The FolderDialog type provides a QML API for native platform folder dialogs.
+
+ \image qtquickdialogs-folderdialog-gtk.png
+
+ To show a folder dialog, construct an instance of FolderDialog, set the
+ desired properties, and call \l {Dialog::}{open()}. The \l currentFolder
+ property can be used to determine the folder that is currently being
+ displayed in the dialog. The \l selectedFolder property can be used to
+ determine the last folder that was selected in the dialog.
+
+ \code
+ MenuItem {
+ text: qsTr("Open...")
+ onTriggered: folderDialog.open()
+ }
+
+ FolderDialog {
+ id: folderDialog
+ currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
+ selectedFolder: viewer.folder
+ }
+
+ MyViewer {
+ id: viewer
+ folder: folderDialog.selectedFolder
+ }
+ \endcode
+
+ \section2 Availability
+
+ A native platform folder 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 FileDialog, {QtCore::}{StandardPaths}
+*/
+
+QQuickFolderDialog::QQuickFolderDialog(QObject *parent)
+ : QQuickAbstractDialog(QQuickDialogType::FolderDialog, parent),
+ m_options(QFileDialogOptions::create())
+{
+ m_options->setFileMode(QFileDialogOptions::Directory);
+ m_options->setAcceptMode(QFileDialogOptions::AcceptOpen);
+ m_options->setInitialDirectory(QUrl::fromLocalFile(QDir::currentPath()));
+}
+
+/*!
+ \qmlproperty url QtQuick.Dialogs::FolderDialog::currentFolder
+
+ This property holds the folder that is currently being displayed in the dialog.
+
+ \sa selectedFolder
+*/
+QUrl QQuickFolderDialog::currentFolder() const
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle()))
+ return fileDialog->directory();
+ return m_options->initialDirectory();
+}
+
+void QQuickFolderDialog::setCurrentFolder(const QUrl &folder)
+{
+ if (folder == m_options->initialDirectory())
+ return;
+
+ m_options->setInitialDirectory(folder);
+ emit currentFolderChanged();
+}
+
+/*!
+ \qmlproperty url QtQuick.Dialogs::FolderDialog::selectedFolder
+
+ This property holds the last folder that was selected in the dialog.
+
+ The value of this property is updated each time the user selects a folder
+ in the dialog, and when the dialog is accepted. Alternatively, the
+ \l {Dialog::}{accepted()} signal can be handled to get the final selection.
+
+ \sa currentFolder, {Dialog::}{accepted()}
+*/
+QUrl QQuickFolderDialog::selectedFolder() const
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) {
+ const QList<QUrl> selectedFiles = fileDialog->selectedFiles();
+ if (!selectedFiles.isEmpty())
+ return selectedFiles.first();
+ }
+ return QUrl();
+}
+
+void QQuickFolderDialog::setSelectedFolder(const QUrl &folder)
+{
+ if (folder == selectedFolder())
+ return;
+
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) {
+ fileDialog->selectFile(folder);
+ emit selectedFolderChanged();
+ }
+}
+
+/*!
+ \qmlproperty flags QtQuick.Dialogs::FolderDialog::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 FolderDialog.DontResolveSymlinks Don't resolve symlinks in the folder dialog. By default symlinks are resolved.
+ \value FolderDialog.ReadOnly Indicates that the dialog doesn't allow creating directories.
+ \value FolderDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation.
+*/
+QFileDialogOptions::FileDialogOptions QQuickFolderDialog::options() const
+{
+ return m_options->options();
+}
+
+void QQuickFolderDialog::setOptions(QFileDialogOptions::FileDialogOptions options)
+{
+ if (options == m_options->options())
+ return;
+
+ m_options->setOptions(options);
+ emit optionsChanged();
+}
+
+void QQuickFolderDialog::resetOptions()
+{
+ setOptions({});
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::FolderDialog::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.
+
+ The default value is an empty string.
+
+ \sa rejectLabel
+*/
+QString QQuickFolderDialog::acceptLabel() const
+{
+ return m_options->labelText(QFileDialogOptions::Accept);
+}
+
+void QQuickFolderDialog::setAcceptLabel(const QString &label)
+{
+ if (label == m_options->labelText(QFileDialogOptions::Accept))
+ return;
+
+ m_options->setLabelText(QFileDialogOptions::Accept, label);
+ emit acceptLabelChanged();
+}
+
+void QQuickFolderDialog::resetAcceptLabel()
+{
+ setAcceptLabel(QString());
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::FolderDialog::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 QQuickFolderDialog::rejectLabel() const
+{
+ return m_options->labelText(QFileDialogOptions::Reject);
+}
+
+void QQuickFolderDialog::setRejectLabel(const QString &label)
+{
+ if (label == m_options->labelText(QFileDialogOptions::Reject))
+ return;
+
+ m_options->setLabelText(QFileDialogOptions::Reject, label);
+ emit rejectLabelChanged();
+}
+
+void QQuickFolderDialog::resetRejectLabel()
+{
+ setRejectLabel(QString());
+}
+
+bool QQuickFolderDialog::useNativeDialog() const
+{
+ if (!QQuickAbstractDialog::useNativeDialog())
+ return false;
+
+ if (m_options->testOption(QFileDialogOptions::DontUseNativeDialog)) {
+ qCDebug(lcDialogs) << " - the FolderDialog was told not to use a native dialog; not using native dialog";
+ return false;
+ }
+
+ return true;
+}
+
+void QQuickFolderDialog::onCreate(QPlatformDialogHelper *dialog)
+{
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) {
+ connect(fileDialog, &QPlatformFileDialogHelper::directoryEntered, this, &QQuickFolderDialog::currentFolderChanged);
+ connect(fileDialog, &QPlatformFileDialogHelper::currentChanged, this, &QQuickFolderDialog::selectedFolderChanged);
+ fileDialog->setOptions(m_options);
+ }
+}
+
+void QQuickFolderDialog::onShow(QPlatformDialogHelper *dialog)
+{
+ m_options->setWindowTitle(title());
+ if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) {
+ fileDialog->setOptions(m_options);
+
+ const QUrl initialDir = m_options->initialDirectory();
+ // If it's not valid, we shouldn't set it.
+ if (m_firstShow && initialDir.isValid() && QDir(QQmlFile::urlToLocalFileOrQrc(initialDir)).exists())
+ fileDialog->setDirectory(m_options->initialDirectory());
+ }
+ QQuickAbstractDialog::onShow(dialog);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickfolderdialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h b/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h
new file mode 100644
index 0000000000..e1231ab3a5
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKFOLDERDIALOG_P_H
+#define QQUICKFOLDERDIALOG_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 QQuickFolderDialog : public QQuickAbstractDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL)
+ Q_PROPERTY(QUrl selectedFolder READ selectedFolder WRITE setSelectedFolder NOTIFY selectedFolderChanged FINAL)
+ Q_PROPERTY(QFileDialogOptions::FileDialogOptions options READ options WRITE setOptions RESET resetOptions NOTIFY optionsChanged 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(FolderDialog)
+ QML_ADDED_IN_VERSION(6, 3)
+
+public:
+ explicit QQuickFolderDialog(QObject *parent = nullptr);
+
+ QUrl currentFolder() const;
+ void setCurrentFolder(const QUrl &folder);
+
+ QUrl selectedFolder() const;
+ void setSelectedFolder(const QUrl &folder);
+
+ QFileDialogOptions::FileDialogOptions options() const;
+ void setOptions(QFileDialogOptions::FileDialogOptions options);
+ void resetOptions();
+
+ QString acceptLabel() const;
+ void setAcceptLabel(const QString &label);
+ void resetAcceptLabel();
+
+ QString rejectLabel() const;
+ void setRejectLabel(const QString &label);
+ void resetRejectLabel();
+
+Q_SIGNALS:
+ void currentFolderChanged();
+ void selectedFolderChanged();
+ void optionsChanged();
+ void acceptLabelChanged();
+ void rejectLabelChanged();
+
+protected:
+ bool useNativeDialog() const override;
+ void onCreate(QPlatformDialogHelper *dialog) override;
+ void onShow(QPlatformDialogHelper *dialog) override;
+
+private:
+ QSharedPointer<QFileDialogOptions> m_options;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFolderDialog)
+
+#endif // QQUICKFOLDERDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickfontdialog.cpp b/src/quickdialogs/quickdialogs/qquickfontdialog.cpp
new file mode 100644
index 0000000000..ff3e73b3c2
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickfontdialog.cpp
@@ -0,0 +1,189 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickfontdialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype FontDialog
+ \inherits Dialog
+//! \instantiates QQuickFontDialog
+ \inqmlmodule QtQuick.Dialogs
+ \since 6.2
+ \brief A font dialog.
+
+ The FontDialog type provides a QML API for font dialogs.
+
+ \image qtquickdialogs-fontdialog-gtk.png
+
+ To show a font dialog, construct an instance of FontDialog, set the
+ desired properties, and call \l {Dialog::}{open()}. The \l currentFont
+ property can be used to determine the currently selected font in the
+ dialog. The \l selectedFont property is updated only after the final selection
+ has been made by accepting the dialog.
+
+ \code
+ MenuItem {
+ text: "Font"
+ onTriggered: fontDialog.open()
+ }
+
+ FontDialog {
+ id: fontDialog
+ currentFont.family: document.font
+ }
+
+ MyDocument {
+ id: document
+ font: fontDialog.selectedFont
+ }
+ \endcode
+
+ \section2 Availability
+
+ A native platform font dialog is currently available on the following platforms:
+
+ \list
+ \li macOS
+ \li Linux (when running with the GTK+ platform theme)
+ \endlist
+
+ \include includes/fallback.qdocinc
+*/
+
+Q_LOGGING_CATEGORY(lcFontDialog, "qt.quick.dialogs.fontdialog")
+
+QQuickFontDialog::QQuickFontDialog(QObject *parent)
+ : QQuickAbstractDialog(QQuickDialogType::FontDialog, parent),
+ m_options(QFontDialogOptions::create())
+{
+}
+
+/*!
+ \qmlproperty font QtQuick.Dialogs::FontDialog::currentFont
+ \deprecated [6.3] Use \l selectedFont instead.
+
+ This property holds the currently selected font in the dialog.
+
+ The \c currentFont property is updated while the user is selecting
+ fonts in the dialog, even before the final selection has been made.
+
+ \sa selectedFont
+*/
+
+QFont QQuickFontDialog::currentFont() const
+{
+ return selectedFont();
+}
+
+void QQuickFontDialog::setCurrentFont(const QFont &font)
+{
+ setSelectedFont(font);
+}
+
+/*!
+ \qmlproperty font QtQuick.Dialogs::FontDialog::selectedFont
+
+ This property holds the currently selected font in the dialog.
+
+ The \c selectedFont property is updated while the user is selecting
+ fonts in the dialog, even before the final selection has been made.
+
+ The \l {Dialog::}{accepted()} signal can be handled to get the final selection.
+ When the user has clicked \uicontrol Open to accept a font, a signal handler
+ for the \l {Dialog::}{accepted()} signal can query the selectedFont property to
+ get the final font that was selected by the user.
+
+ \sa currentFont, {Dialog::}{accepted()}
+*/
+
+QFont QQuickFontDialog::selectedFont() const
+{
+ return m_selectedFont;
+}
+
+void QQuickFontDialog::setSelectedFont(const QFont &font)
+{
+ if (font == m_selectedFont)
+ return;
+
+ m_selectedFont = font;
+
+ emit selectedFontChanged();
+ emit currentFontChanged();
+}
+
+/*!
+ \qmlproperty flags QtQuick.Dialogs::FontDialog::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 FontDialog.ScalableFonts Show scalable fonts.
+ \value FontDialog.NonScalableFonts Show non-scalable fonts.
+ \value FontDialog.MonospacedFonts Show monospaced fonts.
+ \value FontDialog.ProportionalFonts Show proportional fonts.
+ \value FontDialog.NoButtons Don't display \uicontrol Open and \uicontrol Cancel buttons (useful
+ for "live dialogs").
+ \value FontDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation.
+*/
+
+QFontDialogOptions::FontDialogOptions QQuickFontDialog::options() const
+{
+ return m_options->options();
+}
+
+void QQuickFontDialog::setOptions(QFontDialogOptions::FontDialogOptions options)
+{
+ if (options == m_options->options())
+ return;
+
+ m_options->setOptions(options);
+ emit optionsChanged();
+}
+
+void QQuickFontDialog::resetOptions()
+{
+ setOptions({});
+}
+
+bool QQuickFontDialog::useNativeDialog() const
+{
+ return QQuickAbstractDialog::useNativeDialog()
+ && !(m_options->testOption(QFontDialogOptions::DontUseNativeDialog));
+}
+
+void QQuickFontDialog::onCreate(QPlatformDialogHelper *dialog)
+{
+ if (QPlatformFontDialogHelper *fontDialog = qobject_cast<QPlatformFontDialogHelper *>(dialog)) {
+ connect(fontDialog, &QPlatformFontDialogHelper::currentFontChanged, this,
+ [this, fontDialog]() { setSelectedFont(fontDialog->currentFont()); });
+ connect(this, &QQuickFontDialog::selectedFontChanged, this,
+ [this, fontDialog]() { fontDialog->setCurrentFont(m_selectedFont); });
+ fontDialog->setOptions(m_options);
+ }
+}
+
+void QQuickFontDialog::onShow(QPlatformDialogHelper *dialog)
+{
+ m_options->setWindowTitle(title());
+ if (QPlatformFontDialogHelper *fontDialog = qobject_cast<QPlatformFontDialogHelper *>(dialog)) {
+ fontDialog->setOptions(m_options); // setOptions only assigns a member and isn't virtual
+ fontDialog->setCurrentFont(m_selectedFont);
+ }
+
+ QQuickAbstractDialog::onShow(dialog);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickfontdialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickfontdialog_p.h b/src/quickdialogs/quickdialogs/qquickfontdialog_p.h
new file mode 100644
index 0000000000..f5df6d2a9f
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickfontdialog_p.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKFONTDIALOG_P_H
+#define QQUICKFONTDIALOG_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/qfont.h>
+
+#include "qquickabstractdialog_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFontDialog : public QQuickAbstractDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont NOTIFY selectedFontChanged)
+ Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont NOTIFY currentFontChanged FINAL)
+ Q_PROPERTY(QFontDialogOptions::FontDialogOptions options READ options WRITE setOptions
+ RESET resetOptions NOTIFY optionsChanged)
+ Q_FLAGS(QFontDialogOptions::FontDialogOptions)
+ QML_NAMED_ELEMENT(FontDialog)
+ QML_ADDED_IN_VERSION(6, 2)
+
+public:
+ explicit QQuickFontDialog(QObject *parent = nullptr);
+
+ void setCurrentFont(const QFont &font);
+ QFont currentFont() const;
+
+ void setSelectedFont(const QFont &font);
+ QFont selectedFont() const;
+
+ QFontDialogOptions::FontDialogOptions options() const;
+ void setOptions(QFontDialogOptions::FontDialogOptions options);
+ void resetOptions();
+
+Q_SIGNALS:
+ void selectedFontChanged();
+ void currentFontChanged();
+ void optionsChanged();
+
+protected:
+ bool useNativeDialog() const override;
+ void onCreate(QPlatformDialogHelper *dialog) override;
+ void onShow(QPlatformDialogHelper *dialog) override;
+
+private:
+ QSharedPointer<QFontDialogOptions> m_options;
+ QFont m_selectedFont;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFontDialog)
+
+#endif // QQUICKFONTDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp
new file mode 100644
index 0000000000..10f7abe17b
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp
@@ -0,0 +1,244 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmessagedialog_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype MessageDialog
+ \inherits Dialog
+//! \instantiates QQuickMessageDialog
+ \inqmlmodule QtQuick.Dialogs
+ \since 6.3
+ \brief A message dialog.
+
+ The MessageDialog type provides a QML API for message dialogs.
+
+ \image qtquickdialogs-messagedialog-android.png
+
+ A message dialog is used to inform the user, or ask the user a question.
+ A message dialog displays a primary \l text to alert the user to a situation,
+ an \l {informativeText}{informative text} to further explain the alert or to
+ ask the user a question, and an optional \l {detailedText}{detailed text} to
+ provide even more data if the user requests it. A message box can also display
+ a configurable set of \l buttons for accepting a user response.
+
+ To show a message dialog, construct an instance of MessageDialog, set the
+ desired properties, and call \l {Dialog::}{open()}.
+
+ \code
+ MessageDialog {
+ buttons: MessageDialog.Ok
+ text: "The document has been modified."
+ }
+ \endcode
+
+ The user must click the \uicontrol OK button to dismiss the message dialog.
+
+ A more elaborate approach than just alerting the user to an event is to
+ also ask the user what to do about it. Store the question in the
+ \l {informativeText}{informative text} property, and specify the \l buttons
+ property to the set of buttons you want as the set of user responses. The
+ buttons are specified by combining values using the bitwise OR operator.
+
+ \code
+ MessageDialog {
+ text: "The document has been modified."
+ informativeText: "Do you want to save your changes?"
+ buttons: MessageDialog.Ok | MessageDialog.Cancel
+
+ onAccepted: document.save()
+ }
+ \endcode
+
+ \image qtquickdialogs-messagedialog-informative-android.png
+
+
+ \section2 Availability
+
+ A native platform message dialog is currently available on the following platforms:
+
+ \list
+ \li iOS
+ \li Android
+ \endlist
+
+ \include includes/fallback.qdocinc
+*/
+
+/*!
+ \qmlsignal QtQuick.Dialogs::MessageDialog::buttonClicked(QPlatformDialogHelper::StandardButton button, QPlatformDialogHelper::ButtonRole role)
+
+ This signal is emitted when a \a button with the specified \a role is
+ clicked.
+
+ By giving this signal a handler, you can respond to any custom button being pressed.
+ The \a button argument tells which button was clicked, while the \a role argument tells the functional role of that button.
+
+ \code
+ MessageDialog {
+ id: dialog
+ text: qsTr("The document has been modified.")
+ informativeText: qsTr("Do you want to save your changes?")
+ buttons: MessageDialog.Ok | MessageDialog.Cancel
+ onButtonClicked: function (button, role) {
+ switch (button) {
+ case MessageDialog.Ok:
+ document.save()
+ break;
+ }
+ }
+ }
+ \endcode
+
+ \sa buttons
+*/
+
+QQuickMessageDialog::QQuickMessageDialog(QObject *parent)
+ : QQuickAbstractDialog(QQuickDialogType::MessageDialog, parent),
+ m_options(QMessageDialogOptions::create())
+{
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::MessageDialog::text
+
+ This property holds the text to be displayed on the message dialog.
+
+ \sa informativeText, detailedText
+*/
+QString QQuickMessageDialog::text() const
+{
+ return m_options->text();
+}
+
+void QQuickMessageDialog::setText(const QString &text)
+{
+ if (m_options->text() == text)
+ return;
+
+ m_options->setText(text);
+
+ emit textChanged();
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::MessageDialog::informativeText
+
+ This property holds the informative text that provides a fuller description for the message.
+
+ Informative text can be used to expand upon the \l text to give more information to the user.
+
+ \sa text, detailedText
+*/
+QString QQuickMessageDialog::informativeText() const
+{
+ return m_options->informativeText();
+}
+
+void QQuickMessageDialog::setInformativeText(const QString &text)
+{
+ if (m_options->informativeText() == text)
+ return;
+
+ m_options->setInformativeText(text);
+
+ emit informativeTextChanged();
+}
+
+/*!
+ \qmlproperty string QtQuick.Dialogs::MessageDialog::detailedText
+
+ This property holds the text to be displayed in the details area.
+
+ \sa text, informativeText
+*/
+QString QQuickMessageDialog::detailedText() const
+{
+ return m_options->detailedText();
+}
+
+void QQuickMessageDialog::setDetailedText(const QString &text)
+{
+ if (m_options->detailedText() == text)
+ return;
+
+ m_options->setDetailedText(text);
+
+ emit detailedTextChanged();
+}
+
+/*!
+ \qmlproperty flags QtQuick.Dialogs::MessageDialog::buttons
+
+ This property holds a combination of buttons that are used by the message dialog.
+ The default value is \c MessageDialog.NoButton.
+
+ Possible flags:
+ \value MessageDialog.Ok An "OK" button defined with the \c AcceptRole.
+ \value MessageDialog.Open An "Open" button defined with the \c AcceptRole.
+ \value MessageDialog.Save A "Save" button defined with the \c AcceptRole.
+ \value MessageDialog.Cancel A "Cancel" button defined with the \c RejectRole.
+ \value MessageDialog.Close A "Close" button defined with the \c RejectRole.
+ \value MessageDialog.Discard A "Discard" or "Don't Save" button, depending on the platform, defined with the \c DestructiveRole.
+ \value MessageDialog.Apply An "Apply" button defined with the \c ApplyRole.
+ \value MessageDialog.Reset A "Reset" button defined with the \c ResetRole.
+ \value MessageDialog.RestoreDefaults A "Restore Defaults" button defined with the \c ResetRole.
+ \value MessageDialog.Help A "Help" button defined with the \c HelpRole.
+ \value MessageDialog.SaveAll A "Save All" button defined with the \c AcceptRole.
+ \value MessageDialog.Yes A "Yes" button defined with the \c YesRole.
+ \value MessageDialog.YesToAll A "Yes to All" button defined with the \c YesRole.
+ \value MessageDialog.No A "No" button defined with the \c NoRole.
+ \value MessageDialog.NoToAll A "No to All" button defined with the \c NoRole.
+ \value MessageDialog.Abort An "Abort" button defined with the \c RejectRole.
+ \value MessageDialog.Retry A "Retry" button defined with the \c AcceptRole.
+ \value MessageDialog.Ignore An "Ignore" button defined with the \c AcceptRole.
+ \value MessageDialog.NoButton The dialog has no buttons.
+
+ \sa buttonClicked()
+*/
+
+QPlatformDialogHelper::StandardButtons QQuickMessageDialog::buttons() const
+{
+ return m_options->standardButtons();
+}
+
+void QQuickMessageDialog::setButtons(QPlatformDialogHelper::StandardButtons buttons)
+{
+ if (m_options->standardButtons() == buttons)
+ return;
+
+ m_options->setStandardButtons(buttons);
+
+ emit buttonsChanged();
+}
+
+void QQuickMessageDialog::handleClick(QPlatformDialogHelper::StandardButton button,
+ QPlatformDialogHelper::ButtonRole role)
+{
+ emit buttonClicked(button, role);
+}
+
+void QQuickMessageDialog::onCreate(QPlatformDialogHelper *dialog)
+{
+ if (QPlatformMessageDialogHelper *messageDialog =
+ qobject_cast<QPlatformMessageDialogHelper *>(dialog)) {
+ connect(messageDialog, &QPlatformMessageDialogHelper::clicked, this,
+ &QQuickMessageDialog::handleClick);
+ messageDialog->setOptions(m_options);
+ }
+}
+
+void QQuickMessageDialog::onShow(QPlatformDialogHelper *dialog)
+{
+ m_options->setWindowTitle(title());
+
+ if (QPlatformMessageDialogHelper *messageDialog =
+ qobject_cast<QPlatformMessageDialogHelper *>(dialog))
+ messageDialog->setOptions(m_options); // setOptions only assigns a member and isn't virtual
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickmessagedialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h b/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h
new file mode 100644
index 0000000000..40a2b6da8d
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKMESSAGEDIALOG_P_H
+#define QQUICKMESSAGEDIALOG_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 "qquickabstractdialog_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickMessageDialog : public QQuickAbstractDialog
+{
+ Q_OBJECT
+
+private:
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL)
+ Q_PROPERTY(QString informativeText READ informativeText WRITE setInformativeText NOTIFY informativeTextChanged FINAL)
+ Q_PROPERTY(QString detailedText READ detailedText WRITE setDetailedText NOTIFY detailedTextChanged FINAL)
+ Q_PROPERTY(QPlatformDialogHelper::StandardButtons buttons READ buttons WRITE setButtons NOTIFY buttonsChanged FINAL)
+ QML_EXTENDED_NAMESPACE(QPlatformDialogHelper)
+ QML_NAMED_ELEMENT(MessageDialog)
+ QML_ADDED_IN_VERSION(6, 3)
+
+public:
+ explicit QQuickMessageDialog(QObject *parent = nullptr);
+
+ QString text() const;
+ void setText(const QString &text);
+
+ QString informativeText() const;
+ void setInformativeText(const QString &text);
+
+ QString detailedText() const;
+ void setDetailedText(const QString &text);
+
+ QPlatformDialogHelper::StandardButtons buttons() const;
+ void setButtons(QPlatformDialogHelper::StandardButtons buttons);
+
+Q_SIGNALS:
+ void textChanged();
+ void informativeTextChanged();
+ void detailedTextChanged();
+ void buttonsChanged();
+
+ void buttonClicked(QPlatformDialogHelper::StandardButton button,
+ QPlatformDialogHelper::ButtonRole role);
+
+private Q_SLOTS:
+ void handleClick(QPlatformDialogHelper::StandardButton button,
+ QPlatformDialogHelper::ButtonRole role);
+
+protected:
+ void onCreate(QPlatformDialogHelper *dialog) override;
+ void onShow(QPlatformDialogHelper *dialog) override;
+
+private:
+ QSharedPointer<QMessageDialogOptions> m_options;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickMessageDialog)
+
+#endif // QQUICKMESSAGEDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogs/qtquickdialogs2foreign.cpp b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign.cpp
new file mode 100644
index 0000000000..78e7dc7ffd
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtquickdialogs2foreign_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+#include "moc_qtquickdialogs2foreign_p.cpp"
diff --git a/src/quickdialogs/quickdialogs/qtquickdialogs2foreign_p.h b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign_p.h
new file mode 100644
index 0000000000..34df201b51
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign_p.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 QQuickFileNameFilterQuickDialogs2Foreign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QQuickFileNameFilter)
+ QML_ADDED_IN_VERSION(6, 2)
+};
+
+QT_END_NAMESPACE
+
+#endif // QTQUICKDIALOGS2FOREIGN_P_H
diff --git a/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h b/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h
new file mode 100644
index 0000000000..85f9ce6dc4
--- /dev/null
+++ b/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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>
+#include <QtQuickDialogs2/private/qtquickdialogs2exports_p.h>
+
+#endif // QTQUICKDIALOGS2GLOBAL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt b/src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt
new file mode 100644
index 0000000000..c4ccceef39
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/CMakeLists.txt
@@ -0,0 +1,215 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## QuickDialogs2QuickImpl Module:
+#####################################################################
+
+set(qml_files
+ "qml/ColorDialog.qml"
+ "qml/ColorInputs.qml"
+ "qml/FileDialog.qml"
+ "qml/FileDialogDelegate.qml"
+ "qml/FileDialogDelegateLabel.qml"
+ "qml/FolderBreadcrumbBar.qml"
+ "qml/FolderDialog.qml"
+ "qml/FolderDialogDelegate.qml"
+ "qml/FolderDialogDelegateLabel.qml"
+ "qml/FontDialog.qml"
+ "qml/FontDialogContent.qml"
+ "qml/HueGradient.qml"
+ "qml/MessageDialog.qml"
+ "qml/PickerHandle.qml"
+ "qml/SaturationLightnessPicker.qml"
+ "qml/+Fusion/ColorDialog.qml"
+ "qml/+Fusion/FileDialog.qml"
+ "qml/+Fusion/FileDialogDelegate.qml"
+ "qml/+Fusion/FolderBreadcrumbBar.qml"
+ "qml/+Fusion/FolderDialog.qml"
+ "qml/+Fusion/FolderDialogDelegate.qml"
+ "qml/+Fusion/FontDialog.qml"
+ "qml/+Fusion/MessageDialog.qml"
+ "qml/+Imagine/ColorDialog.qml"
+ "qml/+Imagine/FileDialog.qml"
+ "qml/+Imagine/FileDialogDelegate.qml"
+ "qml/+Imagine/FolderBreadcrumbBar.qml"
+ "qml/+Imagine/FolderDialog.qml"
+ "qml/+Imagine/FolderDialogDelegate.qml"
+ "qml/+Imagine/FontDialog.qml"
+ "qml/+Imagine/MessageDialog.qml"
+ "qml/+Material/ColorDialog.qml"
+ "qml/+Material/FileDialog.qml"
+ "qml/+Material/FileDialogDelegate.qml"
+ "qml/+Material/FolderBreadcrumbBar.qml"
+ "qml/+Material/FolderDialog.qml"
+ "qml/+Material/FolderDialogDelegate.qml"
+ "qml/+Material/FontDialog.qml"
+ "qml/+Material/MessageDialog.qml"
+ "qml/+Universal/ColorDialog.qml"
+ "qml/+Universal/FileDialog.qml"
+ "qml/+Universal/FileDialogDelegate.qml"
+ "qml/+Universal/FolderBreadcrumbBar.qml"
+ "qml/+Universal/FolderDialog.qml"
+ "qml/+Universal/FolderDialogDelegate.qml"
+ "qml/+Universal/FontDialog.qml"
+ "qml/+Universal/MessageDialog.qml"
+)
+
+qt_internal_add_qml_module(QuickDialogs2QuickImpl
+ URI "QtQuick.Dialogs.quickimpl"
+ VERSION "${PROJECT_VERSION}"
+ CLASS_NAME QtQuickDialogs2QuickImplPlugin
+ PLUGIN_TARGET qtquickdialogs2quickimplplugin
+ DEPENDENCIES
+ QtQuick/auto
+ QtQuick.Templates/auto
+ SOURCES
+ qquickabstractcolorpicker.cpp
+ qquickabstractcolorpicker_p.h
+ qquickabstractcolorpicker_p_p.h
+ qquickcolordialogimpl.cpp
+ qquickcolordialogimpl_p.h
+ qquickcolordialogimpl_p_p.h
+ qquickcolordialogutils_p.h
+ qquickcolordialogutils.cpp
+ qquickcolorinputs.cpp
+ qquickcolorinputs_p.h
+ 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
+ qquickfolderdialogimpl.cpp
+ qquickfolderdialogimpl_p.h
+ qquickfolderdialogimpl_p_p.h
+ qquickfontdialogimpl.cpp
+ qquickfontdialogimpl_p.h
+ qquickfontdialogimpl_p_p.h
+ qquickmessagedialogimpl.cpp
+ qquickmessagedialogimpl_p.h
+ qquickmessagedialogimpl_p_p.h
+ qquickplatformcolordialog.cpp
+ qquickplatformcolordialog_p.h
+ qquickplatformfiledialog.cpp
+ qquickplatformfiledialog_p.h
+ qquickplatformfolderdialog.cpp
+ qquickplatformfolderdialog_p.h
+ qquickplatformfontdialog.cpp
+ qquickplatformfontdialog_p.h
+ qquickplatformmessagedialog.cpp
+ qquickplatformmessagedialog_p.h
+ qquicksaturationlightnesspicker.cpp
+ qquicksaturationlightnesspicker_p.h
+ qtquickdialogs2quickimplforeign.cpp
+ qtquickdialogs2quickimplforeign_p.h
+ qtquickdialogs2quickimplglobal_p.h
+ QML_FILES
+ ${qml_files}
+ 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::QuickTemplates2
+ Qt::QuickTemplates2Private
+ Qt::QuickControls2ImplPrivate
+ Qt::QuickDialogs2Utils
+ Qt::QuickDialogs2UtilsPrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+ GENERATE_CPP_EXPORTS
+ GENERATE_PRIVATE_CPP_EXPORTS
+)
+
+# Resources:
+set(qtquickdialogs2quickimpl_resource_files
+ "images/checkers.png"
+ "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/eye-dropper.png"
+ "images/eye-dropper@2x.png"
+ "images/eye-dropper@3x.png"
+ "images/eye-dropper@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-disabled.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-focused.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-highlighted.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-pressed.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.9.png"
+ "images/imagine/filedialogdelegate-background@2x.9.png"
+ "images/imagine/filedialogdelegate-background@3x.9.png"
+ "images/imagine/filedialogdelegate-background@4x.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"
+)
+
+qt_internal_add_resource(QuickDialogs2QuickImpl "QuickDialogs2QuickImpl"
+ PREFIX
+ "/qt-project.org/imports/QtQuick/Dialogs/quickimpl"
+ FILES
+ ${qtquickdialogs2quickimpl_resource_files}
+)
+
+qt_internal_add_shaders(QuickDialogs2QuickImpl "QuickDialogs2QuickImplShaders"
+ PREFIX
+ "/qt-project.org/imports/QtQuick/Dialogs/quickimpl"
+ FILES
+ "shaders/SaturationLightness.frag"
+ SILENT
+)
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/checkers.png b/src/quickdialogs/quickdialogsquickimpl/images/checkers.png
new file mode 100644
index 0000000000..55ed3f6cf2
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/checkers.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.png
new file mode 100644
index 0000000000..aaa5a3e431
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.svg b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.svg
new file mode 100644
index 0000000000..013cedefa3
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@2x.png
new file mode 100644
index 0000000000..3f66fe172b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@3x.png
new file mode 100644
index 0000000000..24d9f6a64e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@4x.png
new file mode 100644
index 0000000000..1dc83ef09f
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.png
new file mode 100644
index 0000000000..1f7ac63e7f
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.svg b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.svg
new file mode 100644
index 0000000000..9663673470
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@2x.png
new file mode 100644
index 0000000000..524872c1e9
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@3x.png
new file mode 100644
index 0000000000..33bce847c8
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@4x.png
new file mode 100644
index 0000000000..185c3eb7ae
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.png b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.png
new file mode 100644
index 0000000000..6aefbc8f59
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.svg b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.svg
new file mode 100644
index 0000000000..dced09c5d0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="18"
+ height="18"
+ viewBox="0 0 4.7624998 4.7625001"
+ version="1.1"
+ id="svg5"
+ inkscape:version="1.1.2 (b8e25be8, 2022-02-05)"
+ sodipodi:docname="eye-dropper.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">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:document-units="px"
+ showgrid="false"
+ units="px"
+ inkscape:zoom="12.718197"
+ inkscape:cx="-2.5160799"
+ inkscape:cy="14.074322"
+ inkscape:window-width="1512"
+ inkscape:window-height="916"
+ inkscape:window-x="0"
+ inkscape:window-y="38"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ width="18px" />
+ <defs
+ id="defs2" />
+ <g
+ inkscape:label="eye-dropper"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ id="rect265"
+ style="fill:#757575;fill-opacity:1;stroke-width:0.241258"
+ d="M 3.3004382,0.47019911 C 3.5367132,0.2339241 3.9171409,0.2339241 4.1534172,0.4702004 L 4.3207095,0.63749266 C 4.5569858,0.87376897 4.5569858,1.2541967 4.3207108,1.4904717 3.9309107,1.9103182 3.2426583,2.7134558 2.728634,2.2295681 1.9922154,1.5310491 2.7688987,1.0017386 3.3004382,0.47019911 Z M 1.8379088,1.3388429 2.5031344,0.67361724 4.1172798,2.2877626 3.4520541,2.9529882 Z"
+ sodipodi:nodetypes="csscccccccc" />
+ <path
+ id="path1065"
+ style="fill:#757575;fill-opacity:1;stroke:none;stroke-width:0.241258px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 2.3254393,1.4174845 0.66455899,3.0778481 V 3.7248372 L 0.27078465,4.1186115 C 0.23255447,4.40003 0.37351045,4.4889779 0.61805027,4.4658772 L 0.98236908,4.1015583 H 1.723409 L 3.409611,2.4148396 Z M 2.4246581,1.9270141 2.8556396,2.3435262 1.5745809,3.6251017 1.1399822,3.6158 1.1322307,3.2194418 Z" />
+ <rect
+ style="fill:#000000;fill-opacity:0.00218338;stroke-width:0.264583"
+ id="rect165"
+ width="4.7624998"
+ height="4.7624998"
+ x="0"
+ y="0"
+ sodipodi:insensitive="true" />
+ </g>
+</svg>
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@2x.png
new file mode 100644
index 0000000000..69a2dac2cc
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@3x.png
new file mode 100644
index 0000000000..2cf32fd0cb
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@4x.png
new file mode 100644
index 0000000000..2a6ed16458
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.png
new file mode 100644
index 0000000000..c2a4928daf
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.svg b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.svg
new file mode 100644
index 0000000000..7ac6a23b5c
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/file-icon-round@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@2x.png
new file mode 100644
index 0000000000..86af70d1cf
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@3x.png
new file mode 100644
index 0000000000..06fea29ed0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@4x.png
new file mode 100644
index 0000000000..09f8787314
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.png
new file mode 100644
index 0000000000..9e8f3ddd8f
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.svg b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.svg
new file mode 100644
index 0000000000..107afa385e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/file-icon-square@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@2x.png
new file mode 100644
index 0000000000..8d33067744
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@3x.png
new file mode 100644
index 0000000000..2987d2caed
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@4x.png
new file mode 100644
index 0000000000..69d76176e9
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.png
new file mode 100644
index 0000000000..60bc6c72c1
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.svg b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.svg
new file mode 100644
index 0000000000..ba6b627644
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@2x.png
new file mode 100644
index 0000000000..e28734374f
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@3x.png
new file mode 100644
index 0000000000..5ee70a955e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@4x.png
new file mode 100644
index 0000000000..47d28a4b75
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.png
new file mode 100644
index 0000000000..b84a90aecb
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.svg b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.svg
new file mode 100644
index 0000000000..0002b5ac8e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@2x.png
new file mode 100644
index 0000000000..9bf320bbf0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@3x.png
new file mode 100644
index 0000000000..9f12edb6ee
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@4x.png
new file mode 100644
index 0000000000..9ec5b389bd
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled.9.png
new file mode 100644
index 0000000000..ce48ee7422
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.png
new file mode 100644
index 0000000000..c7abb65c3f
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.png
new file mode 100644
index 0000000000..46b84d7da4
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.png
new file mode 100644
index 0000000000..f4dfd338f9
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused.9.png
new file mode 100644
index 0000000000..39fa866442
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.png
new file mode 100644
index 0000000000..6b61562c14
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.png
new file mode 100644
index 0000000000..e46c0bf1d9
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.png
new file mode 100644
index 0000000000..010444e8e1
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png
new file mode 100644
index 0000000000..6f565e8be2
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.png
new file mode 100644
index 0000000000..9fd0a434bb
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.png
new file mode 100644
index 0000000000..62b7435b84
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.png
new file mode 100644
index 0000000000..96444b8905
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed.9.png
new file mode 100644
index 0000000000..39fa866442
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.png
new file mode 100644
index 0000000000..6b61562c14
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.png
new file mode 100644
index 0000000000..e46c0bf1d9
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.png
new file mode 100644
index 0000000000..010444e8e1
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.9.png
new file mode 100644
index 0000000000..cef1bafab3
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.svg b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.svg
new file mode 100644
index 0000000000..acecfcc9ac
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@2x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@2x.9.png
new file mode 100644
index 0000000000..5a136a0ca9
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@2x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@3x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@3x.9.png
new file mode 100644
index 0000000000..f47a366b7b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@3x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@4x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@4x.9.png
new file mode 100644
index 0000000000..9ecb680f20
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@4x.9.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.png
new file mode 100644
index 0000000000..a4b7bc0383
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.svg b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.svg
new file mode 100644
index 0000000000..fd05395798
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/up-icon-round@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@2x.png
new file mode 100644
index 0000000000..25ee970a17
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@3x.png
new file mode 100644
index 0000000000..486f2f46a9
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@4x.png
new file mode 100644
index 0000000000..585402531c
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.png
new file mode 100644
index 0000000000..f7d4151a5a
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.svg b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.svg
new file mode 100644
index 0000000000..93fc45bae4
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/up-icon-square@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@2x.png
new file mode 100644
index 0000000000..2e2a3d94eb
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@3x.png
new file mode 100644
index 0000000000..8965148c90
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@4x.png
new file mode 100644
index 0000000000..0a56bbcc0a
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.png
new file mode 100644
index 0000000000..3f9f87624a
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.svg b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.svg
new file mode 100644
index 0000000000..dd20777be7
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/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/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@2x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@2x.png
new file mode 100644
index 0000000000..b4efe13228
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@2x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@3x.png
new file mode 100644
index 0000000000..2e8302b1f0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@4x.png
new file mode 100644
index 0000000000..5cf05ab305
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@4x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml
new file mode 100644
index 0000000000..591ac46a84
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/ColorDialog.qml
@@ -0,0 +1,256 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+ColorDialogImpl {
+ 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
+
+ isHsl: true
+
+ standardButtons: T.Dialog.Ok | T.Dialog.Cancel
+
+ ColorDialogImpl.eyeDropperButton: eyeDropperButton
+ ColorDialogImpl.buttonBox: buttonBox
+ ColorDialogImpl.colorPicker: colorPicker
+ ColorDialogImpl.colorInputs: inputs
+ ColorDialogImpl.alphaSlider: alphaSlider
+
+ background: Rectangle {
+ implicitWidth: 200
+ 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: RowLayout {
+ Label {
+ objectName: "titleLabel"
+ text: control.title
+ elide: Label.ElideRight
+ font.bold: true
+ padding: 6
+
+ Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ Layout.leftMargin: 12
+ Layout.alignment: Qt.AlignLeft
+ }
+ Button {
+ id: eyeDropperButton
+ objectName: "eyeDropperButton"
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
+ flat: true
+ visible: false
+
+ Layout.preferredWidth: implicitHeight
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 6
+ }
+ }
+
+ contentItem: ColumnLayout {
+ spacing: 12
+ SaturationLightnessPicker {
+ id: colorPicker
+ objectName: "colorPicker"
+ implicitHeight: width
+ color: control.color
+
+ Layout.fillWidth: true
+ }
+
+ Slider {
+ id: hueSlider
+ objectName: "hueSlider"
+ orientation: Qt.Horizontal
+ value: control.hue
+ implicitHeight: 20
+ onMoved: function() { control.hue = value; }
+ handle: PickerHandle {
+ x: hueSlider.leftPadding + (hueSlider.horizontal
+ ? hueSlider.visualPosition * (hueSlider.availableWidth - width)
+ : (hueSlider.availableWidth - width) / 2)
+ y: hueSlider.topPadding + (hueSlider.horizontal
+ ? (hueSlider.availableHeight - height) / 2
+ : hueSlider.visualPosition * (hueSlider.availableHeight - height))
+ picker: hueSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: hueSlider.handle.width / 2
+ anchors.rightMargin: hueSlider.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: HueGradient {
+ orientation: Gradient.Horizontal
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ Slider {
+ id: alphaSlider
+ objectName: "alphaSlider"
+ orientation: Qt.Horizontal
+ value: control.alpha
+ implicitHeight: 20
+ handle: PickerHandle {
+ x: alphaSlider.leftPadding + (alphaSlider.horizontal
+ ? alphaSlider.visualPosition * (alphaSlider.availableWidth - width)
+ : (alphaSlider.availableWidth - width) / 2)
+ y: alphaSlider.topPadding + (alphaSlider.horizontal
+ ? (alphaSlider.availableHeight - height) / 2
+ : alphaSlider.visualPosition * (alphaSlider.availableHeight - height))
+ picker: alphaSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: parent.handle.width / 2
+ anchors.rightMargin: parent.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+
+ Image {
+ anchors.fill: alphaSliderGradient
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ id: alphaSliderGradient
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop {
+ position: 0
+ color: "transparent"
+ }
+ GradientStop {
+ position: 1
+ color: Qt.rgba(control.color.r, control.color.g, control.color.b, 1)
+ }
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ ColorInputs {
+ id: inputs
+
+ color: control.color
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.bottomMargin: 12
+ }
+ }
+
+ footer: RowLayout {
+ spacing: 12
+
+ Label {
+ text: qsTr("Color")
+
+ Layout.leftMargin: 12
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+ }
+
+ Rectangle {
+ implicitWidth: (parent.height - 24) * 2
+ implicitHeight: implicitWidth / 2
+ color: "transparent"
+
+ Image {
+ anchors.fill: parent
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: control.color
+ }
+
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 6
+ horizontalPadding: 0
+ verticalPadding: 0
+
+ Layout.rightMargin: 12
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+ }
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: Fusion.topShadow
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: Fusion.topShadow
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
new file mode 100644
index 0000000000..a928a4b0cc
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
@@ -0,0 +1,194 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+ FileDialogImpl.fileNameLabel: fileNameLabel
+ FileDialogImpl.fileNameTextField: fileNameTextField
+
+ 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
+ dialog: 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: PlatformTheme.themeHint(PlatformTheme.ShowDirectoriesFirst)
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ x: 1
+ width: ListView.view.width - 2
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: nameFiltersComboBox
+ }
+ }
+ }
+
+ footer: GridLayout {
+ columnSpacing: 12
+ columns: 3
+
+ Label {
+ id: fileNameLabel
+ text: qsTr("File name")
+ Layout.leftMargin: 12
+ visible: false
+ }
+
+ TextField {
+ id: fileNameTextField
+ objectName: "fileNameTextField"
+ text: control.fileName
+ visible: false
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Filter")
+ Layout.column: 0
+ Layout.row: 1
+ Layout.leftMargin: 12
+ Layout.bottomMargin: 12
+ }
+
+
+ ComboBox {
+ // OK to use IDs here, since users shouldn't be overriding this stuff.
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ Layout.fillWidth: true
+ Layout.bottomMargin: 12
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 6
+ horizontalPadding: 0
+ verticalPadding: 0
+ background: null
+
+ // TODO: make the orientation vertical
+ Layout.row: 1
+ Layout.column: 2
+ Layout.columnSpan: 1
+ Layout.rightMargin: 12
+ Layout.bottomMargin: 12
+ }
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: Fusion.topShadow
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: Fusion.topShadow
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml
new file mode 100644
index 0000000000..475528d2ec
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml
@@ -0,0 +1,55 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderBreadcrumbBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderBreadcrumbBar.qml
new file mode 100644
index 0000000000..75f1963368
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderBreadcrumbBar.qml
@@ -0,0 +1,74 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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 {
+ 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.dialog as DialogsQuickImpl.FileDialogImpl)?.selectedFile
+ ?? (control.dialog as DialogsQuickImpl.FolderDialogImpl).currentFolder
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialog.qml
new file mode 100644
index 0000000000..fbe0fa9c19
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialog.qml
@@ -0,0 +1,136 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+FolderDialogImpl {
+ 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
+
+ FolderDialogImpl.folderDialogListView: folderDialogListView
+ FolderDialogImpl.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
+ dialog: control
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+
+ KeyNavigation.tab: folderDialogListView
+ }
+ }
+
+ contentItem: Frame {
+ padding: 0
+ verticalPadding: 1
+
+ ListView {
+ id: folderDialogListView
+ objectName: "fileDialogListView"
+ anchors.fill: parent
+ clip: true
+ focus: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ showFiles: false
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FolderDialogDelegate {
+ objectName: "folderDialogDelegate" + index
+ x: 1
+ width: ListView.view.width - 2
+ highlighted: ListView.isCurrentItem
+ dialog: control
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: control.footer
+ }
+ }
+ }
+
+ footer: DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 6
+ leftPadding: 0
+ rightPadding: 12
+ topPadding: 0
+ bottomPadding: 12
+ background: null
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: Fusion.topShadow
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: Fusion.topShadow
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialogDelegate.qml
new file mode 100644
index 0000000000..e18f0de3ec
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FolderDialogDelegate.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/folder-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 date fileModified
+
+ contentItem: FolderDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.highlighted ? Fusion.highlightedText(control.palette) : control.palette.placeholderText
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FontDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FontDialog.qml
new file mode 100644
index 0000000000..cca4e96ee6
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FontDialog.qml
@@ -0,0 +1,118 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Fusion
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+FontDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(control.implicitBackgroundWidth + control.leftInset + control.rightInset,
+ control.contentWidth + control.leftPadding + control.rightPadding,
+ control.implicitHeaderWidth,
+ control.implicitFooterWidth)
+ implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
+ control.contentHeight + control.topPadding + control.bottomPadding
+ + (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.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.Ok | T.Dialog.Cancel
+
+ FontDialogImpl.buttonBox: buttonBox
+ FontDialogImpl.familyListView: content.familyListView
+ FontDialogImpl.styleListView: content.styleListView
+ FontDialogImpl.sizeListView: content.sizeListView
+ FontDialogImpl.sampleEdit: content.sampleEdit
+ FontDialogImpl.writingSystemComboBox: writingSystemComboBox
+ FontDialogImpl.underlineCheckBox: content.underline
+ FontDialogImpl.strikeoutCheckBox: content.strikeout
+ FontDialogImpl.familyEdit: content.familyEdit
+ FontDialogImpl.styleEdit: content.styleEdit
+ FontDialogImpl.sizeEdit: content.sizeEdit
+
+ 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
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: Fusion.topShadow
+ }
+
+ Overlay.modeless: Rectangle {
+ color: Fusion.topShadow
+ }
+
+ header: Label {
+ text: control.title
+ horizontalAlignment: Label.AlignHCenter
+ elide: Label.ElideRight
+ font.bold: true
+ padding: 6
+ }
+
+ contentItem: FontDialogContent {
+ id: content
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 12
+
+ Label {
+ text: qsTr("Writing System")
+
+ Layout.leftMargin: 12
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+ }
+ ComboBox{
+ id: writingSystemComboBox
+
+ Layout.fillWidth: true
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 6
+ horizontalPadding: 0
+ verticalPadding: 0
+ background: null
+
+ Layout.rightMargin: 12
+ Layout.topMargin: 6
+ Layout.bottomMargin: 6
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml
new file mode 100644
index 0000000000..cb3eb1cc64
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/MessageDialog.qml
@@ -0,0 +1,135 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Fusion
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+MessageDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(control.implicitBackgroundWidth + control.leftInset + control.rightInset,
+ control.implicitHeaderWidth,
+ rowLayout.implicitWidth)
+ implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
+ control.contentHeight + control.topPadding + control.bottomPadding
+ + (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.spacing : 0))
+
+ padding: 6
+ horizontalPadding: 12
+
+ MessageDialogImpl.buttonBox: buttonBox
+ MessageDialogImpl.detailedTextButton: detailedTextButton
+
+ background: Rectangle {
+ implicitWidth: 320
+ implicitHeight: 120
+ 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: Label {
+ text: control.title
+ horizontalAlignment: Label.AlignHCenter
+ elide: Label.ElideRight
+ font.bold: true
+ padding: 6
+ }
+
+ contentItem: Column {
+ padding: 6
+ spacing: 24
+
+ Label {
+ id: textLabel
+ objectName: "textLabel"
+ text: control.text
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+ }
+
+ Label {
+ id: informativeTextLabel
+ objectName: "informativeTextLabel"
+ text: control.informativeText
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+ }
+ }
+
+ footer: ColumnLayout {
+ id: columnLayout
+
+ RowLayout {
+ id: rowLayout
+
+ Button {
+ id: detailedTextButton
+ objectName: "detailedTextButton"
+ text: control.showDetailedText ? qsTr("Hide Details...") : qsTr("Show Details...")
+
+ Layout.leftMargin: 12
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ objectName: "buttonBox"
+ spacing: 6
+ horizontalPadding: 0
+ verticalPadding: 12
+
+ Layout.fillWidth: true
+ Layout.leftMargin: detailedTextButton.visible ? 6 : 12
+ Layout.rightMargin: 12
+ }
+ }
+
+ TextArea {
+ id: detailedTextArea
+ objectName: "detailedText"
+ text: control.detailedText
+ visible: control.showDetailedText
+ wrapMode: TextEdit.WordWrap
+ readOnly: true
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.bottomMargin: 12
+
+ background: Rectangle {
+ color: Qt.rgba(1,1,1,1)
+ radius: 3
+ border.color: Qt.darker(control.palette.light)
+ border.width: 1
+ }
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: Fusion.topShadow
+ }
+
+ Overlay.modeless: Rectangle {
+ color: Fusion.topShadow
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml
new file mode 100644
index 0000000000..641453bca5
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/ColorDialog.qml
@@ -0,0 +1,273 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Imagine
+import QtQuick.Controls.Imagine.impl
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+ColorDialogImpl {
+ id: control
+
+ // Can't set implicitWidth of the NinePatchImage background, so we do it here.
+ implicitWidth: Math.max(200,
+ implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ implicitHeaderWidth,
+ implicitFooterWidth)
+ implicitHeight: Math.max(600,
+ 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.Ok | T.Dialog.Cancel
+
+ isHsl: true
+
+ ColorDialogImpl.eyeDropperButton: eyeDropperButton
+ ColorDialogImpl.buttonBox: buttonBox
+ ColorDialogImpl.colorPicker: colorPicker
+ ColorDialogImpl.alphaSlider: alphaSlider
+ ColorDialogImpl.colorInputs: inputs
+
+ background: NinePatchImage {
+ source: Imagine.url + "dialog-background"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": control.modal},
+ {"dim": control.dim}
+ ]
+ }
+ }
+
+ header: RowLayout {
+ Label {
+ text: control.title
+ elide: Label.ElideRight
+ font.bold: true
+ font.pixelSize: 16
+ leftPadding: 16
+ rightPadding: 16
+ topPadding: 16
+ bottomPadding: 16
+
+ Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ }
+
+ Button {
+ id: eyeDropperButton
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
+ flat: true
+ topPadding: 16
+ bottomPadding: 16
+ visible: false
+
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 16
+ }
+ }
+
+ contentItem: ColumnLayout {
+ SaturationLightnessPicker {
+ id: colorPicker
+ objectName: "colorPicker"
+ implicitHeight: width
+ color: control.color
+
+ Layout.fillWidth: true
+ }
+
+ Slider {
+ id: hueSlider
+ objectName: "hueSlider"
+ orientation: Qt.Horizontal
+ value: control.hue
+ onMoved: function() { control.hue = value; }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+
+ handle: PickerHandle {
+ x: hueSlider.leftPadding + (hueSlider.horizontal
+ ? hueSlider.visualPosition * (hueSlider.availableWidth - width)
+ : (hueSlider.availableWidth - width) / 2)
+ y: hueSlider.topPadding + (hueSlider.horizontal
+ ? (hueSlider.availableHeight - height) / 2
+ : hueSlider.visualPosition * (hueSlider.availableHeight - height))
+ picker: hueSlider
+ }
+
+ implicitHeight: 20
+
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: hueSlider.handle.width / 2
+ anchors.rightMargin: hueSlider.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: HueGradient {
+ orientation: Gradient.Horizontal
+ }
+ }
+ }
+ }
+
+ Slider {
+ id: alphaSlider
+ objectName: "alphaSlider"
+ orientation: Qt.Horizontal
+ value: control.alpha
+ implicitHeight: 20
+ handle: PickerHandle {
+ x: alphaSlider.leftPadding + (alphaSlider.horizontal
+ ? alphaSlider.visualPosition * (alphaSlider.availableWidth - width)
+ : (alphaSlider.availableWidth - width) / 2)
+ y: alphaSlider.topPadding + (alphaSlider.horizontal
+ ? (alphaSlider.availableHeight - height) / 2
+ : alphaSlider.visualPosition * (alphaSlider.availableHeight - height))
+ picker: alphaSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: parent.handle.width / 2
+ anchors.rightMargin: parent.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+
+ Image {
+ anchors.fill: alphaSliderGradient
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ id: alphaSliderGradient
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop {
+ position: 0
+ color: "transparent"
+ }
+ GradientStop {
+ position: 1
+ color: Qt.rgba(control.color.r,
+ control.color.g,
+ control.color.b,
+ 1)
+ }
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ }
+
+ ColorInputs {
+ id: inputs
+
+ color: control.color
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.bottomMargin: 16
+ }
+ }
+
+ footer: RowLayout {
+ spacing: 20
+
+ Label {
+ text: qsTr("Color")
+
+ Layout.leftMargin: 16
+ Layout.bottomMargin: 16
+ }
+
+ Rectangle {
+ implicitWidth: 32
+ implicitHeight: 32
+ border.width: 2
+ border.color: control.palette.dark
+ color: "transparent"
+
+ Image {
+ anchors.fill: parent
+ anchors.margins: 4
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ color: control.color
+ }
+
+ Layout.bottomMargin: 16
+ }
+
+ Item {
+ // empty filler
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+
+ Layout.bottomMargin: 16
+ Layout.rightMargin: 16
+ Layout.alignment: Qt.AlignRight
+ }
+ }
+
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
new file mode 100644
index 0000000000..664965e571
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
@@ -0,0 +1,189 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+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
+ FileDialogImpl.fileNameLabel: fileNameLabel
+ FileDialogImpl.fileNameTextField: fileNameTextField
+
+ 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
+ dialog: 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: PlatformTheme.themeHint(PlatformTheme.ShowDirectoriesFirst)
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+ }
+ }
+
+ footer: GridLayout {
+ columnSpacing: 20
+ columns: 3
+
+ Label {
+ id: fileNameLabel
+ text: qsTr("File name")
+ visible: false
+
+ Layout.leftMargin: 16
+ }
+
+ TextField {
+ id: fileNameTextField
+ objectName: "fileNameTextField"
+ text: control.fileName
+ visible: false
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Filter")
+
+ Layout.column: 0
+ Layout.row: 1
+ Layout.leftMargin: 16
+ Layout.bottomMargin: 16
+ }
+
+ ComboBox {
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ Layout.fillWidth: true
+ Layout.bottomMargin: 16
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+
+ Layout.row: 1
+ Layout.column: 2
+ Layout.columnSpan: 1
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml
new file mode 100644
index 0000000000..30094a7d9d
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml
@@ -0,0 +1,67 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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.enabled && control.hovered }
+ ]
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderBreadcrumbBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderBreadcrumbBar.qml
new file mode 100644
index 0000000000..8becba5108
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderBreadcrumbBar.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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 {
+ 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.dialog as DialogsQuickImpl.FileDialogImpl)?.selectedFile
+ ?? (control.dialog as DialogsQuickImpl.FolderDialogImpl).currentFolder
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialog.qml
new file mode 100644
index 0000000000..d0af142afa
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialog.qml
@@ -0,0 +1,141 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Templates as T
+import QtQuick.Controls.impl
+import QtQuick.Controls.Imagine
+import QtQuick.Controls.Imagine.impl
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+import "." as DialogsImpl
+
+FolderDialogImpl {
+ 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
+
+ FolderDialogImpl.folderDialogListView: folderDialogListView
+ FolderDialogImpl.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
+ dialog: control
+
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.fillWidth: true
+ Layout.maximumWidth: parent.width - 28
+ }
+ }
+
+ contentItem: ListView {
+ id: folderDialogListView
+ objectName: "folderDialogListView"
+ clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ showFiles: false
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FolderDialogDelegate {
+ objectName: "folderDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ }
+ }
+
+ footer: DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ leftPadding: 16
+ rightPadding: 16
+ bottomPadding: 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/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialogDelegate.qml
new file mode 100644
index 0000000000..64195c725a
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FolderDialogDelegate.qml
@@ -0,0 +1,61 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/folder-icon-round.png"
+
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property date fileModified
+
+ contentItem: FolderDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: Qt.lighter(control.icon.color)
+ }
+
+ 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.enabled && control.hovered }
+ ]
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FontDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FontDialog.qml
new file mode 100644
index 0000000000..87660b87b0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FontDialog.qml
@@ -0,0 +1,134 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Imagine
+import QtQuick.Controls.Imagine.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+FontDialogImpl {
+ 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.Ok | T.Dialog.Cancel
+
+ FontDialogImpl.buttonBox: buttonBox
+ FontDialogImpl.familyListView: content.familyListView
+ FontDialogImpl.styleListView: content.styleListView
+ FontDialogImpl.sizeListView: content.sizeListView
+ FontDialogImpl.sampleEdit: content.sampleEdit
+ FontDialogImpl.writingSystemComboBox: writingSystemComboBox
+ FontDialogImpl.underlineCheckBox: content.underline
+ FontDialogImpl.strikeoutCheckBox: content.strikeout
+ FontDialogImpl.familyEdit: content.familyEdit
+ FontDialogImpl.styleEdit: content.styleEdit
+ FontDialogImpl.sizeEdit: content.sizeEdit
+
+ background: NinePatchImage {
+ source: Imagine.url + "dialog-background"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": control.modal},
+ {"dim": control.dim}
+ ]
+ }
+ }
+
+ Overlay.modal: NinePatchImage {
+ source: Imagine.url + "dialog-overlay"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": true}
+ ]
+ }
+ }
+
+ Overlay.modeless: NinePatchImage {
+ source: Imagine.url + "dialog-overlay"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": false}
+ ]
+ }
+ }
+
+ header: Label {
+ text: control.title
+ elide: Label.ElideRight
+ font.bold: true
+
+ leftPadding: 16
+ rightPadding: 16
+ topPadding: 12
+ visible: control.title.length > 0
+
+ background: NinePatchImage {
+ width: parent.width
+ height: parent.height
+
+ source: Imagine.url + "dialog-title"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": control.modal},
+ {"dim": control.dim}
+ ]
+ }
+ }
+ }
+
+ contentItem: FontDialogContent {
+ id: content
+ rowSpacing: 16
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 20
+
+ Label {
+ text: qsTr("Writing System")
+ Layout.leftMargin: 16
+ Layout.bottomMargin: 16
+ }
+ ComboBox{
+ id: writingSystemComboBox
+
+ Layout.fillWidth: true
+ Layout.bottomMargin: 16
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ Layout.rightMargin: 16
+ Layout.bottomMargin: 16
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/MessageDialog.qml
new file mode 100644
index 0000000000..9ec3a550da
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/MessageDialog.qml
@@ -0,0 +1,163 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Imagine
+import QtQuick.Controls.Imagine.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+MessageDialogImpl {
+ id: control
+
+ // Can't set implicitWidth of the NinePatchImage background, so we do it here.
+ implicitWidth: Math.max(320,
+ implicitBackgroundWidth + leftInset + rightInset,
+ implicitHeaderWidth,
+ rowLayout.implicitWidth)
+ implicitHeight: Math.max(160,
+ 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
+
+ MessageDialogImpl.buttonBox: buttonBox
+ MessageDialogImpl.detailedTextButton: detailedTextButton
+
+ background: NinePatchImage {
+ source: Imagine.url + "dialog-background"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": control.modal},
+ {"dim": control.dim}
+ ]
+ }
+ }
+
+ header: Label {
+ text: control.title
+ elide: Label.ElideRight
+ font.bold: true
+
+ leftPadding: 16
+ rightPadding: 16
+ topPadding: 12
+ visible: control.title.length > 0
+
+ background: NinePatchImage {
+ width: parent.width
+ height: parent.height
+
+ source: Imagine.url + "dialog-title"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": control.modal},
+ {"dim": control.dim}
+ ]
+ }
+ }
+ }
+
+ contentItem: Column {
+ padding: 8
+ spacing: 16
+
+ Label {
+ id: textLabel
+ objectName: "textLabel"
+ text: control.text
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+ }
+ Label {
+ id: informativeTextLabel
+ objectName: "informativeTextLabel"
+ text: control.informativeText
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+ }
+ }
+
+ footer: ColumnLayout {
+ id: columnLayout
+
+ RowLayout {
+ id: rowLayout
+ spacing: 12
+
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.bottomMargin: 16
+
+ Button {
+ id: detailedTextButton
+ objectName: "detailedTextButton"
+ text: control.showDetailedText ? qsTr("Hide Details...") : qsTr("Show Details...")
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ objectName: "buttonBox"
+ spacing: 12
+ padding: 0
+
+ Layout.fillWidth: true
+ }
+ }
+
+ TextArea {
+ id: detailedTextArea
+ objectName: "detailedText"
+ text: control.detailedText
+ visible: control.showDetailedText
+ wrapMode: TextEdit.WordWrap
+ readOnly: true
+
+ padding: 12
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.bottomMargin: 16
+
+ background: Rectangle {
+ color: Qt.rgba(1,1,1,1)
+ radius: 3
+ border.color: Qt.darker(control.palette.light)
+ border.width: 1
+ }
+ }
+ }
+
+ Overlay.modal: NinePatchImage {
+ source: Imagine.url + "dialog-overlay"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": true}
+ ]
+ }
+ }
+
+ Overlay.modeless: NinePatchImage {
+ source: Imagine.url + "dialog-overlay"
+ NinePatchImageSelector on source {
+ states: [
+ {"modal": false}
+ ]
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml
new file mode 100644
index 0000000000..313e8645b4
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/ColorDialog.qml
@@ -0,0 +1,249 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+ColorDialogImpl {
+ 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: 0
+ rightPadding: 0
+
+ standardButtons: T.Dialog.Ok | T.Dialog.Cancel
+
+ isHsl: true
+
+ ColorDialogImpl.eyeDropperButton: eyeDropperButton
+ ColorDialogImpl.buttonBox: buttonBox
+ ColorDialogImpl.colorPicker: colorPicker
+ ColorDialogImpl.alphaSlider: alphaSlider
+ ColorDialogImpl.colorInputs: inputs
+
+ Material.elevation: 24
+
+ background: Rectangle {
+ implicitWidth: 200
+ implicitHeight: 560
+ radius: 2
+ color: control.Material.dialogColor
+ layer.enabled: control.Material.elevation > 0
+ layer.effect: ElevationEffect {
+ elevation: control.Material.elevation
+ }
+ }
+
+ header: RowLayout {
+ Label {
+ text: control.title
+ elide: Label.ElideRight
+ font.bold: true
+ font.pixelSize: 16
+ leftPadding: 24
+ rightPadding: 24
+ topPadding: 24
+ bottomPadding: 24
+
+ Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ }
+
+ Button {
+ id: eyeDropperButton
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
+ flat: true
+ topPadding: 24
+ bottomPadding: 24
+ visible: false
+
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 24
+ }
+ }
+
+ contentItem: ColumnLayout {
+ spacing: 12
+ SaturationLightnessPicker {
+ id: colorPicker
+ objectName: "colorPicker"
+ implicitHeight: width
+ color: control.color
+
+ Layout.fillWidth: true
+ }
+
+ Slider {
+ id: hueSlider
+ objectName: "hueSlider"
+ orientation: Qt.Horizontal
+ value: control.hue
+ implicitHeight: 20
+ onMoved: function() { control.hue = value; }
+ handle: PickerHandle {
+ x: hueSlider.leftPadding + (hueSlider.horizontal
+ ? hueSlider.visualPosition * (hueSlider.availableWidth - width)
+ : (hueSlider.availableWidth - width) / 2)
+ y: hueSlider.topPadding + (hueSlider.horizontal
+ ? (hueSlider.availableHeight - height) / 2
+ : hueSlider.visualPosition * (hueSlider.availableHeight - height))
+ picker: hueSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: hueSlider.handle.width / 2
+ anchors.rightMargin: hueSlider.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: HueGradient {
+ orientation: Gradient.Horizontal
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ Slider {
+ id: alphaSlider
+ objectName: "alphaSlider"
+ orientation: Qt.Horizontal
+ value: control.alpha
+ implicitHeight: 20
+ handle: PickerHandle {
+ x: alphaSlider.leftPadding + (alphaSlider.horizontal
+ ? alphaSlider.visualPosition * (alphaSlider.availableWidth - width)
+ : (alphaSlider.availableWidth - width) / 2)
+ y: alphaSlider.topPadding + (alphaSlider.horizontal
+ ? (alphaSlider.availableHeight - height) / 2
+ : alphaSlider.visualPosition * (alphaSlider.availableHeight - height))
+ picker: alphaSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: parent.handle.width / 2
+ anchors.rightMargin: parent.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+
+ Image {
+ anchors.fill: alphaSliderGradient
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ id: alphaSliderGradient
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop {
+ position: 0
+ color: "transparent"
+ }
+ GradientStop {
+ position: 1
+ color: Qt.rgba(control.color.r,
+ control.color.g,
+ control.color.b,
+ 1)
+ }
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ ColorInputs {
+ id: inputs
+
+ color: control.color
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.bottomMargin: 12
+ }
+ }
+
+ footer: RowLayout {
+ spacing: 20
+
+ Label {
+ text: qsTr("Color")
+
+ Layout.leftMargin: 20
+ }
+
+ Rectangle {
+ implicitWidth: 32
+ implicitHeight: 32
+ border.width: 2
+ border.color: control.palette.dark
+ color: "transparent"
+
+ Image {
+ anchors.fill: parent
+ anchors.margins: 4
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ color: control.color
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ horizontalPadding: 0
+
+ Layout.rightMargin: 20
+ Layout.alignment: Qt.AlignRight
+ }
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
new file mode 100644
index 0000000000..cd2c513c2e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
@@ -0,0 +1,168 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+ FileDialogImpl.fileNameLabel: fileNameLabel
+ FileDialogImpl.fileNameTextField: fileNameTextField
+
+ 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
+ dialog: 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: PlatformTheme.themeHint(PlatformTheme.ShowDirectoriesFirst)
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+ }
+ }
+
+ footer: GridLayout {
+ columnSpacing: 20
+ columns: 3
+
+ Label {
+ id: fileNameLabel
+ text: qsTr("File name")
+ visible: false
+
+ Layout.topMargin: 12
+ Layout.leftMargin: 20
+ }
+
+ TextField {
+ id: fileNameTextField
+ objectName: "fileNameTextField"
+ text: control.fileName
+ visible: false
+
+ Layout.topMargin: 12
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Filter")
+
+ Layout.row: 1
+ Layout.topMargin: fileNameTextField.visible ? 0 : 12
+ Layout.leftMargin: 20
+ }
+
+ ComboBox {
+ id: nameFiltersComboBox
+ model: control.nameFilters
+ flat: true
+
+ verticalPadding: 0
+ topInset: 0
+ bottomInset: 0
+ Layout.topMargin: fileNameTextField.visible ? 0 : 12
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ padding: 0
+ topInset: 0
+ bottomInset: 0
+
+ Layout.row: 1
+ Layout.column: 2
+ Layout.topMargin: fileNameTextField.visible ? 0 : 12
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml
new file mode 100644
index 0000000000..c96231345d
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderBreadcrumbBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderBreadcrumbBar.qml
new file mode 100644
index 0000000000..f049304407
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderBreadcrumbBar.qml
@@ -0,0 +1,72 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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 {
+ color: control.Material.backgroundColor
+ }
+ contentItem: 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.dialog as DialogsQuickImpl.FileDialogImpl)?.selectedFile
+ ?? (control.dialog as DialogsQuickImpl.FolderDialogImpl).currentFolder
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialog.qml
new file mode 100644
index 0000000000..4e33a067f0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialog.qml
@@ -0,0 +1,113 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+FolderDialogImpl {
+ 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
+
+ FolderDialogImpl.folderDialogListView: folderDialogListView
+ FolderDialogImpl.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
+ dialog: control
+
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ Layout.fillWidth: true
+ Layout.maximumWidth: parent.width - 48
+ }
+ }
+
+ contentItem: ListView {
+ id: folderDialogListView
+ objectName: "folderDialogListView"
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ showFiles: false
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FolderDialogDelegate {
+ objectName: "folderDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ }
+ }
+
+ footer: DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ leftPadding: 20
+ rightPadding: 20
+ verticalPadding: 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/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialogDelegate.qml
new file mode 100644
index 0000000000..8b3e6af33e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FolderDialogDelegate.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/folder-icon-square.png"
+
+ file: fileUrl
+
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property date fileModified
+
+ contentItem: FolderDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.Material.hintTextColor
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Material/FontDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FontDialog.qml
new file mode 100644
index 0000000000..2322a6542b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FontDialog.qml
@@ -0,0 +1,109 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+FontDialogImpl {
+ 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.Ok | T.Dialog.Cancel
+
+ Material.elevation: 24
+
+ FontDialogImpl.buttonBox: buttonBox
+ FontDialogImpl.familyListView: content.familyListView
+ FontDialogImpl.styleListView: content.styleListView
+ FontDialogImpl.sizeListView: content.sizeListView
+ FontDialogImpl.sampleEdit: content.sampleEdit
+ FontDialogImpl.writingSystemComboBox: writingSystemComboBox
+ FontDialogImpl.underlineCheckBox: content.underline
+ FontDialogImpl.strikeoutCheckBox: content.strikeout
+ FontDialogImpl.familyEdit: content.familyEdit
+ FontDialogImpl.styleEdit: content.styleEdit
+ FontDialogImpl.sizeEdit: content.sizeEdit
+
+ 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
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.5)
+ }
+
+ Overlay.modeless: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.12)
+ }
+
+ header: Label {
+ text: control.title
+ visible: control.title.length > 0
+ elide: Label.ElideRight
+ font.bold: true
+ font.pixelSize: 16
+
+ leftPadding: 24
+ rightPadding: 24
+ topPadding: 24
+ bottomPadding: 24
+ }
+
+ contentItem: FontDialogContent {
+ id: content
+ familyEdit.bottomPadding: 8
+ styleEdit.bottomPadding: 8
+ sizeEdit.bottomPadding: 8
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 20
+
+ Label {
+ text: qsTr("Writing System")
+
+ Layout.leftMargin: 20
+ }
+ ComboBox{
+ id: writingSystemComboBox
+
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ horizontalPadding: 0
+ verticalPadding: 20
+
+ Layout.rightMargin: 20
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml
new file mode 100644
index 0000000000..a678503d6c
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/MessageDialog.qml
@@ -0,0 +1,135 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+import QtQuick.Controls.Material.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+MessageDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ contentWidth + leftPadding + rightPadding,
+ rowLayout.implicitWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ leftPadding: 24
+ rightPadding: 24
+
+ Material.elevation: 24
+
+ MessageDialogImpl.buttonBox: buttonBox
+ MessageDialogImpl.detailedTextButton: detailedTextButton
+
+ background: Rectangle {
+ implicitWidth: 320
+ implicitHeight: 160
+ radius: 2
+ color: control.Material.dialogColor
+
+ layer.enabled: control.Material.elevation > 0
+ layer.effect: ElevationEffect {
+ elevation: control.Material.elevation
+ }
+ }
+
+ header: Label {
+ text: control.title
+ visible: control.title.length > 0
+ elide: Label.ElideRight
+ font.bold: true
+ font.pixelSize: 16
+
+ leftPadding: 24
+ rightPadding: 24
+ topPadding: 24
+ bottomPadding: 24
+ }
+
+ contentItem: Column {
+ spacing: 24
+
+ Label {
+ id: textLabel
+ objectName: "textLabel"
+ text: control.text
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+ }
+
+ Label {
+ id: informativeTextLabel
+ objectName: "informativeTextLabel"
+ text: control.informativeText
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+ }
+ }
+
+ footer: ColumnLayout {
+ id: columnLayout
+
+ RowLayout {
+ id: rowLayout
+
+ Button {
+ id: detailedTextButton
+ objectName: "detailedTextButton"
+ text: control.showDetailedText ? qsTr("Hide Details...") : qsTr("Show Details...")
+
+ Layout.leftMargin: 20
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ objectName: "buttonBox"
+ spacing: 12
+ horizontalPadding: 0
+ verticalPadding: 20
+
+ Layout.fillWidth: true
+ Layout.leftMargin: detailedTextButton.visible ? 12 : 20
+ Layout.rightMargin: 20
+ }
+ }
+
+ TextArea {
+ id: detailedTextArea
+ objectName: "detailedText"
+ text: control.detailedText
+ visible: control.showDetailedText
+ wrapMode: TextEdit.WordWrap
+ readOnly: true
+ padding: 12
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 20
+ Layout.rightMargin: 20
+ Layout.bottomMargin: 20
+
+ background: Rectangle {
+ color: Qt.rgba(1,1,1,1)
+ radius: 3
+ border.color: Qt.darker(control.palette.light)
+ border.width: 1
+ }
+ }
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml
new file mode 100644
index 0000000000..d13b751652
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/ColorDialog.qml
@@ -0,0 +1,257 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Universal
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+ColorDialogImpl {
+ 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.Ok | T.Dialog.Cancel
+
+ isHsl: true
+
+ ColorDialogImpl.eyeDropperButton: eyeDropperButton
+ ColorDialogImpl.buttonBox: buttonBox
+ ColorDialogImpl.colorPicker: colorPicker
+ ColorDialogImpl.alphaSlider: alphaSlider
+ ColorDialogImpl.colorInputs: inputs
+
+ background: Rectangle {
+ implicitWidth: 200
+ implicitHeight: 600
+ color: control.Universal.chromeMediumLowColor
+ border.color: control.Universal.chromeHighColor
+ border.width: 1 // FlyoutBorderThemeThickness
+ }
+
+ header: RowLayout {
+ spacing: 12
+
+ Label {
+ text: control.title
+ elide: Label.ElideRight
+ // TODO: QPlatformTheme::TitleBarFont
+ font.pixelSize: 20
+ background: Rectangle {
+ x: 1; y: 1 // // FlyoutBorderThemeThickness
+ color: control.Universal.chromeMediumLowColor
+ width: parent.width - 2
+ height: parent.height - 1
+ }
+
+ Layout.topMargin: 24
+ Layout.bottomMargin: 24
+ Layout.leftMargin: 18
+ Layout.fillWidth: true
+ Layout.preferredWidth: control.title.length > 0 ? implicitHeight : 0
+ }
+
+ Button {
+ id: eyeDropperButton
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
+ flat: true
+ topPadding: 24
+ bottomPadding: 24
+ visible: false
+
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 18
+ }
+ }
+
+ contentItem: ColumnLayout {
+ spacing: 12
+ SaturationLightnessPicker {
+ id: colorPicker
+ objectName: "colorPicker"
+ implicitHeight: width
+ color: control.color
+
+ Layout.fillWidth: true
+ }
+
+ Slider {
+ id: hueSlider
+ objectName: "hueSlider"
+ orientation: Qt.Horizontal
+ value: control.hue
+ implicitHeight: 20
+ onMoved: function() { control.hue = value; }
+ handle: PickerHandle {
+ x: hueSlider.leftPadding + (hueSlider.horizontal
+ ? hueSlider.visualPosition * (hueSlider.availableWidth - width)
+ : (hueSlider.availableWidth - width) / 2)
+ y: hueSlider.topPadding + (hueSlider.horizontal
+ ? (hueSlider.availableHeight - height) / 2
+ : hueSlider.visualPosition * (hueSlider.availableHeight - height))
+ picker: hueSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: hueSlider.handle.width / 2
+ anchors.rightMargin: hueSlider.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: HueGradient {
+ orientation: Gradient.Horizontal
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ Slider {
+ id: alphaSlider
+ objectName: "alphaSlider"
+ orientation: Qt.Horizontal
+ value: control.alpha
+ implicitHeight: 20
+ handle: PickerHandle {
+ x: alphaSlider.leftPadding + (alphaSlider.horizontal
+ ? alphaSlider.visualPosition * (alphaSlider.availableWidth - width)
+ : (alphaSlider.availableWidth - width) / 2)
+ y: alphaSlider.topPadding + (alphaSlider.horizontal
+ ? (alphaSlider.availableHeight - height) / 2
+ : alphaSlider.visualPosition * (alphaSlider.availableHeight - height))
+ picker: alphaSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: parent.handle.width / 2
+ anchors.rightMargin: parent.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+
+ Image {
+ anchors.fill: alphaSliderGradient
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ id: alphaSliderGradient
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop {
+ position: 0
+ color: "transparent"
+ }
+ GradientStop {
+ position: 1
+ color: Qt.rgba(control.color.r,
+ control.color.g,
+ control.color.b,
+ 1)
+ }
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ ColorInputs {
+ id: inputs
+
+ color: control.color
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.bottomMargin: 12
+ }
+ }
+
+ footer: RowLayout {
+ spacing: 24
+
+ Label {
+ text: qsTr("Color")
+
+ Layout.topMargin: 6
+ Layout.leftMargin: 24
+ Layout.bottomMargin: 24
+ }
+
+ Rectangle {
+ implicitWidth: 56
+ implicitHeight: 36
+ border.width: 2
+ border.color: control.palette.dark
+ color: "transparent"
+
+ Image {
+ anchors.fill: parent
+ anchors.margins: 6
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 6
+ color: control.color
+ }
+
+ Layout.topMargin: 6
+ Layout.bottomMargin: 24
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ horizontalPadding: 0
+
+ Layout.rightMargin: 24
+ Layout.alignment: Qt.AlignRight
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+
+ Overlay.modeless: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
new file mode 100644
index 0000000000..c029b06293
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
@@ -0,0 +1,161 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+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
+ FileDialogImpl.fileNameLabel: fileNameLabel
+ FileDialogImpl.fileNameTextField: fileNameTextField
+
+ 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
+ dialog: 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: PlatformTheme.themeHint(PlatformTheme.ShowDirectoriesFirst)
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+ }
+ }
+
+ footer: GridLayout {
+ columnSpacing: 24
+ columns: 3
+
+ Label {
+ id: fileNameLabel
+ text: qsTr("File name")
+ visible: false
+
+ Layout.leftMargin: 24
+ }
+
+ TextField {
+ id: fileNameTextField
+ objectName: "fileNameTextField"
+ text: control.fileName
+ visible: false
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Filter")
+
+ Layout.row: 1
+ Layout.column: 0
+ Layout.leftMargin: 24
+ Layout.bottomMargin: 24
+ }
+
+ ComboBox {
+ id: nameFiltersComboBox
+ model: control.nameFilters
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml
new file mode 100644
index 0000000000..7650ec7a34
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml
@@ -0,0 +1,60 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderBreadcrumbBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderBreadcrumbBar.qml
new file mode 100644
index 0000000000..4e730c7d00
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderBreadcrumbBar.qml
@@ -0,0 +1,70 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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 {
+ color: control.Universal.background
+ }
+ contentItem: 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.dialog as DialogsQuickImpl.FileDialogImpl)?.selectedFile
+ ?? (control.dialog as DialogsQuickImpl.FolderDialogImpl).currentFolder
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialog.qml
new file mode 100644
index 0000000000..9ae0a50d21
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialog.qml
@@ -0,0 +1,119 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import Qt.labs.folderlistmodel
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Universal
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+import "." as DialogsImpl
+
+FolderDialogImpl {
+ 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
+
+ FolderDialogImpl.folderDialogListView: folderDialogListView
+ FolderDialogImpl.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 {
+ // FlyoutBorderThemeThickness
+ x: 1
+ y: 1
+ color: control.Universal.chromeMediumLowColor
+ width: parent.width - 2
+ height: parent.height - 1
+ }
+ }
+
+ DialogsImpl.FolderBreadcrumbBar {
+ id: breadcrumbBar
+ dialog: control
+
+ Layout.leftMargin: 24
+ Layout.rightMargin: 24
+ Layout.preferredWidth: 400
+ Layout.fillWidth: true
+ }
+ }
+
+ contentItem: ListView {
+ id: folderDialogListView
+ objectName: "folderDialogListView"
+ clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ showFiles: false
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FolderDialogDelegate {
+ objectName: "folderDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ }
+ }
+
+ footer: DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ leftPadding: 24
+ rightPadding: 24
+ topPadding: 6
+ bottomPadding: 24
+ alignment: Qt.AlignRight
+ }
+
+ T.Overlay.modal: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+
+ T.Overlay.modeless: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialogDelegate.qml
new file mode 100644
index 0000000000..97da6d739e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FolderDialogDelegate.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/folder-icon-square.png"
+
+ file: fileUrl
+
+ required property int index
+ required property string fileName
+ required property url fileUrl
+ required property date fileModified
+
+ contentItem: FolderDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: control.Universal.baseMediumColor
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/+Universal/FontDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FontDialog.qml
new file mode 100644
index 0000000000..aa9f38c5ee
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FontDialog.qml
@@ -0,0 +1,111 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Controls.Universal
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+FontDialogImpl {
+ 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.Ok | T.Dialog.Cancel
+
+ FontDialogImpl.buttonBox: buttonBox
+ FontDialogImpl.familyListView: content.familyListView
+ FontDialogImpl.styleListView: content.styleListView
+ FontDialogImpl.sizeListView: content.sizeListView
+ FontDialogImpl.sampleEdit: content.sampleEdit
+ FontDialogImpl.writingSystemComboBox: writingSystemComboBox
+ FontDialogImpl.underlineCheckBox: content.underline
+ FontDialogImpl.strikeoutCheckBox: content.strikeout
+ FontDialogImpl.familyEdit: content.familyEdit
+ FontDialogImpl.styleEdit: content.styleEdit
+ FontDialogImpl.sizeEdit: content.sizeEdit
+
+ background: Rectangle {
+ implicitWidth: 600
+ implicitHeight: 400
+ color: control.Universal.chromeMediumLowColor
+ border.color: control.Universal.chromeHighColor
+ border.width: 1 // FlyoutBorderThemeThickness
+ }
+
+ header: Label {
+ text: control.title
+ elide: Label.ElideRight
+ // TODO: QPlatformTheme::TitleBarFont
+ font.pixelSize: 20
+
+ leftPadding: 24
+ rightPadding: 24
+ topPadding: 18
+ height: 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
+ }
+ }
+
+ contentItem: FontDialogContent {
+ id: content
+ rowSpacing: 12
+ }
+
+ footer: RowLayout {
+ id: rowLayout
+ spacing: 24
+
+ Label {
+ text: qsTr("Writing System")
+
+ Layout.leftMargin: 24
+ Layout.topMargin: 6
+ Layout.bottomMargin: 24
+ }
+ ComboBox{
+ id: writingSystemComboBox
+
+ Layout.fillWidth: true
+ Layout.topMargin: 6
+ Layout.bottomMargin: 24
+
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ spacing: 12
+ horizontalPadding: 0
+
+ Layout.rightMargin: 24
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+
+ Overlay.modeless: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml
new file mode 100644
index 0000000000..ac63d281d1
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/MessageDialog.qml
@@ -0,0 +1,133 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Universal
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+MessageDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitHeaderWidth,
+ rowLayout.implicitWidth)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding
+ + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+
+ padding: 24
+ verticalPadding: 18
+
+ MessageDialogImpl.buttonBox: buttonBox
+ MessageDialogImpl.detailedTextButton: detailedTextButton
+
+ background: Rectangle {
+ implicitWidth: 320
+ implicitHeight: 160
+ color: control.Universal.chromeMediumLowColor
+ border.color: control.Universal.chromeHighColor
+ border.width: 1 // FlyoutBorderThemeThickness
+ }
+
+ header: Label {
+ text: control.title
+ elide: Label.ElideRight
+ // TODO: QPlatformTheme::TitleBarFont
+ font.pixelSize: 20
+ visible: control.title.length > 0
+
+ leftPadding: 24
+ rightPadding: 24
+ topPadding: 18
+
+ background: Rectangle {
+ x: 1; y: 1 // // FlyoutBorderThemeThickness
+ color: control.Universal.chromeMediumLowColor
+ width: parent.width - 2
+ height: parent.height - 1
+ }
+ }
+
+ contentItem: Column {
+ spacing: 24
+
+ Label {
+ id: textLabel
+ objectName: "textLabel"
+ text: control.text
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width
+ }
+
+ Label {
+ id: informativeTextLabel
+ objectName: "informativeTextLabel"
+ text: control.informativeText
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width
+ }
+ }
+
+ footer: ColumnLayout {
+ id: columnLayout
+
+ RowLayout {
+ id: rowLayout
+ spacing: 12
+
+ Layout.margins: 20
+
+ Button {
+ id: detailedTextButton
+ objectName: "detailedTextButton"
+ text: control.showDetailedText ? qsTr("Hide Details...") : qsTr("Show Details...")
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ objectName: "buttonBox"
+ spacing: 12
+ horizontalPadding: 0
+ topPadding: 0
+ bottomPadding: 0
+
+ Layout.fillWidth: true
+ }
+ }
+
+ TextArea {
+ id: detailedTextArea
+ objectName: "detailedText"
+ text: control.detailedText
+ visible: control.showDetailedText
+ wrapMode: TextEdit.WordWrap
+ readOnly: true
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 20
+ Layout.rightMargin: 20
+ Layout.bottomMargin: 20
+
+ background: Rectangle {
+ color: Qt.rgba(1,1,1,1)
+ radius: 3
+ border.color: Qt.darker(control.palette.light)
+ border.width: 1
+ }
+ }
+ }
+
+ Overlay.modal: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+
+ Overlay.modeless: Rectangle {
+ color: control.Universal.baseLowColor
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml
new file mode 100644
index 0000000000..c8395bacd0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/ColorDialog.qml
@@ -0,0 +1,261 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+ColorDialogImpl {
+ 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: 6
+ rightPadding: 6
+
+ // Ensure that the background's border is visible.
+ leftInset: -1
+ rightInset: -1
+ topInset: -1
+ bottomInset: -1
+
+ standardButtons: T.Dialog.Ok | T.Dialog.Cancel
+
+ isHsl: true
+
+ ColorDialogImpl.eyeDropperButton: eyeDropperButton
+ ColorDialogImpl.buttonBox: buttonBox
+ ColorDialogImpl.colorPicker: colorPicker
+ ColorDialogImpl.colorInputs: inputs
+ ColorDialogImpl.alphaSlider: alphaSlider
+
+ background: Rectangle {
+ implicitWidth: 200
+ implicitHeight: 600
+ color: control.palette.window
+ border.color: control.palette.dark
+ }
+
+ header: Pane {
+ palette.window: control.palette.light
+ padding: 20
+
+ contentItem: RowLayout {
+ Label {
+ objectName: "titleLabel"
+ text: control.title
+ elide: Label.ElideRight
+ font.bold: true
+
+ Layout.preferredWidth: control.title.length > 0 ? implicitWidth : 0
+ Layout.leftMargin: 12
+ Layout.alignment: Qt.AlignLeft
+ }
+ Button {
+ id: eyeDropperButton
+ objectName: "eyeDropperButton"
+ icon.source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/eye-dropper.png"
+ flat: true
+ visible: false
+
+ Layout.preferredWidth: implicitHeight
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 6
+ }
+ }
+ }
+
+ contentItem: ColumnLayout {
+ spacing: 12
+ SaturationLightnessPicker {
+ id: colorPicker
+ objectName: "colorPicker"
+ implicitHeight: width
+ color: control.color
+
+ Layout.fillWidth: true
+ }
+
+ Slider {
+ id: hueSlider
+ objectName: "hueSlider"
+ orientation: Qt.Horizontal
+ value: control.hue
+ implicitHeight: 20
+ onMoved: function() { control.hue = value; }
+ handle: PickerHandle {
+ x: hueSlider.leftPadding + (hueSlider.horizontal
+ ? hueSlider.visualPosition * (hueSlider.availableWidth - width)
+ : (hueSlider.availableWidth - width) / 2)
+ y: hueSlider.topPadding + (hueSlider.horizontal
+ ? (hueSlider.availableHeight - height) / 2
+ : hueSlider.visualPosition * (hueSlider.availableHeight - height))
+ picker: hueSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: hueSlider.handle.width / 2
+ anchors.rightMargin: hueSlider.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: HueGradient {
+ orientation: Gradient.Horizontal
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ Slider {
+ id: alphaSlider
+ objectName: "alphaSlider"
+ orientation: Qt.Horizontal
+ value: control.alpha
+ implicitHeight: 20
+ handle: PickerHandle {
+ x: alphaSlider.leftPadding + (alphaSlider.horizontal
+ ? alphaSlider.visualPosition * (alphaSlider.availableWidth - width)
+ : (alphaSlider.availableWidth - width) / 2)
+ y: alphaSlider.topPadding + (alphaSlider.horizontal
+ ? (alphaSlider.availableHeight - height) / 2
+ : alphaSlider.visualPosition * (alphaSlider.availableHeight - height))
+ picker: alphaSlider
+ }
+ background: Rectangle {
+ anchors.fill: parent
+ anchors.leftMargin: parent.handle.width / 2
+ anchors.rightMargin: parent.handle.width / 2
+ border.width: 2
+ border.color: control.palette.dark
+ radius: 10
+ color: "transparent"
+
+ Image {
+ anchors.fill: alphaSliderGradient
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ id: alphaSliderGradient
+ anchors.fill: parent
+ anchors.margins: 4
+ radius: 10
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop {
+ position: 0
+ color: "transparent"
+ }
+ GradientStop {
+ position: 1
+ color: Qt.rgba(control.color.r,
+ control.color.g,
+ control.color.b,
+ 1)
+ }
+ }
+ }
+ }
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ }
+
+ ColorInputs {
+ id: inputs
+
+ color: control.color
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+ Layout.bottomMargin: 12
+ }
+ }
+
+ footer: Rectangle {
+ color: control.palette.light
+ implicitWidth: rowLayout.implicitWidth
+ implicitHeight: rowLayout.implicitHeight
+
+ RowLayout {
+ id: rowLayout
+ width: parent.width
+ height: parent.height
+ spacing: 20
+
+ Label {
+ text: qsTr("Color")
+
+ Layout.leftMargin: 20
+ }
+
+ Rectangle {
+ implicitWidth: 32
+ implicitHeight: 32
+ border.width: 2
+ border.color: control.palette.dark
+ color: "transparent"
+
+ Image {
+ anchors.fill: parent
+ anchors.margins: 4
+ source: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/images/checkers.png"
+ fillMode: Image.Tile
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 4
+ color: control.color
+ }
+ }
+
+ Item {
+ // empty space filler
+ 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/quickdialogs/quickdialogsquickimpl/qml/ColorInputs.qml b/src/quickdialogs/quickdialogsquickimpl/qml/ColorInputs.qml
new file mode 100644
index 0000000000..8fc0e0df07
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/ColorInputs.qml
@@ -0,0 +1,260 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Dialogs.quickimpl
+
+ColorInputsImpl {
+ id: root
+
+ hexInput: hex
+ redInput: rgbRed
+ greenInput: rgbGreen
+ blueInput: rgbBlue
+ rgbAlphaInput: rgbAlpha
+ hsvHueInput: hsvHue
+ hsvSaturationInput: hsvSaturation
+ valueInput: hsvValue
+ hsvAlphaInput: hsvAlpha
+ hslHueInput: hslHue
+ hslSaturationInput: hslSaturation
+ lightnessInput: hslLightness
+ hslAlphaInput: hslAlpha
+
+ implicitWidth: content.implicitWidth
+ implicitHeight: content.implicitHeight
+
+ TextMetrics {
+ id: fourM
+ text: "MMMM"
+ font: colorSystemComboBox.font
+ }
+
+ RowLayout {
+ id: content
+ anchors.fill: parent
+ spacing: 12
+
+ ComboBox {
+ id: colorSystemComboBox
+ objectName: "colorSystemComboBox"
+ editable: false
+ flat: true
+ background.implicitWidth: 0
+ implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
+ implicitWidth: implicitContentWidth + leftPadding + rightPadding // Workaround QTBUG-106098
+ model: ListModel {
+ ListElement {
+ name: qsTr("Hex")
+ }
+ ListElement {
+ name: qsTr("RGB")
+ }
+ ListElement {
+ name: qsTr("HSV")
+ }
+ ListElement {
+ name: qsTr("HSL")
+ }
+ }
+ }
+
+ StackLayout {
+ objectName: "colorParameters"
+ currentIndex: colorSystemComboBox.currentIndex
+
+ Layout.fillWidth: true
+
+ TextField {
+ id: hex
+ horizontalAlignment: Qt.AlignLeft
+ text: root.color
+ maximumLength: 9
+ validator: RegularExpressionValidator {
+ regularExpression: root.showAlpha ? /^#[0-9A-f]{6}(?:[0-9A-f]{2})?$/ : /^#[0-9A-f]{6}$/
+ }
+ Layout.fillWidth: true
+ }
+
+ // TODO: QTBUG-106246
+ // Using RowLayout as the root type should work here, but doesn't:
+ // when ShowAlphaChannel is true, switching from hex to rgba causes a
+ // jump in the StackLayout's implicitWidth.
+ Item {
+ implicitWidth: rgbRed.implicitWidth + rgbGreen.implicitWidth + rgbBlue.implicitWidth + rgbAlpha.implicitWidth
+ implicitHeight: Math.max(rgbRed.implicitHeight, rgbGreen.implicitHeight, rgbBlue.implicitHeight, rgbAlpha.implicitHeight)
+
+ RowLayout {
+ width: parent.implicitWidth
+ TextField {
+ id: rgbRed
+ objectName: "rgbRed"
+ horizontalAlignment: Qt.AlignHCenter
+ text: root.red
+ maximumLength: 3
+ validator: IntValidator {
+ bottom: 0
+ top: 999
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: rgbGreen
+ objectName: "rgbGreen"
+ horizontalAlignment: Qt.AlignHCenter
+ text: root.green
+ maximumLength: 3
+ validator: IntValidator {
+ bottom: 0
+ top: 999
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: rgbBlue
+ objectName: "rgbBlue"
+ horizontalAlignment: Qt.AlignHCenter
+ text: root.blue
+ maximumLength: 3
+ validator: IntValidator {
+ bottom: 0
+ top: 999
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: rgbAlpha
+ objectName: "rgbAlpha"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.alpha * 100).toString() + "%"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}%?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ }
+ }
+
+ Item {
+ implicitWidth: hsvHue.implicitWidth + hsvSaturation.implicitWidth + hsvValue.implicitWidth + hsvAlpha.implicitWidth
+ implicitHeight: Math.max(hsvHue.implicitHeight, hsvSaturation.implicitHeight, hsvValue.implicitHeight, hsvAlpha.implicitHeight)
+
+ RowLayout {
+ width: parent.implicitWidth
+ TextField {
+ id: hsvHue
+ objectName: "hsvHue"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.hue * 360).toString() + "°"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}°?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: hsvSaturation
+ objectName: "hsvSaturation"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.hsvSaturation * 100).toString() + "%"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}%?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: hsvValue
+ objectName: "hsvValue"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.value * 100).toString() + "%"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}%?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: hsvAlpha
+ objectName: "hsvAlpha"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.alpha * 100).toString() + "%"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}%?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ }
+ }
+ Item {
+ implicitWidth: hslHue.implicitWidth + hsvSaturation.implicitWidth + hslLightness.implicitWidth + hslAlpha.implicitWidth
+ implicitHeight: Math.max(hslHue.implicitHeight, hsvSaturation.implicitHeight, hslLightness.implicitHeight, hslAlpha.implicitHeight)
+
+ RowLayout {
+ width: parent.implicitWidth
+
+ TextField {
+ id: hslHue
+ objectName: "hslHue"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.hue * 360).toString() + "°"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}°?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: hslSaturation
+ objectName: "hslSaturation"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.hslSaturation * 100).toString() + "%"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}%?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: hslLightness
+ objectName: "hslLightness"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.lightness * 100).toString() + "%"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}%?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ TextField {
+ id: hslAlpha
+ objectName: "hslAlpha"
+ horizontalAlignment: Qt.AlignHCenter
+ text: Math.round(root.alpha * 100).toString() + "%"
+ maximumLength: 4
+ validator: RegularExpressionValidator {
+ regularExpression: /^[0-9]{0,3}%?$/
+ }
+ implicitWidth: fourM.width + leftPadding + rightPadding
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
new file mode 100644
index 0000000000..0f25dee35b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
@@ -0,0 +1,181 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+ FileDialogImpl.fileNameLabel: fileNameLabel
+ FileDialogImpl.fileNameTextField: fileNameTextField
+
+ 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
+ dialog: 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: PlatformTheme.themeHint(PlatformTheme.ShowDirectoriesFirst)
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FileDialogDelegate {
+ objectName: "fileDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+ fileDetailRowWidth: nameFiltersComboBox.width
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: nameFiltersComboBox
+ }
+ }
+
+ footer: Rectangle {
+ color: control.palette.light
+ implicitWidth: gridLayout.implicitWidth
+ implicitHeight: gridLayout.implicitHeight + 12
+
+ GridLayout {
+ // OK to use IDs here, since users shouldn't be overriding this stuff.
+ id: gridLayout
+ anchors.fill: parent
+ anchors.topMargin: 6
+ anchors.bottomMargin: 6
+ columnSpacing: 20
+ columns: 3
+
+ Label {
+ id: fileNameLabel
+ text: qsTr("File name")
+ visible: false
+
+ Layout.leftMargin: 20
+ }
+
+ TextField {
+ id: fileNameTextField
+ objectName: "fileNameTextField"
+ text: control.fileName
+ visible: false
+
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Filter")
+
+ Layout.row: 1
+ Layout.column: 0
+ Layout.leftMargin: 20
+ }
+
+ ComboBox {
+ id: nameFiltersComboBox
+ model: control.nameFilters
+ verticalPadding: 0
+
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ palette.window: control.palette.light
+ spacing: 12
+ padding: 0
+
+ Layout.row: 1
+ Layout.column: 2
+ 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/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml
new file mode 100644
index 0000000000..3abe35f43e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml
new file mode 100644
index 0000000000..ac543e735d
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegateLabel.qml
@@ -0,0 +1,65 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/quickdialogs/quickdialogsquickimpl/qml/FolderBreadcrumbBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FolderBreadcrumbBar.qml
new file mode 100644
index 0000000000..1ee10dadad
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FolderBreadcrumbBar.qml
@@ -0,0 +1,69 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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 {
+ 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.dialog as DialogsQuickImpl.FileDialogImpl)?.selectedFile
+ ?? (control.dialog as DialogsQuickImpl.FolderDialogImpl).currentFolder
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialog.qml
new file mode 100644
index 0000000000..7df617584d
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialog.qml
@@ -0,0 +1,114 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+FolderDialogImpl {
+ 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
+
+ FolderDialogImpl.folderDialogListView: folderDialogListView
+ FolderDialogImpl.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
+ dialog: control
+
+ KeyNavigation.tab: folderDialogListView
+ }
+ }
+ }
+
+ contentItem: ListView {
+ id: folderDialogListView
+ objectName: "folderDialogListView"
+ clip: true
+ focus: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: FolderListModel {
+ folder: control.currentFolder
+ showFiles: false
+ sortCaseSensitive: false
+ }
+ delegate: DialogsImpl.FolderDialogDelegate {
+ objectName: "folderDialogDelegate" + index
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ dialog: control
+
+ KeyNavigation.backtab: breadcrumbBar
+ KeyNavigation.tab: control.footer
+ }
+ }
+
+ footer: DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ palette.window: control.palette.light
+ spacing: 12
+ alignment: Qt.AlignRight
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml
new file mode 100644
index 0000000000..750a581a5d
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegate.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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/folder-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 date fileModified
+
+ contentItem: FolderDialogDelegateLabel {
+ delegate: control
+ fileDetailRowTextColor: Qt.lighter(control.icon.color)
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegateLabel.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegateLabel.qml
new file mode 100644
index 0000000000..ada6fdd632
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FolderDialogDelegateLabel.qml
@@ -0,0 +1,52 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+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
+
+ 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
+ }
+ }
+
+ Label {
+ x: iconImage.width + root.delegate.spacing
+ text: Qt.formatDateTime(root.delegate.fileModified)
+ font.pixelSize: root.delegate.font.pixelSize * 0.75
+ color: root.fileDetailRowTextColor
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FontDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FontDialog.qml
new file mode 100644
index 0000000000..2643b88e3b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FontDialog.qml
@@ -0,0 +1,115 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+import QtQuick.Templates as T
+
+FontDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(control.implicitBackgroundWidth + control.leftInset + control.rightInset,
+ control.contentWidth + control.leftPadding + control.rightPadding,
+ control.implicitHeaderWidth,
+ control.implicitFooterWidth)
+ implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
+ control.contentHeight + control.topPadding + control.bottomPadding
+ + (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.spacing : 0))
+
+ leftPadding: 20
+ rightPadding: 20
+ // Ensure that the background's border is visible.
+ leftInset: -1
+ rightInset: -1
+ topInset: -1
+ bottomInset: -1
+
+ spacing: 12
+
+ standardButtons: T.Dialog.Ok | T.Dialog.Cancel
+
+ FontDialogImpl.buttonBox: buttonBox
+ FontDialogImpl.familyListView: content.familyListView
+ FontDialogImpl.styleListView: content.styleListView
+ FontDialogImpl.sizeListView: content.sizeListView
+ FontDialogImpl.sampleEdit: content.sampleEdit
+ FontDialogImpl.writingSystemComboBox: writingSystemComboBox
+ FontDialogImpl.underlineCheckBox: content.underline
+ FontDialogImpl.strikeoutCheckBox: content.strikeout
+ FontDialogImpl.familyEdit: content.familyEdit
+ FontDialogImpl.styleEdit: content.styleEdit
+ FontDialogImpl.sizeEdit: content.sizeEdit
+
+ background: Rectangle {
+ implicitWidth: 600
+ implicitHeight: 400
+ color: control.palette.window
+ border.color: control.palette.dark
+ }
+
+ Overlay.modal: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.5)
+ }
+
+ Overlay.modeless: Rectangle {
+ color: Color.transparent(control.palette.shadow, 0.12)
+ }
+
+ header: Pane {
+ palette.window: control.palette.light
+ padding: 20
+
+ contentItem: Label {
+ width: parent.width
+ text: control.title
+ visible: control.title.length > 0
+ horizontalAlignment: Label.AlignHCenter
+ elide: Label.ElideRight
+ font.bold: true
+ }
+ }
+
+ contentItem: FontDialogContent {
+ id: content
+ }
+
+ footer: Rectangle {
+ color: control.palette.light
+ implicitWidth: rowLayout.implicitWidth
+ implicitHeight: rowLayout.implicitHeight
+
+ RowLayout {
+ id: rowLayout
+ width: parent.width
+ height: parent.height
+ spacing: 20
+
+ Label {
+ text: qsTr("Writing System")
+
+ Layout.leftMargin: 20
+ }
+ ComboBox{
+ id: writingSystemComboBox
+
+ Layout.fillWidth: true
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ standardButtons: control.standardButtons
+ palette.window: control.palette.light
+ spacing: 12
+ horizontalPadding: 0
+ verticalPadding: 20
+
+ Layout.rightMargin: 20
+ }
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FontDialogContent.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FontDialogContent.qml
new file mode 100644
index 0000000000..a2b4ae043e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FontDialogContent.qml
@@ -0,0 +1,234 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+GridLayout {
+ property alias familyListView: fontFamilyListView
+ property alias styleListView: fontStyleListView
+ property alias sizeListView: fontSizeListView
+ property alias sampleEdit: fontSample
+ property alias underline: fontUnderline
+ property alias strikeout: fontStrikeout
+ property alias familyEdit: fontFamilyEdit
+ property alias styleEdit: fontStyleEdit
+ property alias sizeEdit: fontSizeEdit
+
+ columns: 3
+
+ ColumnLayout {
+ spacing: 0
+
+ Layout.preferredWidth: 50
+
+ Label {
+ text: qsTr("Family")
+ Layout.alignment: Qt.AlignLeft
+ }
+ TextField {
+ id: fontFamilyEdit
+ objectName: "familyEdit"
+ readOnly: true
+ Layout.fillWidth: true
+ focus: true
+ }
+ Frame {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ background: Rectangle {
+ color: "white"
+ }
+ ListView {
+ id: fontFamilyListView
+ objectName: "familyListView"
+ implicitHeight: 200
+ anchors.fill: parent
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+ }
+
+ boundsBehavior: Flickable.StopAtBounds
+
+ highlightMoveVelocity: -1
+ highlightMoveDuration: 1
+ highlightFollowsCurrentItem: true
+ keyNavigationEnabled: true
+
+ delegate: ItemDelegate {
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ onClicked: () => fontFamilyListView.currentIndex = index
+ text: modelData
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ spacing: 0
+
+ Layout.preferredWidth: 30
+
+ Label {
+ text: qsTr("Style")
+ Layout.alignment: Qt.AlignLeft
+ }
+ TextField {
+ id: fontStyleEdit
+ objectName: "styleEdit"
+ readOnly: true
+ Layout.fillWidth: true
+ }
+ Frame {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ background: Rectangle {
+ color: "white"
+ }
+ ListView {
+ id: fontStyleListView
+ objectName: "styleListView"
+ implicitHeight: 200
+ anchors.fill: parent
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+ boundsBehavior: Flickable.StopAtBounds
+
+ highlightMoveVelocity: -1
+ highlightMoveDuration: 1
+ highlightFollowsCurrentItem: true
+ keyNavigationEnabled: true
+
+ delegate: ItemDelegate {
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ onClicked: () => fontStyleListView.currentIndex = index
+ text: modelData
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ spacing: 0
+
+ Layout.preferredWidth: 20
+
+ Label {
+ text: qsTr("Size")
+ Layout.alignment: Qt.AlignLeft
+ }
+ TextField {
+ id: fontSizeEdit
+ objectName: "sizeEdit"
+ Layout.fillWidth: true
+ validator: IntValidator {
+ bottom: 1
+ top: 512
+ }
+ }
+ Frame {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ background: Rectangle {
+ color: "white"
+ }
+ ListView {
+ id: fontSizeListView
+ objectName: "sizeListView"
+ implicitHeight: 200
+ anchors.fill: parent
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+ }
+
+ boundsBehavior: Flickable.StopAtBounds
+
+ highlightMoveVelocity: -1
+ highlightMoveDuration: 1
+ highlightFollowsCurrentItem: true
+ keyNavigationEnabled: true
+
+ delegate: ItemDelegate {
+ width: ListView.view.width
+ highlighted: ListView.isCurrentItem
+ onClicked: () => fontSizeListView.currentIndex = index
+ text: modelData
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ Layout.preferredWidth: 80
+
+ GroupBox {
+ id: effectsGroupBox
+ title: qsTr("Effects")
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ label: Label {
+ anchors.left: effectsGroupBox.left
+ text: parent.title
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ CheckBox {
+ id: fontUnderline
+ objectName: "underlineEffect"
+ text: qsTr("Underline")
+ }
+ CheckBox{
+ id: fontStrikeout
+ objectName: "strikeoutEffect"
+ text: qsTr("Strikeout")
+ }
+ }
+ }
+ }
+
+ GroupBox {
+ id: sample
+ padding: label.implicitHeight
+ title: qsTr("Sample")
+
+ Layout.fillWidth: true
+ Layout.preferredWidth: 80
+ Layout.fillHeight: true
+ Layout.columnSpan: 2
+ clip: true
+
+ background: Rectangle {
+ y: sample.topPadding - sample.bottomPadding
+ width: sample.width - sample.leftPadding + sample.rightPadding
+ height: sample.height - sample.topPadding + sample.bottomPadding
+ radius: 3
+ }
+
+ label: Label {
+ anchors.left: sample.left
+ text: sample.title
+ }
+
+ TextEdit {
+ id: fontSample
+ objectName: "sampleEdit"
+ anchors.centerIn: parent
+ readOnly: true
+ }
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/HueGradient.qml b/src/quickdialogs/quickdialogsquickimpl/qml/HueGradient.qml
new file mode 100644
index 0000000000..a32a336bad
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/HueGradient.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+
+Gradient {
+ GradientStop {
+ position: 0
+ color: "#ff0000"
+ }
+ GradientStop {
+ position: 0.166666
+ color: "#ffff00"
+ }
+ GradientStop {
+ position: 0.333333
+ color: "#00ff00"
+ }
+ GradientStop {
+ position: 0.5
+ color: "#00ffff"
+ }
+ GradientStop {
+ position: 0.666666
+ color: "#0000ff"
+ }
+ GradientStop {
+ position: 0.833333
+ color: "#ff00ff"
+ }
+ GradientStop {
+ position: 1
+ color: "#ff0000"
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/MessageDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/MessageDialog.qml
new file mode 100644
index 0000000000..5dc7ee7873
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/MessageDialog.qml
@@ -0,0 +1,136 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+import QtQuick.Layouts
+
+MessageDialogImpl {
+ id: control
+
+ implicitWidth: Math.max(control.implicitBackgroundWidth + control.leftInset + control.rightInset,
+ control.implicitHeaderWidth,
+ rowLayout.implicitWidth)
+ implicitHeight: Math.max(control.implicitBackgroundHeight + control.topInset + control.bottomInset,
+ control.contentHeight + control.topPadding + control.bottomPadding
+ + (control.implicitHeaderHeight > 0 ? control.implicitHeaderHeight + control.spacing : 0)
+ + (control.implicitFooterHeight > 0 ? control.implicitFooterHeight + control.spacing : 0))
+ leftPadding: 20
+ rightPadding: 20
+
+ // Ensure that the background's border is visible.
+ leftInset: -1
+ rightInset: -1
+ topInset: -1
+ bottomInset: -1
+
+ spacing: 16
+
+ MessageDialogImpl.buttonBox: buttonBox
+ MessageDialogImpl.detailedTextButton: detailedTextButton
+
+ background: Rectangle {
+ implicitWidth: 320
+ implicitHeight: 160
+ color: control.palette.window
+ border.color: control.palette.dark
+ }
+
+ header: Pane {
+ palette.window: control.palette.light
+ padding: 20
+
+ contentItem: Label {
+ width: parent.width
+ text: control.title
+ visible: control.title.length > 0
+ horizontalAlignment: Label.AlignHCenter
+ elide: Label.ElideRight
+ font.bold: true
+ }
+ }
+
+ contentItem: Column {
+ padding: 10
+ spacing: 16
+
+ Label {
+ id: textLabel
+ objectName: "textLabel"
+ text: control.text
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+
+ }
+
+ Label {
+ id: informativeTextLabel
+ objectName: "informativeTextLabel"
+ text: control.informativeText
+ visible: text.length > 0
+ wrapMode: Text.Wrap
+ width: parent.width - parent.leftPadding - parent.rightPadding
+ }
+ }
+
+ footer: ColumnLayout {
+ id: columnLayout
+
+ RowLayout {
+ id: rowLayout
+ spacing: 12
+
+ Layout.leftMargin: 20
+ Layout.rightMargin: 20
+ Layout.bottomMargin: 20
+
+ Button {
+ id: detailedTextButton
+ objectName: "detailedTextButton"
+ text: control.showDetailedText ? qsTr("Hide Details...") : qsTr("Show Details...")
+ padding: 0
+ }
+
+ DialogButtonBox {
+ id: buttonBox
+ objectName: "buttonBox"
+ spacing: 12
+ padding: 0
+
+ Layout.fillWidth: true
+ }
+ }
+
+ TextArea {
+ id: detailedTextArea
+ objectName: "detailedText"
+ text: control.detailedText
+ visible: control.showDetailedText
+ wrapMode: TextEdit.WordWrap
+ readOnly: true
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 20
+ Layout.rightMargin: 20
+ Layout.bottomMargin: 20
+
+ background: Rectangle {
+ color: Qt.rgba(1,1,1,1)
+ radius: 3
+ border.color: Qt.darker(control.palette.light)
+ border.width: 1
+ }
+ }
+ }
+
+ 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/quickdialogs/quickdialogsquickimpl/qml/PickerHandle.qml b/src/quickdialogs/quickdialogsquickimpl/qml/PickerHandle.qml
new file mode 100644
index 0000000000..cdea430c40
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/PickerHandle.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Templates as T
+
+Rectangle {
+ id: root
+ implicitWidth: 16
+ implicitHeight: 16
+ radius: 8
+ color: "transparent"
+ border.color: picker.visualFocus ? "#0066ff" : (picker.pressed ? "#36383a" : "#454647")
+ border.width: 1
+
+ required property T.Control picker
+
+ property alias handleColor: circle.color
+
+ Rectangle {
+ id: circle
+ x: 1
+ y: 1
+ width: 14
+ height: 14
+ radius: 7
+ color: "transparent"
+ border.color: root.picker.visualFocus ? "#0066ff" : (root.picker.pressed ? "#86888a" : "#959697")
+ border.width: 1
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/SaturationLightnessPicker.qml b/src/quickdialogs/quickdialogsquickimpl/qml/SaturationLightnessPicker.qml
new file mode 100644
index 0000000000..6f4316a659
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/SaturationLightnessPicker.qml
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Dialogs
+import QtQuick.Dialogs.quickimpl
+
+SaturationLightnessPickerImpl {
+ id: control
+
+ implicitWidth: Math.max(background ? background.implicitWidth : 0, contentItem.implicitWidth)
+ implicitHeight: Math.max(background ? background.implicitHeight : 0, contentItem.implicitHeight)
+
+ background: Rectangle {
+ anchors.fill: parent
+ color: control.visualFocus ? (control.pressed ? "#cce0ff" : "#f0f6ff") : (control.pressed ? "#d6d6d6" : "#f6f6f6")
+ border.color: "#353637"
+ }
+
+ contentItem: ShaderEffect {
+ scale: contentItem.width / width
+ layer.enabled: true
+ layer.smooth: true
+ anchors.fill: parent
+
+ property alias hue: control.hue
+
+ fragmentShader: "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/shaders/SaturationLightness.frag.qsb"
+ }
+
+ handle: PickerHandle {
+ x: control.leftPadding + control.lightness * control.availableWidth - width / 2
+ y: control.topPadding + (1.0 - control.saturation) * control.availableHeight - height / 2
+ picker: control
+ handleColor: control.color
+ z: 1
+ }
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
new file mode 100644
index 0000000000..4606c00502
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp
@@ -0,0 +1,359 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickabstractcolorpicker_p_p.h"
+
+#include "qquickcolordialogutils_p.h"
+
+#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
+#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
+
+#include <qpa/qplatformintegration.h>
+#include <private/qguiapplication_p.h>
+
+QQuickAbstractColorPickerPrivate::QQuickAbstractColorPickerPrivate() = default;
+
+static inline QString handleName()
+{
+ return QStringLiteral("handle");
+}
+
+bool QQuickAbstractColorPickerPrivate::handlePress(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickAbstractColorPicker);
+ QQuickControlPrivate::handlePress(point, timestamp);
+ m_pressPoint = point;
+ q->setPressed(true);
+ q->updateColor(point);
+ return true;
+}
+
+bool QQuickAbstractColorPickerPrivate::handleMove(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickAbstractColorPicker);
+ QQuickControlPrivate::handleMove(point, timestamp);
+ if (point != m_pressPoint)
+ q->updateColor(point);
+ return true;
+}
+
+bool QQuickAbstractColorPickerPrivate::handleRelease(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickAbstractColorPicker);
+ QQuickControlPrivate::handleRelease(point, timestamp);
+ m_pressPoint = QPointF();
+ q->setKeepMouseGrab(false);
+ q->setKeepTouchGrab(false);
+ q->setPressed(false);
+ q->updateColor(point);
+ return true;
+}
+
+void QQuickAbstractColorPickerPrivate::handleUngrab()
+{
+ Q_Q(QQuickAbstractColorPicker);
+ QQuickControlPrivate::handleUngrab();
+ m_pressPoint = QPointF();
+ q->setPressed(false);
+}
+
+void QQuickAbstractColorPickerPrivate::cancelHandle()
+{
+ Q_Q(QQuickAbstractColorPicker);
+ quickCancelDeferred(q, handleName());
+}
+
+void QQuickAbstractColorPickerPrivate::executeHandle(bool complete)
+{
+ Q_Q(QQuickAbstractColorPicker);
+ if (m_handle.wasExecuted())
+ return;
+
+ if (!m_handle || complete)
+ quickBeginDeferred(q, handleName(), m_handle);
+ if (complete)
+ quickCompleteDeferred(q, handleName(), m_handle);
+}
+
+void QQuickAbstractColorPickerPrivate::itemImplicitWidthChanged(QQuickItem *item)
+{
+ Q_Q(QQuickAbstractColorPicker);
+ QQuickControlPrivate::itemImplicitWidthChanged(item);
+ if (item == m_handle)
+ emit q->implicitHandleWidthChanged();
+}
+
+void QQuickAbstractColorPickerPrivate::itemImplicitHeightChanged(QQuickItem *item)
+{
+ Q_Q(QQuickAbstractColorPicker);
+ QQuickControlPrivate::itemImplicitHeightChanged(item);
+ if (item == m_handle)
+ emit q->implicitHandleHeightChanged();
+}
+
+QQuickAbstractColorPicker::QQuickAbstractColorPicker(QQuickAbstractColorPickerPrivate &dd,
+ QQuickItem *parent)
+ : QQuickControl(dd, parent)
+{
+ setActiveFocusOnTab(true);
+ setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+QColor QQuickAbstractColorPicker::color() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ return d->m_hsl ? QColor::fromHslF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.l, d->m_hsva.a)
+ : QColor::fromHsvF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.v, d->m_hsva.a);
+}
+
+void QQuickAbstractColorPicker::setColor(const QColor &c)
+{
+ Q_D(QQuickAbstractColorPicker);
+ // QColor represents a theoretical color, rather than simply an rgba value.
+ // Therefore, two QColor objects can be different,
+ // and yet translate to the same rgba value.
+ // Since the color picker can reuse the same rgba value for multiple pixels,
+ // we should not return early if the rgba() values are equal,
+ // but only if the QColor objects are exactly the same.
+
+ if (color() == c)
+ return;
+
+ // When called from QQuickColorDialogImpl, it might not have the same spec as the current color
+ // picker.
+ if (d->m_hsl && c.spec() == QColor::Spec::Hsv) {
+ const auto sl = getSaturationAndLightness(c.hsvSaturationF(), c.valueF());
+ d->m_hsva.h = qBound(.0, c.hsvHueF(), 1.0);
+ d->m_hsva.s = qBound(.0, sl.first, 1.0);
+ d->m_hsva.l = qBound(.0, sl.second, 1.0);
+ } else if (!d->m_hsl && c.spec() == QColor::Spec::Hsl) {
+ const auto sv = getSaturationAndValue(c.hslSaturationF(), c.lightnessF());
+ d->m_hsva.h = qBound(.0, c.hslHueF(), 1.0);
+ d->m_hsva.s = qBound(.0, sv.first, 1.0);
+ d->m_hsva.v = qBound(.0, sv.second, 1.0);
+ } else {
+ d->m_hsva.h = qBound(.0, d->m_hsl ? c.hslHueF() : c.hsvHueF(), 1.0);
+ d->m_hsva.s = qBound(.0, d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF(), 1.0);
+ d->m_hsva.v = qBound(.0, d->m_hsl ? c.lightnessF() : c.valueF(), 1.0);
+ }
+
+ d->m_hsva.a = qBound(.0, c.alphaF(), 1.0);
+
+ emit colorChanged(color());
+}
+
+qreal QQuickAbstractColorPicker::alpha() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ return d->m_hsva.a;
+}
+
+void QQuickAbstractColorPicker::setAlpha(qreal alpha)
+{
+ Q_D(QQuickAbstractColorPicker);
+
+ if (!qt_is_finite(alpha))
+ return;
+
+ alpha = qBound(.0, alpha, 1.0);
+
+ if (qFuzzyCompare(d->m_hsva.a, alpha))
+ return;
+
+ d->m_hsva.a = alpha;
+
+ emit colorChanged(color());
+}
+
+qreal QQuickAbstractColorPicker::hue() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ return d->m_hsva.h;
+}
+void QQuickAbstractColorPicker::setHue(qreal hue)
+{
+ Q_D(QQuickAbstractColorPicker);
+
+ if (!qt_is_finite(hue))
+ return;
+
+ d->m_hsva.h = hue;
+
+ emit colorChanged(color());
+}
+
+qreal QQuickAbstractColorPicker::saturation() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ return d->m_hsva.s;
+}
+
+void QQuickAbstractColorPicker::setSaturation(qreal saturation)
+{
+ Q_D(QQuickAbstractColorPicker);
+ if (!qt_is_finite(saturation))
+ return;
+
+ d->m_hsva.s = saturation;
+
+ emit colorChanged(color());
+}
+qreal QQuickAbstractColorPicker::value() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ return d->m_hsl ? getSaturationAndValue(d->m_hsva.s, d->m_hsva.l).second : d->m_hsva.v;
+}
+void QQuickAbstractColorPicker::setValue(qreal value)
+{
+ Q_D(QQuickAbstractColorPicker);
+ if (!qt_is_finite(value))
+ return;
+
+ const auto sv = d->m_hsl ? getSaturationAndValue(d->m_hsva.s, d->m_hsva.l)
+ : std::pair<qreal, qreal>(d->m_hsva.s, value);
+ d->m_hsva.s = sv.first;
+ d->m_hsva.v = sv.second;
+
+ emit colorChanged(color());
+}
+
+qreal QQuickAbstractColorPicker::lightness() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ return d->m_hsl ? d->m_hsva.l : getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v).second;
+}
+void QQuickAbstractColorPicker::setLightness(qreal lightness)
+{
+ Q_D(QQuickAbstractColorPicker);
+ if (!qt_is_finite(lightness))
+ return;
+
+ const auto sl = !d->m_hsl ? getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v)
+ : std::pair<qreal, qreal>(d->m_hsva.s, lightness);
+ d->m_hsva.s = sl.first;
+ d->m_hsva.l = sl.second;
+
+ emit colorChanged(color());
+}
+
+/*!
+ \internal
+
+ This property holds whether the slider is pressed.
+*/
+bool QQuickAbstractColorPicker::isPressed() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ return d->m_pressed;
+}
+
+void QQuickAbstractColorPicker::setPressed(bool pressed)
+{
+ Q_D(QQuickAbstractColorPicker);
+ if (pressed == d->m_pressed)
+ return;
+
+ d->m_pressed = pressed;
+ emit pressedChanged();
+}
+
+/*!
+ \internal
+
+ This property holds the handle item.
+*/
+QQuickItem *QQuickAbstractColorPicker::handle() const
+{
+ QQuickAbstractColorPickerPrivate *d = const_cast<QQuickAbstractColorPickerPrivate *>(d_func());
+ if (!d->m_handle)
+ d->executeHandle();
+ return d->m_handle;
+}
+
+void QQuickAbstractColorPicker::setHandle(QQuickItem *handle)
+{
+ Q_D(QQuickAbstractColorPicker);
+ if (handle == d->m_handle)
+ return;
+
+ if (!d->m_handle.isExecuting())
+ d->cancelHandle();
+
+ const qreal oldImplicitHandleWidth = implicitHandleWidth();
+ const qreal oldImplicitHandleHeight = implicitHandleHeight();
+
+ d->removeImplicitSizeListener(d->m_handle);
+ QQuickControlPrivate::hideOldItem(d->m_handle);
+ d->m_handle = handle;
+
+ if (handle) {
+ if (!handle->parentItem())
+ handle->setParentItem(this);
+ d->addImplicitSizeListener(handle);
+ }
+
+ if (!qFuzzyCompare(oldImplicitHandleWidth, implicitHandleWidth()))
+ emit implicitHandleWidthChanged();
+ if (!qFuzzyCompare(oldImplicitHandleHeight, implicitHandleHeight()))
+ emit implicitHandleHeightChanged();
+ if (!d->m_handle.isExecuting())
+ emit handleChanged();
+}
+
+/*!
+ \internal
+ \readonly
+
+ This property holds the implicit handle width.
+
+ The value is equal to \c {handle ? handle.implicitWidth : 0}.
+
+ This is typically used, together with \l {Control::}{implicitContentWidth} and
+ \l {Control::}{implicitBackgroundWidth}, to calculate the \l {Item::}{implicitWidth}.
+
+ \sa implicitHandleHeight
+*/
+qreal QQuickAbstractColorPicker::implicitHandleWidth() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ if (!d->m_handle)
+ return 0;
+ return d->m_handle->implicitWidth();
+}
+
+/*!
+ \internal
+ \readonly
+
+ This property holds the implicit handle height.
+
+ The value is equal to \c {handle ? handle.implicitHeight : 0}.
+
+ This is typically used, together with \l {Control::}{implicitContentHeight} and
+ \l {Control::}{implicitBackgroundHeight}, to calculate the \l {Item::}{implicitHeight}.
+
+ \sa implicitHandleWidth
+*/
+qreal QQuickAbstractColorPicker::implicitHandleHeight() const
+{
+ Q_D(const QQuickAbstractColorPicker);
+ if (!d->m_handle)
+ return 0;
+ return d->m_handle->implicitHeight();
+}
+
+void QQuickAbstractColorPicker::componentComplete()
+{
+ Q_D(QQuickAbstractColorPicker);
+ d->executeHandle(true);
+ QQuickControl::componentComplete();
+}
+
+void QQuickAbstractColorPicker::updateColor(const QPointF &pos)
+{
+ QColor c = colorAt(pos);
+ c.setAlphaF(alpha());
+ setColor(c);
+
+ emit colorPicked(c);
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
new file mode 100644
index 0000000000..b29e488b3a
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef QQUICKABSTRACTCOLORPICKER_P_H
+#define QQUICKABSTRACTCOLORPICKER_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/qcolor.h>
+#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAbstractColorPickerPrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickAbstractColorPicker : public QQuickControl
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY colorChanged)
+ Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY colorChanged)
+ Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY colorChanged)
+ Q_PROPERTY(qreal lightness READ lightness WRITE setLightness NOTIFY colorChanged)
+ Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY colorChanged FINAL)
+ Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL)
+ Q_PROPERTY(QQuickItem *handle READ handle WRITE setHandle NOTIFY handleChanged FINAL)
+ Q_PROPERTY(qreal implicitHandleWidth READ implicitHandleWidth NOTIFY implicitHandleWidthChanged FINAL)
+ Q_PROPERTY(qreal implicitHandleHeight READ implicitHandleHeight NOTIFY implicitHandleHeightChanged FINAL)
+ Q_CLASSINFO("DeferredPropertyNames", "background,contentItem,handle")
+ QML_NAMED_ELEMENT(AbstractColorPicker)
+ QML_ADDED_IN_VERSION(6, 4)
+ QML_UNCREATABLE("AbstractColorPicker is abstract.")
+
+public:
+ QColor color() const;
+ void setColor(const QColor &c);
+
+ qreal hue() const;
+ void setHue(qreal hue);
+
+ qreal saturation() const;
+ void setSaturation(qreal saturation);
+
+ qreal value() const;
+ void setValue(qreal value);
+
+ qreal lightness() const;
+ void setLightness(qreal lightness);
+
+ qreal alpha() const;
+ void setAlpha(qreal alpha);
+
+ bool isPressed() const;
+ void setPressed(bool pressed);
+
+ QQuickItem *handle() const;
+ void setHandle(QQuickItem *handle);
+
+ qreal implicitHandleWidth() const;
+ qreal implicitHandleHeight() const;
+
+Q_SIGNALS:
+ void colorChanged(const QColor &color);
+ void pressedChanged();
+ void handleChanged();
+ void implicitHandleWidthChanged();
+ void implicitHandleHeightChanged();
+
+ void colorPicked(const QColor &color);
+
+protected:
+ QQuickAbstractColorPicker(QQuickAbstractColorPickerPrivate &dd, QQuickItem *parent);
+
+ virtual QColor colorAt(const QPointF &pos) = 0;
+ void componentComplete() override;
+
+private:
+ void updateColor(const QPointF &pos);
+ Q_DISABLE_COPY(QQuickAbstractColorPicker)
+ Q_DECLARE_PRIVATE(QQuickAbstractColorPicker)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKABSTRACTCOLORPICKER_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p_p.h
new file mode 100644
index 0000000000..73878a1cc0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker_p_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKABSTRACTCOLORPICKER_P_P_H
+#define QQUICKABSTRACTCOLORPICKER_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/qquickcontrol_p_p.h>
+#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
+
+#include "qquickabstractcolorpicker_p.h"
+#include "qquickcolordialogutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAbstractColorPickerPrivate : public QQuickControlPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickAbstractColorPicker);
+
+public:
+ explicit QQuickAbstractColorPickerPrivate();
+
+ static QQuickAbstractColorPickerPrivate *get(QQuickAbstractColorPicker *colorPicker)
+ {
+ return colorPicker->d_func();
+ }
+
+ bool handlePress(const QPointF &point, ulong timestamp) override;
+ bool handleMove(const QPointF &point, ulong timestamp) override;
+ bool handleRelease(const QPointF &point, ulong timestamp) override;
+ void handleUngrab() override;
+
+ void cancelHandle();
+ void executeHandle(bool complete = false);
+
+ void itemImplicitWidthChanged(QQuickItem *item) override;
+ void itemImplicitHeightChanged(QQuickItem *item) override;
+
+ HSVA m_hsva;
+ QPointF m_pressPoint;
+ QQuickDeferredPointer<QQuickItem> m_handle;
+ bool m_pressed = false;
+
+protected:
+ bool m_hsl = false; // Use hsv by default.
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKABSTRACTCOLORPICKER_P_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp
new file mode 100644
index 0000000000..6f13403766
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl.cpp
@@ -0,0 +1,613 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickcolordialogimpl_p.h"
+#include "qquickcolordialogimpl_p_p.h"
+
+#include "qquickcolordialogutils_p.h"
+
+#include <QtQuickTemplates2/private/qquickslider_p.h>
+
+#include <qpa/qplatformintegration.h>
+#include <private/qguiapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QColor grabScreenColor(const QPoint &p)
+{
+ QScreen *screen = QGuiApplication::screenAt(p);
+ if (!screen)
+ screen = QGuiApplication::primaryScreen();
+ const QRect screenRect = screen->geometry();
+ const QPixmap pixmap =
+ screen->grabWindow(0, p.x() - screenRect.x(), p.y() - screenRect.y(), 1, 1);
+ const QImage i = pixmap.toImage();
+ return i.pixel(0, 0);
+}
+
+bool QQuickEyeDropperEventFilter::eventFilter(QObject *obj, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::MouseMove: {
+ m_lastPosition = static_cast<QMouseEvent *>(event)->globalPosition().toPoint();
+ m_update(m_lastPosition);
+ return true;
+ }
+ case QEvent::MouseButtonRelease: {
+ m_lastPosition = static_cast<QMouseEvent *>(event)->globalPosition().toPoint();
+ m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Default);
+ return true;
+ }
+ case QEvent::MouseButtonPress:
+ return true;
+ case QEvent::KeyPress: {
+ auto keyEvent = static_cast<QKeyEvent *>(event);
+#if QT_CONFIG(shortcut)
+ if (keyEvent->matches(QKeySequence::Cancel))
+ m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Cancel);
+ else
+#endif
+ if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
+ m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Default);
+ } else if (keyEvent->key() == Qt::Key_Escape) {
+ m_leave(m_lastPosition, QQuickEyeDropperEventFilter::LeaveReason::Cancel);
+ }
+ keyEvent->accept();
+ return true;
+ }
+ default:
+ return QObject::eventFilter(obj, event);
+ }
+}
+
+QQuickColorDialogImplPrivate::QQuickColorDialogImplPrivate() = default;
+
+QQuickColorDialogImplPrivate::~QQuickColorDialogImplPrivate()
+{
+ if (m_eyeDropperMode)
+ eyeDropperLeave(QCursor::pos(), QQuickEyeDropperEventFilter::LeaveReason::Default);
+}
+
+void QQuickColorDialogImplPrivate::handleClick(QQuickAbstractButton *button)
+{
+ Q_Q(QQuickColorDialogImpl);
+ const auto c = q->color();
+ if (buttonRole(button) == QPlatformDialogHelper::AcceptRole && c.isValid()) {
+ q->setColor(c);
+ q->accept();
+ }
+ QQuickDialogPrivate::handleClick(button);
+}
+
+QQuickColorDialogImplAttached *QQuickColorDialogImplPrivate::attachedOrWarn()
+{
+ Q_Q(QQuickColorDialogImpl);
+ QQuickColorDialogImplAttached *attached = static_cast<QQuickColorDialogImplAttached *>(
+ qmlAttachedPropertiesObject<QQuickColorDialogImpl>(q, false));
+ if (!attached)
+ qmlWarning(q) << "Expected ColorDialogImpl attached object to be present on" << this;
+ return attached;
+}
+
+void QQuickColorDialogImplPrivate::eyeDropperEnter()
+{
+ Q_Q(const QQuickColorDialogImpl);
+ if (m_eyeDropperMode)
+ return;
+
+ if (m_eyeDropperWindow.isNull()) {
+ if (window.isNull()) {
+ qWarning() << "No window found, cannot enter eyeDropperMode.";
+ return;
+ }
+
+ m_eyeDropperWindow = window;
+ }
+
+ m_eyeDropperPreviousColor = q->color();
+
+ if (!bool(eyeDropperEventFilter))
+ eyeDropperEventFilter.reset(new QQuickEyeDropperEventFilter(
+ [this](QPoint pos, QQuickEyeDropperEventFilter::LeaveReason c) {
+ eyeDropperLeave(pos, c);
+ },
+ [this](QPoint pos) { eyeDropperPointerMoved(pos); }));
+
+ if (m_eyeDropperWindow->setMouseGrabEnabled(true)) {
+ QGuiApplication::setOverrideCursor(Qt::CrossCursor);
+ m_eyeDropperWindow->installEventFilter(eyeDropperEventFilter.get());
+ m_eyeDropperMode = true;
+ }
+}
+
+void QQuickColorDialogImplPrivate::eyeDropperLeave(
+ const QPoint &pos, QQuickEyeDropperEventFilter::LeaveReason actionOnLeave)
+{
+ Q_Q(QQuickColorDialogImpl);
+
+ if (!m_eyeDropperMode)
+ return;
+
+ if (!m_eyeDropperWindow) {
+ qWarning() << "Window not set, cannot leave eyeDropperMode.";
+ return;
+ }
+
+ const QColor colorToUse = actionOnLeave == QQuickEyeDropperEventFilter::LeaveReason::Cancel
+ ? m_eyeDropperPreviousColor
+ : grabScreenColor(pos);
+ q->setColor(colorToUse);
+
+ m_eyeDropperWindow->removeEventFilter(eyeDropperEventFilter.get());
+ m_eyeDropperWindow->setMouseGrabEnabled(false);
+ QGuiApplication::restoreOverrideCursor();
+
+ m_eyeDropperMode = false;
+ m_eyeDropperWindow.clear();
+}
+
+void QQuickColorDialogImplPrivate::eyeDropperPointerMoved(const QPoint &pos)
+{
+ Q_Q(QQuickColorDialogImpl);
+ q->setColor(grabScreenColor(pos));
+}
+
+void QQuickColorDialogImplPrivate::alphaSliderMoved()
+{
+ Q_Q(QQuickColorDialogImpl);
+ if (auto attached = attachedOrWarn())
+ q->setAlpha(attached->alphaSlider()->value());
+}
+
+QQuickColorDialogImpl::QQuickColorDialogImpl(QObject *parent)
+ : QQuickDialog(*(new QQuickColorDialogImplPrivate), parent)
+{
+}
+
+QQuickColorDialogImplAttached *QQuickColorDialogImpl::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickColorDialogImplAttached(object);
+}
+
+QColor QQuickColorDialogImpl::color() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->m_hsl ? QColor::fromHslF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.l, d->m_hsva.a)
+ : QColor::fromHsvF(d->m_hsva.h, d->m_hsva.s, d->m_hsva.v, d->m_hsva.a);
+}
+
+void QQuickColorDialogImpl::setColor(const QColor &c)
+{
+ Q_D(QQuickColorDialogImpl);
+ if (color().rgba() == c.rgba())
+ return;
+
+ // If we get a QColor from an Hsv or Hsl color system,
+ // we want to get the raw values without the risk of QColor converting them,
+ // and possible deleting relevant information for achromatic cases.
+ if (c.spec() == QColor::Spec::Hsv) {
+ d->m_hsva.h = qBound(.0, c.hsvHueF(), 1.0);
+ if (d->m_hsl) {
+ const auto sl = getSaturationAndLightness(c.hsvSaturationF(), c.valueF());
+ d->m_hsva.s = qBound(.0, sl.first, 1.0);
+ d->m_hsva.l = qBound(.0, sl.second, 1.0);
+ } else {
+ d->m_hsva.s = qBound(.0, c.hsvSaturationF(), 1.0);
+ d->m_hsva.v = qBound(.0, c.valueF(), 1.0);
+ }
+ } else if (c.spec() == QColor::Spec::Hsl) {
+ d->m_hsva.h = qBound(.0, c.hslHueF(), 1.0);
+ if (d->m_hsl) {
+ d->m_hsva.s = qBound(.0, c.hslSaturationF(), 1.0);
+ d->m_hsva.l = qBound(.0, c.lightnessF(), 1.0);
+ } else {
+ const auto sv = getSaturationAndValue(c.hslSaturationF(), c.lightnessF());
+ d->m_hsva.s = qBound(.0, sv.first, 1.0);
+ d->m_hsva.v = qBound(.0, sv.second, 1.0);
+ }
+ } else {
+ d->m_hsva.h = qBound(.0, d->m_hsl ? c.hslHueF() : c.hsvHueF(), 1.0);
+ d->m_hsva.s = qBound(.0, d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF(), 1.0);
+ d->m_hsva.v = qBound(.0, d->m_hsl ? c.lightnessF() : c.valueF(), 1.0);
+ }
+
+ d->m_hsva.a = c.alphaF();
+
+ emit colorChanged(color());
+}
+
+int QQuickColorDialogImpl::red() const
+{
+ return color().red();
+}
+
+void QQuickColorDialogImpl::setRed(int red)
+{
+ Q_D(QQuickColorDialogImpl);
+
+ auto c = color();
+
+ if (c.red() == red)
+ return;
+
+ c.setRed(red);
+
+ d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
+ d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
+ d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
+ d->m_hsva.a = c.alphaF();
+
+ emit colorChanged(c);
+}
+
+int QQuickColorDialogImpl::green() const
+{
+ return color().green();
+}
+
+void QQuickColorDialogImpl::setGreen(int green)
+{
+ Q_D(QQuickColorDialogImpl);
+
+ auto c = color();
+
+ if (c.green() == green)
+ return;
+
+ c.setGreen(green);
+
+ d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
+ d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
+ d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
+ d->m_hsva.a = c.alphaF();
+
+ emit colorChanged(c);
+}
+
+int QQuickColorDialogImpl::blue() const
+{
+ return color().blue();
+}
+
+void QQuickColorDialogImpl::setBlue(int blue)
+{
+ Q_D(QQuickColorDialogImpl);
+
+ auto c = color();
+
+ if (c.blue() == blue)
+ return;
+
+ c.setBlue(blue);
+
+ d->m_hsva.h = d->m_hsl ? c.hslHueF() : c.hsvHueF();
+ d->m_hsva.s = d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF();
+ d->m_hsva.v = d->m_hsl ? c.lightnessF() : c.valueF();
+ d->m_hsva.a = c.alphaF();
+
+ emit colorChanged(c);
+}
+
+qreal QQuickColorDialogImpl::alpha() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->m_hsva.a;
+}
+
+void QQuickColorDialogImpl::setAlpha(qreal alpha)
+{
+ Q_D(QQuickColorDialogImpl);
+
+ if (!qt_is_finite(alpha))
+ return;
+
+ alpha = qBound(.0, alpha, 1.0);
+
+ if (qFuzzyCompare(d->m_hsva.a, alpha))
+ return;
+
+ d->m_hsva.a = alpha;
+
+ emit colorChanged(color());
+}
+
+qreal QQuickColorDialogImpl::hue() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->m_hsva.h;
+}
+
+void QQuickColorDialogImpl::setHue(qreal hue)
+{
+ Q_D(QQuickColorDialogImpl);
+
+ if (!qt_is_finite(hue))
+ return;
+
+ d->m_hsva.h = hue;
+
+ emit colorChanged(color());
+}
+
+qreal QQuickColorDialogImpl::saturation() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->m_hsva.s;
+}
+
+void QQuickColorDialogImpl::setSaturation(qreal saturation)
+{
+ Q_D(QQuickColorDialogImpl);
+ if (!qt_is_finite(saturation))
+ return;
+
+ d->m_hsva.s = saturation;
+
+ emit colorChanged(color());
+}
+
+qreal QQuickColorDialogImpl::value() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->m_hsl ? getSaturationAndValue(d->m_hsva.s, d->m_hsva.l).second : d->m_hsva.v;
+}
+
+void QQuickColorDialogImpl::setValue(qreal value)
+{
+ Q_D(QQuickColorDialogImpl);
+ if (!qt_is_finite(value))
+ return;
+
+ d->m_hsva.v = value;
+
+ if (d->m_hsl)
+ d->m_hsva.s = getSaturationAndValue(d->m_hsva.s, d->m_hsva.l).first;
+
+ d->m_hsl = false;
+ emit colorChanged(color());
+}
+
+qreal QQuickColorDialogImpl::lightness() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->m_hsl ? d->m_hsva.l : getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v).second;
+}
+
+void QQuickColorDialogImpl::setLightness(qreal lightness)
+{
+ Q_D(QQuickColorDialogImpl);
+ if (!qt_is_finite(lightness))
+ return;
+
+ d->m_hsva.l = lightness;
+
+ if (!d->m_hsl)
+ d->m_hsva.s = getSaturationAndLightness(d->m_hsva.s, d->m_hsva.v).first;
+
+ d->m_hsl = true;
+ emit colorChanged(color());
+}
+
+bool QQuickColorDialogImpl::isHsl() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->m_hsl;
+}
+
+void QQuickColorDialogImpl::setHsl(bool hsl)
+{
+ Q_D(QQuickColorDialogImpl);
+
+ if (d->m_hsl == hsl)
+ return;
+
+ d->m_hsl = hsl;
+ emit specChanged();
+}
+
+QSharedPointer<QColorDialogOptions> QQuickColorDialogImpl::options() const
+{
+ Q_D(const QQuickColorDialogImpl);
+ return d->options;
+}
+
+void QQuickColorDialogImpl::setOptions(const QSharedPointer<QColorDialogOptions> &options)
+{
+ Q_D(QQuickColorDialogImpl);
+ d->options = options;
+
+ QQuickColorDialogImplAttached *attached = d->attachedOrWarn();
+
+ if (attached) {
+ const bool screenGrabbingAllowed = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ScreenWindowGrabbing);
+ const bool offscreen = qgetenv("QT_QPA_PLATFORM").compare(QLatin1String("offscreen"), Qt::CaseInsensitive) == 0;
+ attached->eyeDropperButton()->setVisible(screenGrabbingAllowed && !offscreen);
+
+ if (d->options) {
+ attached->buttonBox()->setVisible(
+ !(d->options->options() & QColorDialogOptions::NoButtons));
+
+ const bool showAlpha = d->options->options() & QColorDialogOptions::ShowAlphaChannel;
+ attached->alphaSlider()->setVisible(showAlpha);
+ attached->colorInputs()->setShowAlpha(showAlpha);
+ }
+ }
+}
+
+void QQuickColorDialogImpl::invokeEyeDropper()
+{
+ Q_D(QQuickColorDialogImpl);
+ d->eyeDropperEnter();
+}
+
+QQuickColorDialogImplAttached::QQuickColorDialogImplAttached(QObject *parent)
+ : QObject(*(new QQuickColorDialogImplAttachedPrivate), parent)
+{
+ if (!qobject_cast<QQuickColorDialogImpl *>(parent)) {
+ qmlWarning(this) << "ColorDialogImpl attached properties should only be "
+ << "accessed through the root ColorDialogImpl instance";
+ }
+}
+
+QQuickDialogButtonBox *QQuickColorDialogImplAttached::buttonBox() const
+{
+ Q_D(const QQuickColorDialogImplAttached);
+ return d->buttonBox;
+}
+
+void QQuickColorDialogImplAttached::setButtonBox(QQuickDialogButtonBox *buttonBox)
+{
+ Q_D(QQuickColorDialogImplAttached);
+ if (d->buttonBox == buttonBox)
+ return;
+
+ if (d->buttonBox) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(colorDialogImpl);
+ 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 (d->buttonBox) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(colorDialogImpl);
+ 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();
+}
+
+QQuickAbstractButton *QQuickColorDialogImplAttached::eyeDropperButton() const
+{
+ Q_D(const QQuickColorDialogImplAttached);
+ return d->eyeDropperButton;
+}
+
+void QQuickColorDialogImplAttached::setEyeDropperButton(QQuickAbstractButton *eyeDropperButton)
+{
+ Q_D(QQuickColorDialogImplAttached);
+ Q_ASSERT(!d->eyeDropperButton);
+ if (d->eyeDropperButton == eyeDropperButton)
+ return;
+
+ d->eyeDropperButton = eyeDropperButton;
+ if (auto dialog = qobject_cast<QQuickColorDialogImpl *>(parent()))
+ connect(d->eyeDropperButton, &QQuickAbstractButton::clicked, dialog, &QQuickColorDialogImpl::invokeEyeDropper);
+ emit eyeDropperButtonChanged();
+}
+
+QQuickAbstractColorPicker *QQuickColorDialogImplAttached::colorPicker() const
+{
+ Q_D(const QQuickColorDialogImplAttached);
+ return d->colorPicker;
+}
+void QQuickColorDialogImplAttached::setColorPicker(QQuickAbstractColorPicker *colorPicker)
+{
+ Q_D(QQuickColorDialogImplAttached);
+ if (d->colorPicker == colorPicker)
+ return;
+
+ if (d->colorPicker) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl) {
+ QObject::disconnect(d->colorPicker, &QQuickAbstractColorPicker::colorPicked,
+ colorDialogImpl, &QQuickColorDialogImpl::setColor);
+ }
+ }
+
+ d->colorPicker = colorPicker;
+
+ if (d->colorPicker) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl) {
+ QObject::connect(d->colorPicker, &QQuickAbstractColorPicker::colorPicked,
+ colorDialogImpl, &QQuickColorDialogImpl::setColor);
+ }
+ }
+
+ emit colorPickerChanged();
+}
+
+QQuickSlider *QQuickColorDialogImplAttached::alphaSlider() const
+{
+ Q_D(const QQuickColorDialogImplAttached);
+ return d->alphaSlider;
+}
+
+void QQuickColorDialogImplAttached::setAlphaSlider(QQuickSlider *alphaSlider)
+{
+ Q_D(QQuickColorDialogImplAttached);
+ if (d->alphaSlider == alphaSlider)
+ return;
+
+ if (d->alphaSlider) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl) {
+ auto dialogPrivate = QQuickColorDialogImplPrivate::get(colorDialogImpl);
+ QObjectPrivate::disconnect(d->alphaSlider, &QQuickSlider::moved,
+ dialogPrivate, &QQuickColorDialogImplPrivate::alphaSliderMoved);
+ }
+ }
+
+ d->alphaSlider = alphaSlider;
+
+ if (d->alphaSlider) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl) {
+ auto dialogPrivate = QQuickColorDialogImplPrivate::get(colorDialogImpl);
+ QObjectPrivate::connect(d->alphaSlider, &QQuickSlider::moved,
+ dialogPrivate, &QQuickColorDialogImplPrivate::alphaSliderMoved);
+ }
+ }
+
+ emit alphaSliderChanged();
+}
+
+QQuickColorInputs *QQuickColorDialogImplAttached::colorInputs() const
+{
+ Q_D(const QQuickColorDialogImplAttached);
+ return d->colorInputs;
+}
+
+void QQuickColorDialogImplAttached::setColorInputs(QQuickColorInputs *colorInputs)
+{
+ Q_D(QQuickColorDialogImplAttached);
+
+ if (d->colorInputs == colorInputs)
+ return;
+
+ if (d->colorInputs) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl)
+ QObject::disconnect(d->colorInputs, &QQuickColorInputs::colorModified,
+ colorDialogImpl, &QQuickColorDialogImpl::setColor);
+ }
+
+ d->colorInputs = colorInputs;
+
+ if (d->colorInputs) {
+ QQuickColorDialogImpl *colorDialogImpl = qobject_cast<QQuickColorDialogImpl *>(parent());
+ if (colorDialogImpl)
+ QObject::connect(d->colorInputs, &QQuickColorInputs::colorModified,
+ colorDialogImpl, &QQuickColorDialogImpl::setColor);
+ }
+
+ emit colorInputsChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h
new file mode 100644
index 0000000000..110298b5f2
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p.h
@@ -0,0 +1,150 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKCOLORDIALOGIMPL_P_H
+#define QQUICKCOLORDIALOGIMPL_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/qquickdialog_p.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickDialogButtonBox;
+class QQuickAbstractColorPicker;
+class QQuickColorInputs;
+class QQuickSlider;
+
+class QQuickColorDialogImplAttached;
+class QQuickColorDialogImplAttachedPrivate;
+class QQuickColorDialogImplPrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickColorDialogImpl : public QQuickDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY colorChanged)
+ Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation NOTIFY colorChanged)
+ Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY colorChanged)
+ Q_PROPERTY(qreal lightness READ lightness WRITE setLightness NOTIFY colorChanged)
+ Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY colorChanged FINAL)
+ Q_PROPERTY(int red READ red WRITE setRed NOTIFY colorChanged FINAL)
+ Q_PROPERTY(int green READ green WRITE setGreen NOTIFY colorChanged FINAL)
+ Q_PROPERTY(int blue READ blue WRITE setBlue NOTIFY colorChanged FINAL)
+ Q_PROPERTY(bool isHsl READ isHsl WRITE setHsl NOTIFY specChanged FINAL)
+ QML_NAMED_ELEMENT(ColorDialogImpl)
+ QML_ATTACHED(QQuickColorDialogImplAttached)
+ QML_ADDED_IN_VERSION(6, 4)
+
+public:
+ explicit QQuickColorDialogImpl(QObject *parent = nullptr);
+
+ static QQuickColorDialogImplAttached *qmlAttachedProperties(QObject *object);
+
+ QSharedPointer<QColorDialogOptions> options() const;
+ void setOptions(const QSharedPointer<QColorDialogOptions> &options);
+
+ QColor color() const;
+ void setColor(const QColor &c);
+
+ int red() const;
+ void setRed(int red);
+
+ int green() const;
+ void setGreen(int green);
+
+ int blue() const;
+ void setBlue(int blue);
+
+ qreal alpha() const;
+ void setAlpha(qreal alpha);
+
+ qreal hue() const;
+ void setHue(qreal hue);
+
+ qreal saturation() const;
+ void setSaturation(qreal saturation);
+
+ qreal value() const;
+ void setValue(qreal value);
+
+ qreal lightness() const;
+ void setLightness(qreal lightness);
+
+ bool isHsl() const;
+ void setHsl(bool hsl);
+
+ Q_INVOKABLE void invokeEyeDropper();
+
+Q_SIGNALS:
+ void colorChanged(const QColor &color);
+ void specChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickColorDialogImpl)
+ Q_DECLARE_PRIVATE(QQuickColorDialogImpl)
+};
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickColorDialogImplAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox WRITE setButtonBox NOTIFY buttonBoxChanged FINAL)
+ Q_PROPERTY(QQuickAbstractButton *eyeDropperButton READ eyeDropperButton WRITE setEyeDropperButton NOTIFY eyeDropperButtonChanged FINAL)
+ Q_PROPERTY(QQuickAbstractColorPicker *colorPicker READ colorPicker WRITE setColorPicker NOTIFY
+ colorPickerChanged FINAL)
+ Q_PROPERTY(QQuickColorInputs *colorInputs READ colorInputs WRITE setColorInputs NOTIFY
+ colorInputsChanged FINAL)
+ Q_PROPERTY(QQuickSlider *alphaSlider READ alphaSlider WRITE setAlphaSlider NOTIFY
+ alphaSliderChanged FINAL)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickabstractbutton_p.h>)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickslider_p.h>)
+ Q_MOC_INCLUDE("qquickabstractcolorpicker_p.h")
+ Q_MOC_INCLUDE("qquickcolorinputs_p.h")
+
+public:
+ explicit QQuickColorDialogImplAttached(QObject *parent = nullptr);
+
+ QQuickDialogButtonBox *buttonBox() const;
+ void setButtonBox(QQuickDialogButtonBox *buttonBox);
+
+ QQuickAbstractButton *eyeDropperButton() const;
+ void setEyeDropperButton(QQuickAbstractButton *eyeDropperButton);
+
+ QQuickAbstractColorPicker *colorPicker() const;
+ void setColorPicker(QQuickAbstractColorPicker *colorPicker);
+
+ QQuickColorInputs *colorInputs() const;
+ void setColorInputs(QQuickColorInputs *colorInputs);
+
+ QQuickSlider *alphaSlider() const;
+ void setAlphaSlider(QQuickSlider *alphaSlider);
+
+Q_SIGNALS:
+ void buttonBoxChanged();
+ void eyeDropperButtonChanged();
+ void colorPickerChanged();
+ void colorInputsChanged();
+ void alphaSliderChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickColorDialogImplAttached)
+ Q_DECLARE_PRIVATE(QQuickColorDialogImplAttached)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickColorDialogImpl)
+
+#endif // QQUICKCOLORDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h
new file mode 100644
index 0000000000..b7c4dc8e2c
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogimpl_p_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKCOLORDIALOGIMPL_P_P_H
+#define QQUICKCOLORDIALOGIMPL_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 "qquickcolordialogimpl_p.h"
+#include "qquickabstractcolorpicker_p.h"
+#include "qquickcolorinputs_p.h"
+
+#include <QtQuickTemplates2/private/qquickdialog_p_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>
+#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
+#include <QtQuickTemplates2/private/qquickslider_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickEyeDropperEventFilter : public QObject
+{
+public:
+ enum class LeaveReason { Default, Cancel };
+ explicit QQuickEyeDropperEventFilter(std::function<void(QPoint, LeaveReason)> callOnLeave,
+ std::function<void(QPoint)> callOnUpdate)
+ : m_leave(callOnLeave), m_update(callOnUpdate)
+ {
+ }
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+
+private:
+ std::function<void(QPoint, LeaveReason)> m_leave;
+ std::function<void(QPoint)> m_update;
+ QPoint m_lastPosition;
+};
+
+class QQuickColorDialogImplPrivate : public QQuickDialogPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQuickColorDialogImpl);
+
+public:
+ explicit QQuickColorDialogImplPrivate();
+ ~QQuickColorDialogImplPrivate();
+
+ static QQuickColorDialogImplPrivate *get(QQuickColorDialogImpl *dialog)
+ {
+ return dialog->d_func();
+ }
+
+ QQuickColorDialogImplAttached *attachedOrWarn();
+
+ void handleClick(QQuickAbstractButton *button) override;
+
+ void eyeDropperEnter();
+ void eyeDropperLeave(const QPoint &pos, QQuickEyeDropperEventFilter::LeaveReason actionOnLeave);
+ void eyeDropperPointerMoved(const QPoint &pos);
+
+ void alphaSliderMoved();
+
+ QSharedPointer<QColorDialogOptions> options;
+ HSVA m_hsva;
+ std::unique_ptr<QQuickEyeDropperEventFilter> eyeDropperEventFilter;
+ QPointer<QQuickWindow> m_eyeDropperWindow;
+ QColor m_eyeDropperPreviousColor;
+ bool m_eyeDropperMode = false;
+ bool m_showAlpha = false;
+ bool m_hsl = false;
+};
+
+class QQuickColorDialogImplAttachedPrivate : public QObjectPrivate
+{
+public:
+ QPointer<QQuickDialogButtonBox> buttonBox;
+ QPointer<QQuickAbstractButton> eyeDropperButton;
+ QPointer<QQuickColorInputs> colorInputs;
+ QPointer<QQuickAbstractColorPicker> colorPicker;
+ QPointer<QQuickSlider> alphaSlider;
+
+ Q_DECLARE_PUBLIC(QQuickColorDialogImplAttached)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKCOLORDIALOGIMPL_P_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils.cpp
new file mode 100644
index 0000000000..0a123f84a6
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickcolordialogutils_p.h"
+
+std::pair<qreal, qreal> getSaturationAndValue(qreal saturation, qreal lightness)
+{
+ const qreal v = lightness + saturation * qMin(lightness, 1 - lightness);
+ if (v == .0)
+ return { .0, .0 };
+ const qreal s = 2 * (1 - lightness / v);
+ return { s, v };
+}
+
+std::pair<qreal, qreal> getSaturationAndLightness(qreal saturation, qreal value)
+{
+ const qreal l = value * (1 - saturation / 2);
+ if (l == .0)
+ return { .0, .0 };
+ if (l == 1 && value == 1)
+ return { saturation, l };
+ const qreal s = (value - l) / qMin(l, 1 - l);
+ return { s, l };
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils_p.h
new file mode 100644
index 0000000000..6b5c2e026c
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolordialogutils_p.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKCOLORDIALOGUTILS_P_H
+#define QQUICKCOLORDIALOGUTILS_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/QtGlobal>
+
+std::pair<qreal, qreal> getSaturationAndValue(qreal saturation, qreal lightness);
+
+std::pair<qreal, qreal> getSaturationAndLightness(qreal saturation, qreal value);
+
+struct HSVA
+{
+ qreal h = .0;
+ qreal s = .0;
+ union {
+ qreal v = 1.0;
+ qreal l;
+ };
+ qreal a = 1.0;
+};
+
+#endif // QQUICKCOLORDIALOGUTILS_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs.cpp
new file mode 100644
index 0000000000..cc3743c494
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs.cpp
@@ -0,0 +1,509 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickcolorinputs_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQuickColorInputs::QQuickColorInputs() = default;
+
+QColor QQuickColorInputs::color() const
+{
+ return QColor::fromHsvF(m_hsva.h, m_hsva.s, m_hsva.v, m_hsva.a);
+}
+
+void QQuickColorInputs::setColor(const QColor &c)
+{
+ if (color().rgba() == c.rgba())
+ return;
+
+ // If we get a QColor from an Hsv or Hsl color system,
+ // we want to get the raw values without the risk of QColor converting them,
+ // and possible deleting relevant information for achromatic cases.
+ if (c.spec() == QColor::Spec::Hsl) {
+ const auto sv = getSaturationAndValue(c.hslSaturationF(), c.lightnessF());
+ m_hsva.h = qBound(.0, c.hslHueF(), 1.0);
+ m_hsva.s = qBound(.0, sv.first, 1.0);
+ m_hsva.v = qBound(.0, sv.second, 1.0);
+ } else {
+ m_hsva.h = qBound(.0, c.hsvHueF(), 1.0);
+ m_hsva.s = qBound(.0, c.hsvSaturationF(), 1.0);
+ m_hsva.v = qBound(.0, c.valueF(), 1.0);
+ }
+
+ m_hsva.a = c.alphaF();
+
+ emit colorChanged(color());
+}
+
+int QQuickColorInputs::red() const
+{
+ return color().red();
+}
+
+int QQuickColorInputs::green() const
+{
+ return color().green();
+}
+
+int QQuickColorInputs::blue() const
+{
+ return color().blue();
+}
+
+qreal QQuickColorInputs::alpha() const
+{
+ return m_hsva.a;
+}
+
+qreal QQuickColorInputs::hue() const
+{
+ return m_hsva.h;
+}
+
+qreal QQuickColorInputs::hslSaturation() const
+{
+ return getSaturationAndLightness(m_hsva.s, m_hsva.v).first;
+}
+
+qreal QQuickColorInputs::hsvSaturation() const
+{
+ return m_hsva.s;
+}
+
+qreal QQuickColorInputs::value() const
+{
+ return m_hsva.v;
+}
+
+qreal QQuickColorInputs::lightness() const
+{
+ return getSaturationAndLightness(m_hsva.s, m_hsva.v).second;
+}
+
+bool QQuickColorInputs::showAlpha() const
+{
+ return m_showAlpha;
+}
+
+void QQuickColorInputs::setShowAlpha(bool showAlpha)
+{
+ if (m_showAlpha == showAlpha)
+ return;
+
+ m_showAlpha = showAlpha;
+ emit showAlphaChanged(m_showAlpha);
+}
+
+QQuickTextInput *QQuickColorInputs::hexInput() const
+{
+ return m_hexInput;
+}
+
+void QQuickColorInputs::setHexInput(QQuickTextInput *hexInput)
+{
+ if (m_hexInput == hexInput)
+ return;
+
+ if (m_hexInput)
+ disconnect(m_hexInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHexChanged);
+
+ m_hexInput = hexInput;
+
+ if (m_hexInput)
+ connect(m_hexInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHexChanged);
+
+ emit hexInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::redInput() const
+{
+ return m_redInput;
+}
+
+void QQuickColorInputs::setRedInput(QQuickTextInput *redInput)
+{
+ if (m_redInput == redInput)
+ return;
+
+ if (m_redInput)
+ disconnect(m_redInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleRedChanged);
+
+ m_redInput = redInput;
+
+ if (m_redInput)
+ connect(m_redInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleRedChanged);
+
+ emit redInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::greenInput() const
+{
+ return m_greenInput;
+}
+
+void QQuickColorInputs::setGreenInput(QQuickTextInput *greenInput)
+{
+ if (m_greenInput == greenInput)
+ return;
+
+ if (m_greenInput)
+ disconnect(m_greenInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleGreenChanged);
+
+ m_greenInput = greenInput;
+
+ if (m_greenInput)
+ connect(m_greenInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleGreenChanged);
+
+ emit greenInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::blueInput() const
+{
+ return m_blueInput;
+}
+
+void QQuickColorInputs::setBlueInput(QQuickTextInput *blueInput)
+{
+ if (m_blueInput == blueInput)
+ return;
+
+ if (m_blueInput)
+ disconnect(m_blueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleBlueChanged);
+
+ m_blueInput = blueInput;
+
+ if (m_blueInput)
+ connect(m_blueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleBlueChanged);
+
+ emit blueInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::hsvHueInput() const
+{
+ return m_hsvHueInput;
+}
+
+void QQuickColorInputs::setHsvHueInput(QQuickTextInput *hsvHueInput)
+{
+ if (m_hsvHueInput == hsvHueInput)
+ return;
+
+ if (m_hsvHueInput)
+ disconnect(m_hsvHueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHsvHueChanged);
+
+ m_hsvHueInput = hsvHueInput;
+
+ if (m_hsvHueInput)
+ connect(m_hsvHueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHsvHueChanged);
+
+ emit hsvHueInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::hslHueInput() const
+{
+ return m_hslHueInput;
+}
+
+void QQuickColorInputs::setHslHueInput(QQuickTextInput *hslHueInput)
+{
+ if (m_hslHueInput == hslHueInput)
+ return;
+
+ if (m_hslHueInput)
+ disconnect(m_hslHueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHslHueChanged);
+
+ m_hslHueInput = hslHueInput;
+
+ if (m_hslHueInput)
+ connect(m_hslHueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHslHueChanged);
+
+ emit hslHueInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::hsvSaturationInput() const
+{
+ return m_hsvSaturationInput;
+}
+
+void QQuickColorInputs::setHsvSaturationInput(QQuickTextInput *hsvSaturationInput)
+{
+ if (m_hsvSaturationInput == hsvSaturationInput)
+ return;
+
+ if (m_hsvSaturationInput)
+ disconnect(m_hsvSaturationInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHsvSaturationChanged);
+
+ m_hsvSaturationInput = hsvSaturationInput;
+
+ if (m_hsvSaturationInput)
+ connect(m_hsvSaturationInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHsvSaturationChanged);
+
+ emit hsvSaturationInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::hslSaturationInput() const
+{
+ return m_hslSaturationInput;
+}
+
+void QQuickColorInputs::setHslSaturationInput(QQuickTextInput *hslSaturationInput)
+{
+ if (m_hslSaturationInput == hslSaturationInput)
+ return;
+
+ if (m_hslSaturationInput)
+ disconnect(m_hslSaturationInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHslSaturationChanged);
+
+ m_hslSaturationInput = hslSaturationInput;
+
+ if (m_hslSaturationInput)
+ connect(m_hslSaturationInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHslSaturationChanged);
+
+ emit hslSaturationInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::valueInput() const
+{
+ return m_valueInput;
+}
+
+void QQuickColorInputs::setValueInput(QQuickTextInput *valueInput)
+{
+ if (m_valueInput == valueInput)
+ return;
+
+ if (m_valueInput)
+ disconnect(m_valueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleValueChanged);
+
+ m_valueInput = valueInput;
+
+ if (m_valueInput)
+ connect(m_valueInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleValueChanged);
+
+ emit valueInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::lightnessInput() const
+{
+ return m_lightnessInput;
+}
+
+void QQuickColorInputs::setLightnessInput(QQuickTextInput *lightnessInput)
+{
+ if (m_lightnessInput == lightnessInput)
+ return;
+
+ if (m_lightnessInput)
+ disconnect(m_lightnessInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleLightnessChanged);
+
+ m_lightnessInput = lightnessInput;
+
+ if (m_lightnessInput)
+ connect(m_lightnessInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleLightnessChanged);
+
+ emit lightnessInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::rgbAlphaInput() const
+{
+ return m_rgbAlphaInput;
+}
+
+void QQuickColorInputs::setRgbAlphaInput(QQuickTextInput *alphaInput)
+{
+ if (alphaInput == m_rgbAlphaInput)
+ return;
+
+ if (m_rgbAlphaInput) {
+ disconnect(m_rgbAlphaInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleRgbAlphaChanged);
+ disconnect(this, &QQuickColorInputs::showAlphaChanged, m_rgbAlphaInput, &QQuickTextInput::setVisible);
+ }
+
+ m_rgbAlphaInput = alphaInput;
+
+ if (m_rgbAlphaInput) {
+ connect(m_rgbAlphaInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleRgbAlphaChanged);
+ connect(this, &QQuickColorInputs::showAlphaChanged, m_rgbAlphaInput, &QQuickTextInput::setVisible);
+ m_rgbAlphaInput->setVisible(showAlpha());
+ }
+
+ emit rgbAlphaInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::hsvAlphaInput() const
+{
+ return m_hsvAlphaInput;
+}
+
+void QQuickColorInputs::setHsvAlphaInput(QQuickTextInput *alphaInput)
+{
+ if (alphaInput == m_hsvAlphaInput)
+ return;
+
+ if (m_hsvAlphaInput) {
+ disconnect(m_hsvAlphaInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHsvAlphaChanged);
+ disconnect(this, &QQuickColorInputs::showAlphaChanged, m_hsvAlphaInput, &QQuickTextInput::setVisible);
+ }
+
+ m_hsvAlphaInput = alphaInput;
+
+ if (m_hsvAlphaInput) {
+ connect(m_hsvAlphaInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHsvAlphaChanged);
+ connect(this, &QQuickColorInputs::showAlphaChanged, m_hsvAlphaInput, &QQuickTextInput::setVisible);
+ m_hsvAlphaInput->setVisible(showAlpha());
+ }
+
+ emit hsvAlphaInputChanged();
+}
+
+QQuickTextInput *QQuickColorInputs::hslAlphaInput() const
+{
+ return m_hslAlphaInput;
+}
+
+void QQuickColorInputs::setHslAlphaInput(QQuickTextInput *alphaInput)
+{
+ if (alphaInput == m_hslAlphaInput)
+ return;
+
+ if (m_hslAlphaInput) {
+ disconnect(m_hslAlphaInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHslAlphaChanged);
+ disconnect(this, &QQuickColorInputs::showAlphaChanged, m_hslAlphaInput, &QQuickTextInput::setVisible);
+ }
+
+ m_hslAlphaInput = alphaInput;
+
+ if (m_hslAlphaInput) {
+ connect(m_hslAlphaInput, &QQuickTextInput::editingFinished, this, &QQuickColorInputs::handleHslAlphaChanged);
+ connect(this, &QQuickColorInputs::showAlphaChanged, m_hslAlphaInput, &QQuickTextInput::setVisible);
+ m_hslAlphaInput->setVisible(showAlpha());
+ }
+
+ emit hslAlphaInputChanged();
+}
+
+void QQuickColorInputs::handleHexChanged()
+{
+ emit colorModified(QColor::fromString(m_hexInput->text()));
+}
+
+void QQuickColorInputs::handleRedChanged()
+{
+ QColor c = color();
+ c.setRed(qBound(0, m_redInput->text().toInt(), 255));
+ emit colorModified(c);
+}
+
+void QQuickColorInputs::handleGreenChanged()
+{
+ QColor c = color();
+ c.setGreen(qBound(0, m_greenInput->text().toInt(), 255));
+ emit colorModified(c);
+}
+
+void QQuickColorInputs::handleBlueChanged()
+{
+ QColor c = color();
+ c.setBlue(qBound(0, m_blueInput->text().toInt(), 255));
+ emit colorModified(c);
+}
+
+static QString s_percentage_pattern = QString::fromUtf8("^(\\d+)%?$");
+static QString s_degree_pattern = QString::fromUtf8("(\\d+)°?$");
+
+void QQuickColorInputs::handleHsvHueChanged()
+{
+ const QRegularExpression pattern(s_degree_pattern);
+ const auto match = pattern.match(m_hsvHueInput->text());
+ if (match.hasMatch()) {
+ const auto substr = match.captured(1);
+ const qreal input = static_cast<qreal>(qBound(0, substr.toInt(), 360)) / static_cast<qreal>(360);
+ emit colorModified(QColor::fromHsvF(input, hsvSaturation(), value(), alpha()));
+ }
+}
+
+void QQuickColorInputs::handleHslHueChanged()
+{
+ const QRegularExpression pattern(s_degree_pattern);
+ const auto match = pattern.match(m_hslHueInput->text());
+ if (match.hasMatch()) {
+ const auto substr = match.captured(1);
+ const qreal input = static_cast<qreal>(qBound(0, substr.toInt(), 360)) / static_cast<qreal>(360);
+ emit colorModified(QColor::fromHslF(input, hslSaturation(), lightness(), alpha()));
+ }
+}
+
+void QQuickColorInputs::handleHsvSaturationChanged()
+{
+ const QRegularExpression pattern(s_percentage_pattern);
+ const auto match = pattern.match(m_hsvSaturationInput->text());
+ if (match.hasMatch()) {
+ const auto substr = match.captured(1);
+ const qreal input = static_cast<qreal>(qBound(0, substr.toInt(), 100)) / static_cast<qreal>(100);
+ emit colorModified(QColor::fromHsvF(hue(), input, value(), alpha()));
+ }
+}
+
+void QQuickColorInputs::handleHslSaturationChanged()
+{
+ const QRegularExpression pattern(s_percentage_pattern);
+ const auto match = pattern.match(m_hslSaturationInput->text());
+ if (match.hasMatch()) {
+ const auto substr = match.captured(1);
+ const qreal input = static_cast<qreal>(qBound(0, substr.toInt(), 100)) / static_cast<qreal>(100);
+ emit colorModified(QColor::fromHslF(hue(), input, lightness(), alpha()));
+ }
+}
+
+void QQuickColorInputs::handleValueChanged()
+{
+ const QRegularExpression pattern(s_percentage_pattern);
+ const auto match = pattern.match(m_valueInput->text());
+ if (match.hasMatch()) {
+ const auto substr = match.captured(1);
+ const qreal input = static_cast<qreal>(qBound(0, substr.toInt(), 100)) / static_cast<qreal>(100);
+ emit colorModified(QColor::fromHsvF(hue(), hsvSaturation(), input, alpha()));
+ }
+}
+
+void QQuickColorInputs::handleLightnessChanged()
+{
+ const QRegularExpression pattern(s_percentage_pattern);
+ const auto match = pattern.match(m_lightnessInput->text());
+ if (match.hasMatch()) {
+ const auto substr = match.captured(1);
+ const qreal input = static_cast<qreal>(qBound(0, substr.toInt(), 100)) / static_cast<qreal>(100);
+ emit colorModified(QColor::fromHslF(hue(), hslSaturation(), input, alpha()));
+ }
+}
+
+void QQuickColorInputs::handleRgbAlphaChanged()
+{
+ handleAlphaChanged(m_rgbAlphaInput->text());
+}
+
+void QQuickColorInputs::handleHsvAlphaChanged()
+{
+ handleAlphaChanged(m_hsvAlphaInput->text());
+}
+
+void QQuickColorInputs::handleHslAlphaChanged()
+{
+ handleAlphaChanged(m_hslAlphaInput->text());
+}
+
+void QQuickColorInputs::handleAlphaChanged(const QString &input)
+{
+ const QRegularExpression pattern(s_percentage_pattern);
+ const auto match = pattern.match(input);
+ if (match.hasMatch()) {
+ QColor c = color();
+ const auto substr = match.captured(1);
+ const qreal input = static_cast<qreal>(qBound(0, substr.toInt(), 100)) / static_cast<qreal>(100);
+ c.setAlphaF(input);
+ emit colorModified(c);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h
new file mode 100644
index 0000000000..4827627d6f
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickcolorinputs_p.h
@@ -0,0 +1,171 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKCOLORINPUTS_P_H
+#define QQUICKCOLORINPUTS_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/qcolor.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuickTemplates2/private/qquickcombobox_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+#include "qquickcolordialogutils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickColorInputs : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(int red READ red NOTIFY colorChanged)
+ Q_PROPERTY(int green READ green NOTIFY colorChanged)
+ Q_PROPERTY(int blue READ blue NOTIFY colorChanged)
+ Q_PROPERTY(qreal hue READ hue NOTIFY colorChanged)
+ Q_PROPERTY(qreal hslSaturation READ hslSaturation NOTIFY colorChanged)
+ Q_PROPERTY(qreal hsvSaturation READ hsvSaturation NOTIFY colorChanged)
+ Q_PROPERTY(qreal value READ value NOTIFY colorChanged)
+ Q_PROPERTY(qreal lightness READ lightness NOTIFY colorChanged)
+ Q_PROPERTY(qreal alpha READ alpha NOTIFY colorChanged)
+ Q_PROPERTY(bool showAlpha READ showAlpha WRITE setShowAlpha NOTIFY showAlphaChanged)
+ Q_PROPERTY(QQuickTextInput *hexInput READ hexInput WRITE setHexInput NOTIFY hexInputChanged)
+ Q_PROPERTY(QQuickTextInput *redInput READ redInput WRITE setRedInput NOTIFY redInputChanged)
+ Q_PROPERTY(QQuickTextInput *greenInput READ greenInput WRITE setGreenInput NOTIFY greenInputChanged)
+ Q_PROPERTY(QQuickTextInput *blueInput READ blueInput WRITE setBlueInput NOTIFY blueInputChanged)
+ Q_PROPERTY(QQuickTextInput *hsvHueInput READ hsvHueInput WRITE setHsvHueInput NOTIFY hsvHueInputChanged)
+ Q_PROPERTY(QQuickTextInput *hslHueInput READ hslHueInput WRITE setHslHueInput NOTIFY hslHueInputChanged)
+ Q_PROPERTY(QQuickTextInput *hsvSaturationInput READ hsvSaturationInput WRITE setHsvSaturationInput NOTIFY hsvSaturationInputChanged)
+ Q_PROPERTY(QQuickTextInput *hslSaturationInput READ hslSaturationInput WRITE setHslSaturationInput NOTIFY hslSaturationInputChanged)
+ Q_PROPERTY(QQuickTextInput *valueInput READ valueInput WRITE setValueInput NOTIFY valueInputChanged)
+ Q_PROPERTY(QQuickTextInput *lightnessInput READ lightnessInput WRITE setLightnessInput NOTIFY lightnessInputChanged)
+ Q_PROPERTY(QQuickTextInput *rgbAlphaInput READ rgbAlphaInput WRITE setRgbAlphaInput NOTIFY rgbAlphaInputChanged)
+ Q_PROPERTY(QQuickTextInput *hsvAlphaInput READ hsvAlphaInput WRITE setHsvAlphaInput NOTIFY hsvAlphaInputChanged)
+ Q_PROPERTY(QQuickTextInput *hslAlphaInput READ hslAlphaInput WRITE setHslAlphaInput NOTIFY hslAlphaInputChanged)
+ QML_NAMED_ELEMENT(ColorInputsImpl)
+
+public:
+ explicit QQuickColorInputs();
+
+ QColor color() const;
+ void setColor(const QColor &c);
+ int red() const;
+ int green() const;
+ int blue() const;
+ qreal alpha() const;
+ qreal hue() const;
+ qreal hslSaturation() const;
+ qreal hsvSaturation() const;
+ qreal value() const;
+ qreal lightness() const;
+
+ bool showAlpha() const;
+ void setShowAlpha(bool showAlpha);
+
+ QQuickTextInput *hexInput() const;
+ void setHexInput(QQuickTextInput *hexInput);
+
+ QQuickTextInput *redInput() const;
+ void setRedInput(QQuickTextInput *redInput);
+
+ QQuickTextInput *greenInput() const;
+ void setGreenInput(QQuickTextInput *greenInput);
+
+ QQuickTextInput *blueInput() const;
+ void setBlueInput(QQuickTextInput *blueInput);
+
+ QQuickTextInput *hsvHueInput() const;
+ void setHsvHueInput(QQuickTextInput *hsvHueInput);
+
+ QQuickTextInput *hslHueInput() const;
+ void setHslHueInput(QQuickTextInput *hslHueInput);
+
+ QQuickTextInput *hsvSaturationInput() const;
+ void setHsvSaturationInput(QQuickTextInput *hsvSaturationInput);
+
+ QQuickTextInput *hslSaturationInput() const;
+ void setHslSaturationInput(QQuickTextInput *hslSaturationInput);
+
+ QQuickTextInput *valueInput() const;
+ void setValueInput(QQuickTextInput *valueInput);
+
+ QQuickTextInput *lightnessInput() const;
+ void setLightnessInput(QQuickTextInput *lightnessInput);
+
+ QQuickTextInput *rgbAlphaInput() const;
+ void setRgbAlphaInput(QQuickTextInput *alphaInput);
+
+ QQuickTextInput *hsvAlphaInput() const;
+ void setHsvAlphaInput(QQuickTextInput *alphaInput);
+
+ QQuickTextInput *hslAlphaInput() const;
+ void setHslAlphaInput(QQuickTextInput *alphaInput);
+
+Q_SIGNALS:
+ void colorChanged(const QColor &c);
+ void colorModified(const QColor &c);
+ void hslChanged();
+ void showAlphaChanged(bool);
+ void hexInputChanged();
+ void redInputChanged();
+ void greenInputChanged();
+ void blueInputChanged();
+ void hsvHueInputChanged();
+ void hslHueInputChanged();
+ void hsvSaturationInputChanged();
+ void hslSaturationInputChanged();
+ void valueInputChanged();
+ void lightnessInputChanged();
+ void rgbAlphaInputChanged();
+ void hsvAlphaInputChanged();
+ void hslAlphaInputChanged();
+
+private:
+ void handleHexChanged();
+ void handleRedChanged();
+ void handleGreenChanged();
+ void handleBlueChanged();
+ void handleHsvHueChanged();
+ void handleHslHueChanged();
+ void handleHueChanged(const QString &input);
+ void handleHsvSaturationChanged();
+ void handleHslSaturationChanged();
+ void handleSaturationChanged(const QString &input);
+ void handleValueChanged();
+ void handleLightnessChanged();
+ void handleRgbAlphaChanged();
+ void handleHsvAlphaChanged();
+ void handleHslAlphaChanged();
+ void handleAlphaChanged(const QString &input);
+
+ QPointer<QQuickTextInput> m_hexInput;
+ QPointer<QQuickTextInput> m_redInput;
+ QPointer<QQuickTextInput> m_greenInput;
+ QPointer<QQuickTextInput> m_blueInput;
+ QPointer<QQuickTextInput> m_hsvHueInput;
+ QPointer<QQuickTextInput> m_hslHueInput;
+ QPointer<QQuickTextInput> m_hsvSaturationInput;
+ QPointer<QQuickTextInput> m_hslSaturationInput;
+ QPointer<QQuickTextInput> m_valueInput;
+ QPointer<QQuickTextInput> m_lightnessInput;
+ QPointer<QQuickTextInput> m_rgbAlphaInput;
+ QPointer<QQuickTextInput> m_hsvAlphaInput;
+ QPointer<QQuickTextInput> m_hslAlphaInput;
+ HSVA m_hsva;
+ bool m_showAlpha = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKCOLORINPUTS_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
new file mode 100644
index 0000000000..9a63fb0718
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
@@ -0,0 +1,55 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickdialogimplfactory_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qquickplatformfiledialog_p.h"
+#include "qquickplatformfolderdialog_p.h"
+#include "qquickplatformfontdialog_p.h"
+#include "qquickplatformcolordialog_p.h"
+#include "qquickplatformmessagedialog_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ Creates concrete QML-based dialogs.
+*/
+
+Q_LOGGING_CATEGORY(lcQuickDialogImplFactory, "qt.quick.dialogs.quickdialogimplfactory")
+
+std::unique_ptr<QPlatformDialogHelper> QQuickDialogImplFactory::createPlatformDialogHelper(QQuickDialogType type, QObject *parent)
+{
+ std::unique_ptr<QPlatformDialogHelper> dialogHelper;
+ switch (type) {
+ case QQuickDialogType::ColorDialog: {
+ dialogHelper.reset(new QQuickPlatformColorDialog(parent));
+ break;
+ }
+ case QQuickDialogType::FileDialog: {
+ dialogHelper.reset(new QQuickPlatformFileDialog(parent));
+ break;
+ }
+ case QQuickDialogType::FolderDialog: {
+ dialogHelper.reset(new QQuickPlatformFolderDialog(parent));
+ break;
+ }
+ case QQuickDialogType::FontDialog: {
+ dialogHelper.reset(new QQuickPlatformFontDialog(parent));
+ break;
+ }
+ case QQuickDialogType::MessageDialog: {
+ dialogHelper.reset(new QQuickPlatformMessageDialog(parent));
+ break;
+ }
+ default:
+ break;
+ }
+
+ return dialogHelper;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h
new file mode 100644
index 0000000000..5072c9e1cb
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory_p.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 <memory>
+
+#include <QtCore/qobject.h>
+#include <QtGui/qpa/qplatformdialoghelper.h>
+#include <QtQuickDialogs2Utils/private/qquickdialogtype_p.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickDialogImplFactory
+{
+public:
+ static std::unique_ptr<QPlatformDialogHelper> createPlatformDialogHelper(QQuickDialogType type, QObject *parent);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKQMLDIALOGFACTORY_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp
new file mode 100644
index 0000000000..71b1fccbc5
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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"
+#include "qquickfolderdialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileDialogDelegatePrivate : public QQuickItemDelegatePrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQuickFileDialogDelegate)
+
+ void highlightFile();
+ void chooseFile();
+
+ bool acceptKeyClick(Qt::Key key) const override;
+
+ QQuickDialog *dialog = nullptr;
+ QQuickFileDialogImpl *fileDialog = nullptr;
+ QQuickFolderDialogImpl *folderDialog = 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);
+ if (fileDialog)
+ fileDialog->setSelectedFile(file);
+ else if (folderDialog)
+ folderDialog->setSelectedFolder(file);
+ }
+}
+
+void QQuickFileDialogDelegatePrivate::chooseFile()
+{
+ const QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(file));
+ if (fileInfo.isDir()) {
+ // If it's a directory, navigate to it.
+ if (fileDialog)
+ fileDialog->setCurrentFolder(file);
+ else
+ folderDialog->setCurrentFolder(file);
+ } else {
+ Q_ASSERT(fileDialog);
+ // 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);
+}
+
+QQuickDialog *QQuickFileDialogDelegate::dialog() const
+{
+ Q_D(const QQuickFileDialogDelegate);
+ return d->dialog;
+}
+
+void QQuickFileDialogDelegate::setDialog(QQuickDialog *dialog)
+{
+ Q_D(QQuickFileDialogDelegate);
+ if (dialog == d->dialog)
+ return;
+
+ d->dialog = dialog;
+ d->fileDialog = qobject_cast<QQuickFileDialogImpl*>(dialog);
+ d->folderDialog = qobject_cast<QQuickFolderDialogImpl*>(dialog);
+ emit dialogChanged();
+}
+
+QUrl QQuickFileDialogDelegate::file() const
+{
+ Q_D(const QQuickFileDialogDelegate);
+ return d->file;
+}
+
+void QQuickFileDialogDelegate::setFile(const QUrl &file)
+{
+ Q_D(QQuickFileDialogDelegate);
+ QUrl adjustedFile = file;
+#ifdef Q_OS_WIN32
+ // Work around QTBUG-99105 (FolderListModel uses lowercase drive letter).
+ QString path = adjustedFile.path();
+ const int driveColonIndex = path.indexOf(QLatin1Char(':'));
+ if (driveColonIndex == 2) {
+ path.replace(1, 1, path.at(1).toUpper());
+ adjustedFile.setPath(path);
+ }
+#endif
+ if (adjustedFile == d->file)
+ return;
+
+ d->file = adjustedFile;
+ 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/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h
new file mode 100644
index 0000000000..36804497d7
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 QQuickDialog;
+class QQuickFileDialogDelegatePrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogDelegate : public QQuickItemDelegate
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickDialog *dialog READ dialog WRITE setDialog NOTIFY dialogChanged)
+ 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);
+
+ QQuickDialog *dialog() const;
+ void setDialog(QQuickDialog *dialog);
+
+ QUrl file() const;
+ void setFile(const QUrl &file);
+
+Q_SIGNALS:
+ void dialogChanged();
+ 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/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
new file mode 100644
index 0000000000..72f2ea25cf
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
@@ -0,0 +1,758 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickfiledialogimpl_p.h"
+#include "qquickfiledialogimpl_p_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQuick/private/qquickitemview_p_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupitem_p_p.h>
+#include <QtQuickControls2Impl/private/qquickplatformtheme_p.h>
+#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_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(lcSelectedFile, "qt.quick.dialogs.quickfiledialogimpl.selectedFile")
+Q_LOGGING_CATEGORY(lcUpdateSelectedFile, "qt.quick.dialogs.quickfiledialogimpl.updateSelectedFile")
+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")
+Q_LOGGING_CATEGORY(lcAttachedCurrentIndex, "qt.quick.dialogs.quickfiledialogimplattached.currentIndex")
+
+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(!selectedFile.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::updateSelectedFile(const QString &oldFolderPath)
+{
+ Q_Q(QQuickFileDialogImpl);
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ if (!attached || !attached->fileDialogListView())
+ return;
+
+ qCDebug(lcUpdateSelectedFile) << "updateSelectedFile called with oldFolderPath" << oldFolderPath;
+
+ QString newSelectedFilePath;
+ int newSelectedFileIndex = -1;
+ const QString newFolderPath = QQmlFile::urlToLocalFileOrQrc(currentFolder);
+ if (!oldFolderPath.isEmpty() && !newFolderPath.isEmpty()) {
+ // TODO: Add another platform theme hint for this behavior too, as e.g. macOS
+ // doesn't do it this way.
+ // If the user went up a directory (or several), we should set
+ // selectedFile 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 selectedFile 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);
+ newSelectedFilePath = 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" << newSelectedFilePath << "doesn't exist; can't get a file entry list for it";
+ return;
+ }
+
+ const QFileInfoList filesInNewDir = fileList(newFolderDir);
+ const QFileInfo newSelectedFileInfo(newSelectedFilePath);
+ newSelectedFileIndex = filesInNewDir.indexOf(newSelectedFileInfo);
+ }
+ }
+
+ static const bool preselectFirstFile = []() {
+ const QVariant envVar = qEnvironmentVariable("QT_QUICK_DIALOGS_PRESELECT_FIRST_FILE");
+ if (envVar.isValid() && envVar.canConvert<bool>())
+ return envVar.toBool();
+ return QGuiApplicationPrivate::platformTheme()->themeHint(
+ QPlatformTheme::PreselectFirstFileInDirectory).toBool();
+ }();
+
+ if (preselectFirstFile && newSelectedFilePath.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...
+ const QDir newFolderDir(newFolderPath);
+ if (newFolderDir.exists()) {
+ if (!cachedFileList.isEmpty()) {
+ newSelectedFilePath = cachedFileList.first().absoluteFilePath();
+ newSelectedFileIndex = 0;
+ }
+ }
+ }
+
+ const QUrl newSelectedFileUrl = QUrl::fromLocalFile(newSelectedFilePath);
+ qCDebug(lcUpdateSelectedFile).nospace() << "updateSelectedFile is setting selectedFile to " << newSelectedFileUrl
+ << ", newSelectedFileIndex is " << newSelectedFileIndex;
+ q->setSelectedFile(newSelectedFileUrl);
+ // If the index is -1, there are no files in the directory, and so fileDialogListView's
+ // currentIndex will already be -1.
+ if (newSelectedFileIndex != -1)
+ tryUpdateFileDialogListViewCurrentIndex(newSelectedFileIndex);
+}
+
+QDir::SortFlags QQuickFileDialogImplPrivate::fileListSortFlags()
+{
+ QDir::SortFlags sortFlags = QDir::IgnoreCase;
+ if (QQuickPlatformTheme::getThemeHint(QPlatformTheme::ShowDirectoriesFirst).toBool())
+ sortFlags.setFlag(QDir::DirsFirst);
+ return sortFlags;
+}
+
+QFileInfoList QQuickFileDialogImplPrivate::fileList(const QDir &dir)
+{
+ return dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, fileListSortFlags());
+}
+
+void QQuickFileDialogImplPrivate::setFileDialogListViewCurrentIndex(int newCurrentIndex)
+{
+ qCDebug(lcSelectedFile) << "setting fileDialogListView's currentIndex to" << newCurrentIndex;
+
+ // We block signals from ListView because we don't want fileDialogListViewCurrentIndexChanged
+ // to be called, as the file it gets from the delegate will not be up-to-date (but most
+ // importantly because we already just set the selected file).
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ const QSignalBlocker blocker(attached->fileDialogListView());
+ attached->fileDialogListView()->setCurrentIndex(newCurrentIndex);
+ attached->fileDialogListView()->positionViewAtIndex(newCurrentIndex, QQuickListView::Center);
+ if (QQuickItem *currentItem = attached->fileDialogListView()->currentItem())
+ currentItem->forceActiveFocus();
+}
+
+/*!
+ \internal
+
+ Tries to set the currentIndex of fileDialogListView to \a newCurrentIndex and gives
+ focus to the current item.
+*/
+void QQuickFileDialogImplPrivate::tryUpdateFileDialogListViewCurrentIndex(int newCurrentIndex)
+{
+ qCDebug(lcSelectedFile) << "tryUpdateFileDialogListViewCurrentIndex called with newCurrentIndex" << newCurrentIndex;
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ Q_ASSERT(attached);
+ Q_ASSERT(attached->fileDialogListView());
+
+ // We were likely trying to set an index for a file that the ListView hadn't loaded yet.
+ // We need to wait until the ListView has loaded all expected items, but since we have no
+ // efficient way of verifying that, we just check that the count is as expected.
+ if (newCurrentIndex != -1 && newCurrentIndex >= attached->fileDialogListView()->count()) {
+ qCDebug(lcSelectedFile) << "- trying to set currentIndex to" << newCurrentIndex
+ << "but fileDialogListView only has" << attached->fileDialogListView()->count()
+ << "items; setting pendingCurrentIndexToSet to" << newCurrentIndex;
+ pendingCurrentIndexToSet = newCurrentIndex;
+ QObjectPrivate::connect(attached->fileDialogListView(), &QQuickItemView::countChanged,
+ this, &QQuickFileDialogImplPrivate::fileDialogListViewCountChanged, Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection));
+ return;
+ }
+
+ setFileDialogListViewCurrentIndex(newCurrentIndex);
+}
+
+void QQuickFileDialogImplPrivate::fileDialogListViewCountChanged()
+{
+ QQuickFileDialogImplAttached *attached = attachedOrWarn();
+ qCDebug(lcSelectedFile) << "fileDialogListView count changed to" << attached->fileDialogListView()->count();
+
+ if (pendingCurrentIndexToSet != -1 && pendingCurrentIndexToSet < attached->fileDialogListView()->count()) {
+ // The view now has all of the items we expect it to, so we can set
+ // its currentIndex back to the selected file.
+ qCDebug(lcSelectedFile) << "- ListView has expected count;"
+ << "applying pending fileDialogListView currentIndex" << pendingCurrentIndexToSet;
+
+ QObjectPrivate::disconnect(attached->fileDialogListView(), &QQuickItemView::countChanged,
+ this, &QQuickFileDialogImplPrivate::fileDialogListViewCountChanged);
+ setFileDialogListViewCurrentIndex(pendingCurrentIndexToSet);
+ pendingCurrentIndexToSet = -1;
+ qCDebug(lcSelectedFile) << "- reset pendingCurrentIndexToSet to -1";
+ } else {
+ qCDebug(lcSelectedFile) << "- ListView doesn't yet have expected count of" << cachedFileList.size();
+ }
+}
+
+void QQuickFileDialogImplPrivate::handleAccept()
+{
+ // Let handleClick take care of calling accept().
+}
+
+void QQuickFileDialogImplPrivate::handleClick(QQuickAbstractButton *button)
+{
+ Q_Q(QQuickFileDialogImpl);
+ if (buttonRole(button) == QPlatformDialogHelper::AcceptRole && selectedFile.isValid()) {
+ // The "Open" button was clicked, so we need to set the file to the current file, if any.
+ const QFileInfo fileInfo(selectedFile.toLocalFile());
+ if (fileInfo.isDir()) {
+ // If it's a directory, navigate to it.
+ q->setCurrentFolder(selectedFile);
+ // 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(selectedFile);
+ q->accept();
+ QQuickDialogPrivate::handleClick(button);
+ emit q->fileSelected(selectedFile);
+ }
+ }
+}
+
+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, SetReason setReason)
+{
+ Q_D(QQuickFileDialogImpl);
+ qCDebug(lcCurrentFolder).nospace() << "setCurrentFolder called with " << currentFolder
+ << " (old currentFolder is " << d->currentFolder << ")";
+
+ // As we would otherwise get the file list from scratch in a couple of places,
+ // just get it once and cache it.
+ // We need to cache it before the equality check, otherwise opening the dialog
+ // several times in the same directory wouldn't update the cache.
+ if (!currentFolder.isEmpty())
+ d->cachedFileList = d->fileList(QQmlFile::urlToLocalFileOrQrc(currentFolder));
+ else
+ d->cachedFileList.clear();
+ qCDebug(lcCurrentFolder) << "- cachedFileList size is now " << d->cachedFileList.size();
+
+ if (currentFolder == d->currentFolder)
+ return;
+
+ const QString oldFolderPath = QQmlFile::urlToLocalFileOrQrc(d->currentFolder);
+
+ d->currentFolder = currentFolder;
+ // Don't update the selectedFile if it's an Internal set, as that
+ // means that the user just set selectedFile, and we're being called as a result of that.
+ if (setReason == SetReason::External) {
+ // Since the directory changed, the old file can no longer be selected.
+ d->updateSelectedFile(oldFolderPath);
+ }
+ emit currentFolderChanged(d->currentFolder);
+}
+
+QUrl QQuickFileDialogImpl::selectedFile() const
+{
+ Q_D(const QQuickFileDialogImpl);
+ return d->selectedFile;
+}
+
+/*!
+ \internal
+
+ This is mostly called as a result of user interaction, but is also
+ called (indirectly) by QQuickFileDialog::onShow when the user set an initial
+ selectedFile.
+*/
+void QQuickFileDialogImpl::setSelectedFile(const QUrl &selectedFile)
+{
+ qCDebug(lcSelectedFile) << "setSelectedFile called with" << selectedFile;
+ Q_D(QQuickFileDialogImpl);
+ if (selectedFile == d->selectedFile)
+ return;
+
+ d->selectedFile = selectedFile;
+ d->updateEnabled();
+ emit selectedFileChanged(d->selectedFile);
+}
+
+/*!
+ \internal
+
+ Called when showing the FileDialog each time, so long as
+ QFileDialogOptions::initiallySelectedFiles is not empty.
+*/
+void QQuickFileDialogImpl::setInitialCurrentFolderAndSelectedFile(const QUrl &file)
+{
+ Q_D(QQuickFileDialogImpl);
+ const QUrl fileDirUrl = QUrl::fromLocalFile(QFileInfo(file.toLocalFile()).dir().absolutePath());
+ const bool currentFolderChanged = d->currentFolder != fileDirUrl;
+ qCDebug(lcSelectedFile) << "setting initial currentFolder to" << fileDirUrl << "and selectedFile to" << file;
+ setCurrentFolder(fileDirUrl, QQuickFileDialogImpl::SetReason::Internal);
+ setSelectedFile(file);
+ d->setCurrentIndexToInitiallySelectedFile = true;
+
+ // If the currentFolder didn't change, the FolderListModel won't change and
+ // neither will the ListView. This means that setFileDialogListViewCurrentIndex
+ // will never get called and the currentIndex will not reflect selectedFile.
+ // We need to account for that here.
+ if (!currentFolderChanged) {
+ const QFileInfo newSelectedFileInfo(d->selectedFile.toLocalFile());
+ const int indexOfSelectedFileInFileDialogListView = d->cachedFileList.indexOf(newSelectedFileInfo);
+ d->tryUpdateFileDialogListViewCurrentIndex(indexOfSelectedFileInFileDialogListView);
+ }
+}
+
+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());
+
+ if (auto attached = d->attachedOrWarn()) {
+ const bool isSaveMode = d->options->fileMode() == QFileDialogOptions::AnyFile;
+ attached->fileNameLabel()->setVisible(isSaveMode);
+ attached->fileNameTextField()->setVisible(isSaveMode);
+ }
+ }
+}
+
+/*!
+ \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);
+}
+
+QString QQuickFileDialogImpl::fileName() const
+{
+ return selectedFile().fileName();
+}
+void QQuickFileDialogImpl::setFileName(const QString &fileName)
+{
+ const QString previous = selectedFile().fileName();
+ if (previous == fileName)
+ return;
+
+ setSelectedFile(QUrl(currentFolder().path() + u'/' + fileName));
+}
+
+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, false));
+ 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;
+
+ const QQuickItemViewPrivate::MovementReason moveReason = QQuickItemViewPrivate::get(fileDialogListView)->moveReason;
+ qCDebug(lcAttachedCurrentIndex).nospace() << "fileDialogListView currentIndex changed to " << fileDialogListView->currentIndex()
+ << " with moveReason " << moveReason
+ << "; the file at that index is " << fileDialogDelegate->file();
+
+ // Only update selectedFile if the currentIndex changed as a result of user interaction;
+ // things like model changes (i.e. QQuickItemViewPrivate::applyModelChanges() calling
+ // QQuickItemViewPrivate::updateCurrent as a result of us changing the directory on the FolderListModel)
+ // shouldn't cause the selectedFile to change.
+ auto fileDialogImplPrivate = QQuickFileDialogImplPrivate::get(fileDialogImpl);
+ if (moveReason != QQuickItemViewPrivate::Other) {
+ fileDialogImpl->setSelectedFile(fileDialogDelegate->file());
+ } else if (fileDialogImplPrivate->setCurrentIndexToInitiallySelectedFile) {
+ // When setting selectedFile before opening the FileDialog,
+ // we need to ensure that the currentIndex is correct, because the initial change
+ // in directory will cause the underyling FolderListModel to change its folder property,
+ // which in turn resets the fileDialogListView's currentIndex to 0.
+ const QFileInfo newSelectedFileInfo(fileDialogImplPrivate->selectedFile.toLocalFile());
+ const int indexOfSelectedFileInFileDialogListView = fileDialogImplPrivate->cachedFileList.indexOf(newSelectedFileInfo);
+ fileDialogImplPrivate->tryUpdateFileDialogListViewCurrentIndex(indexOfSelectedFileInFileDialogListView);
+ fileDialogImplPrivate->setCurrentIndexToInitiallySelectedFile = false;
+ }
+}
+
+void QQuickFileDialogImplAttachedPrivate::fileNameChangedByUser()
+{
+ auto fileDialogImpl = qobject_cast<QQuickFileDialogImpl *>(parent);
+ if (!fileDialogImpl)
+ return;
+
+ fileDialogImpl->setFileName(fileNameTextField->text());
+}
+
+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();
+}
+
+QQuickLabel *QQuickFileDialogImplAttached::fileNameLabel() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->fileNameLabel;
+}
+
+void QQuickFileDialogImplAttached::setFileNameLabel(QQuickLabel *fileNameLabel)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ if (fileNameLabel == d->fileNameLabel)
+ return;
+
+ d->fileNameLabel = fileNameLabel;
+
+ emit fileNameLabelChanged();
+}
+
+QQuickTextField *QQuickFileDialogImplAttached::fileNameTextField() const
+{
+ Q_D(const QQuickFileDialogImplAttached);
+ return d->fileNameTextField;
+}
+
+void QQuickFileDialogImplAttached::setFileNameTextField(QQuickTextField *fileNameTextField)
+{
+ Q_D(QQuickFileDialogImplAttached);
+ if (fileNameTextField == d->fileNameTextField)
+ return;
+
+ if (d->fileNameTextField)
+ QObjectPrivate::disconnect(d->fileNameTextField, &QQuickTextField::editingFinished,
+ d, &QQuickFileDialogImplAttachedPrivate::fileNameChangedByUser);
+
+ d->fileNameTextField = fileNameTextField;
+
+ if (d->fileNameTextField)
+ QObjectPrivate::connect(d->fileNameTextField, &QQuickTextField::editingFinished,
+ d, &QQuickFileDialogImplAttachedPrivate::fileNameChangedByUser);
+
+ emit fileNameTextFieldChanged();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickfiledialogimpl_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h
new file mode 100644
index 0000000000..4c64c5e4dd
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h
@@ -0,0 +1,156 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 QQuickTextField;
+class QQuickLabel;
+
+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(QStringList nameFilters READ nameFilters NOTIFY nameFiltersChanged FINAL)
+ Q_PROPERTY(QQuickFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT)
+ Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY selectedFileChanged FINAL)
+ 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);
+
+ enum class SetReason {
+ // Either user interaction or e.g. a change in ListView's currentIndex after changing its model.
+ External,
+ // As a result of the user setting an initial selectedFile.
+ Internal
+ };
+
+ QUrl currentFolder() const;
+ void setCurrentFolder(const QUrl &currentFolder, SetReason setReason = SetReason::External);
+
+ QUrl selectedFile() const;
+ void setSelectedFile(const QUrl &file);
+ void setInitialCurrentFolderAndSelectedFile(const QUrl &file);
+
+ 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);
+
+ QString fileName() const;
+ void setFileName(const QString &fileName);
+
+public Q_SLOTS:
+ void selectNameFilter(const QString &filter);
+
+Q_SIGNALS:
+ void currentFolderChanged(const QUrl &folderUrl);
+ void selectedFileChanged(const QUrl &selectedFileUrl);
+ 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_PROPERTY(QQuickLabel *fileNameLabel READ fileNameLabel WRITE setFileNameLabel NOTIFY fileNameLabelChanged FINAL)
+ Q_PROPERTY(QQuickTextField *fileNameTextField READ fileNameTextField WRITE setFileNameTextField NOTIFY fileNameTextFieldChanged FINAL)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickcombobox_p.h>)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquicktextfield_p.h>)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquicklabel_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);
+
+ QQuickLabel *fileNameLabel() const;
+ void setFileNameLabel(QQuickLabel *fileNameLabel);
+
+ QQuickTextField *fileNameTextField() const;
+ void setFileNameTextField(QQuickTextField *fileNameTextField);
+
+Q_SIGNALS:
+ void buttonBoxChanged();
+ void nameFiltersComboBoxChanged();
+ void fileDialogListViewChanged();
+ void breadcrumbBarChanged();
+ void fileNameLabelChanged();
+ void fileNameTextFieldChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickFileDialogImplAttached)
+ Q_DECLARE_PRIVATE(QQuickFileDialogImplAttached)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFileDialogImpl)
+
+#endif // QQUICKFILEDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h
new file mode 100644
index 0000000000..6f3cc55e86
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 <QtQuickTemplates2/private/qquicklabel_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
+
+#include "qquickfiledialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileNameFilter;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImplPrivate : public QQuickDialogPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQuickFileDialogImpl)
+
+ QQuickFileDialogImplPrivate();
+
+ static QQuickFileDialogImplPrivate *get(QQuickFileDialogImpl *dialog)
+ {
+ return dialog->d_func();
+ }
+
+ QQuickFileDialogImplAttached *attachedOrWarn();
+
+ void setNameFilters(const QStringList &filters);
+
+ void updateEnabled();
+ void updateSelectedFile(const QString &oldFolderPath);
+ static QDir::SortFlags fileListSortFlags();
+ static QFileInfoList fileList(const QDir &dir);
+ void setFileDialogListViewCurrentIndex(int newCurrentIndex);
+ void tryUpdateFileDialogListViewCurrentIndex(int newCurrentIndex);
+ void fileDialogListViewCountChanged();
+
+ void handleAccept() override;
+ void handleClick(QQuickAbstractButton *button) override;
+
+ QSharedPointer<QFileDialogOptions> options;
+ QUrl currentFolder;
+ QUrl selectedFile;
+ QStringList nameFilters;
+ mutable QQuickFileNameFilter *selectedNameFilter = nullptr;
+ QString acceptLabel;
+ QString rejectLabel;
+ bool setCurrentIndexToInitiallySelectedFile = false;
+ QFileInfoList cachedFileList;
+ int pendingCurrentIndexToSet = -1;
+};
+
+class QQuickFileDialogImplAttachedPrivate : public QObjectPrivate
+{
+ void nameFiltersComboBoxItemActivated(int index);
+ void fileDialogListViewCurrentIndexChanged();
+ void fileNameChangedByUser();
+
+public:
+ Q_DECLARE_PUBLIC(QQuickFileDialogImplAttached)
+
+ QPointer<QQuickDialogButtonBox> buttonBox;
+ QPointer<QQuickComboBox> nameFiltersComboBox;
+ QPointer<QQuickListView> fileDialogListView;
+ QPointer<QQuickFolderBreadcrumbBar> breadcrumbBar;
+ QPointer<QQuickLabel> fileNameLabel;
+ QPointer<QQuickTextField> fileNameTextField;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFILEDIALOG_P_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar.cpp
new file mode 100644
index 0000000000..a78833f470
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar.cpp
@@ -0,0 +1,777 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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"
+#include "qquickfolderdialogimpl_p.h"
+#include "qquickfolderdialogimpl_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 *context = 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 (!context)
+ context = qmlContext(q);
+
+ // If we have initial properties we assume that all necessary information is passed via
+ // initial properties.
+ if (!component->isBound() && initialProperties.isEmpty()) {
+ context = new QQmlContext(context, 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 = [this, q](){
+ folderPaths.clear();
+ while (q->count() > 0)
+ q->removeItem(q->itemAt(0));
+ };
+
+ qCDebug(lcDelegates) << "- getting paths for directory" << dialogFolder();
+ folderPaths = crumbPathsForFolder(dialogFolder());
+
+ 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;
+ setDialogFolder(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(dialogFolder()));
+ dir.cdUp();
+ setDialogFolder(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()
+{
+ textField->setText(QQmlFile::urlToLocalFileOrQrc(dialogFolder()));
+
+ qCDebug(lcTextInput).nospace() << "text field visibility was " << textField->isVisible()
+ << "; setting it to " << !textField->isVisible();
+ textField->setVisible(!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.
+ if (auto fileDialog = asFileDialog()) {
+ auto fileDialogPrivate = QQuickFileDialogImplPrivate::get(fileDialog);
+ fileDialogPrivate->updateEnabled();
+ } else if (auto folderDialog = asFolderDialog()) {
+ auto folderDialogPrivate = QQuickFolderDialogImplPrivate::get(folderDialog);
+ folderDialogPrivate->updateEnabled();
+ }
+}
+
+void QQuickFolderBreadcrumbBarPrivate::textFieldAccepted()
+{
+ const QUrl fileUrl = QUrl::fromLocalFile(textField->text());
+ const auto fileDialog = asFileDialog();
+ const bool mustExist = fileDialog ? fileDialog->options()->acceptMode() != QFileDialogOptions::AcceptSave : true;
+ 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";
+ setDialogFolder(fileUrl);
+ } else if (!enteredPathIsDir && (enteredPathExists || !mustExist)) {
+ qCDebug(lcTextInput) << "path entered is a file; setting file and calling accept()";
+ if (isFileDialog()) {
+ auto fileDialog = asFileDialog();
+ fileDialog->setSelectedFile(fileUrl);
+ fileDialog->accept();
+ } else {
+ setDialogFolder(fileUrl);
+ }
+ } else {
+ qCDebug(lcTextInput) << "path entered is not valid; not setting file/folder";
+ }
+
+ // If the enter key was pressed and the dialog closed, the text input will lose
+ // active focus, and textFieldActiveFocusChanged() will toggle its visibility.
+ // We should only toggle visibility if the dialog is actually closed, otherwise
+ // we'll end up toggling twice, and the text input will be visible the next time
+ // the dialog is opened.
+ if (dialog->isVisible())
+ toggleTextFieldVisibility();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::textFieldVisibleChanged()
+{
+ qCDebug(lcShortcuts) << "text field was either hidden or shown";
+
+ if (textField && textField->isVisible())
+ handleTextFieldShown();
+ else
+ handleTextFieldHidden();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::textFieldActiveFocusChanged()
+{
+ qCDebug(lcTextInput) << "text field activeFocus changed to" << textField->hasActiveFocus();
+
+ // 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)
+ if (editPathToggleShortcutId == 0)
+ return;
+
+ qCDebug(lcShortcuts) << "text field was shown; ungrabbing edit path shortcut";
+ ungrabEditPathShortcut();
+#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 edit path shortcut";
+
+ if (editPathToggleShortcutId == 0) {
+ editPathToggleShortcutId = appPrivate->shortcutMap.addShortcut(
+ q, Qt::CTRL | Qt::Key_L, Qt::WindowShortcut, QQuickShortcutContext::matcher);
+ }
+
+ qCDebug(lcShortcuts).nospace() << "... editPathToggleShortcutId=" << editPathToggleShortcutId;
+#endif
+}
+
+void QQuickFolderBreadcrumbBarPrivate::ungrabEditPathShortcut()
+{
+#if QT_CONFIG(shortcut)
+ Q_Q(QQuickFolderBreadcrumbBar);
+ QGuiApplicationPrivate *appPrivate = QGuiApplicationPrivate::instance();
+ if (editPathToggleShortcutId != 0) {
+ appPrivate->shortcutMap.removeShortcut(editPathToggleShortcutId, q);
+ editPathToggleShortcutId = 0;
+ }
+#endif
+}
+
+QQuickFileDialogImpl *QQuickFolderBreadcrumbBarPrivate::asFileDialog() const
+{
+ return qobject_cast<QQuickFileDialogImpl*>(dialog);
+}
+
+QQuickFolderDialogImpl *QQuickFolderBreadcrumbBarPrivate::asFolderDialog() const
+{
+ return qobject_cast<QQuickFolderDialogImpl*>(dialog);
+}
+
+bool QQuickFolderBreadcrumbBarPrivate::isFileDialog() const
+{
+ return asFileDialog();
+}
+
+QUrl QQuickFolderBreadcrumbBarPrivate::dialogFolder() const
+{
+ return dialog->property("currentFolder").toUrl();
+}
+
+void QQuickFolderBreadcrumbBarPrivate::setDialogFolder(const QUrl &folder)
+{
+ Q_Q(QQuickFolderBreadcrumbBar);
+ if (!dialog->setProperty("currentFolder", folder))
+ qmlWarning(q) << "Failed to set currentFolder property of dialog" << dialog->objectName() << "to" << folder;
+}
+
+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;
+}
+
+QQuickDialog *QQuickFolderBreadcrumbBar::dialog() const
+{
+ Q_D(const QQuickFolderBreadcrumbBar);
+ return d->dialog;
+}
+
+void QQuickFolderBreadcrumbBar::setDialog(QQuickDialog *dialog)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+ if (dialog == d->dialog)
+ return;
+
+ if (d->dialog) {
+ if (auto fileDialog = d->asFileDialog()) {
+ // TODO: rename impl's currentFolder too, when name is decided
+ QObjectPrivate::disconnect(fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
+ } else if (auto folderDialog = d->asFolderDialog()) {
+ QObjectPrivate::disconnect(folderDialog, &QQuickFolderDialogImpl::currentFolderChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
+ }
+ }
+
+ d->dialog = dialog;
+
+ if (d->dialog) {
+ if (auto fileDialog = d->asFileDialog()) {
+ QObjectPrivate::connect(fileDialog, &QQuickFileDialogImpl::currentFolderChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
+ } else if (auto folderDialog = d->asFolderDialog()) {
+ QObjectPrivate::connect(folderDialog, &QQuickFolderDialogImpl::currentFolderChanged,
+ d, &QQuickFolderBreadcrumbBarPrivate::folderChanged);
+ }
+ }
+
+ emit dialogChanged();
+}
+
+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) {
+ d->toggleTextFieldVisibility();
+ return true;
+ } else if (shortcutEvent->shortcutId() == d->goUpShortcutId) {
+ d->goUp();
+ }
+ }
+#endif
+ return QQuickItem::event(event);
+}
+
+void QQuickFolderBreadcrumbBar::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QQuickFolderBreadcrumbBar);
+
+ if (event->matches(QKeySequence::Cancel) && d->textField->isVisible()) {
+ d->toggleTextFieldVisibility();
+ event->accept();
+ } else {
+ QQuickContainer::keyPressEvent(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->dialog->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 the edit path shortcut when we're not visible.
+ d->ungrabEditPathShortcut();
+
+ 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
+
+#include "moc_qquickfolderbreadcrumbbar_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h
new file mode 100644
index 0000000000..d210112b33
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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(QQuickDialog *dialog READ dialog WRITE setDialog NOTIFY dialogChanged)
+ 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);
+
+ QQuickDialog *dialog() const;
+ void setDialog(QQuickDialog *dialog);
+
+ 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 dialogChanged();
+ void buttonDelegateChanged();
+ void separatorDelegateChanged();
+ void upButtonChanged();
+ void upButtonSpacingChanged();
+ void textFieldChanged();
+
+protected:
+ bool event(QEvent *event) override;
+ void keyPressEvent(QKeyEvent *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/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h
new file mode 100644
index 0000000000..2e1288dc0b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderbreadcrumbbar_p_p.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 <QtQuickTemplates2/private/qquickdialog_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAbstractButton;
+class QQuickTextField;
+
+class QQuickFileDialogImpl;
+class QQuickFolderDialogImpl;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderBreadcrumbBarPrivate : public QQuickContainerPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQuickFolderBreadcrumbBar)
+
+ 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 ungrabEditPathShortcut();
+
+ QQuickFileDialogImpl *asFileDialog() const;
+ QQuickFolderDialogImpl *asFolderDialog() const;
+ bool isFileDialog() const;
+ QUrl dialogFolder() const;
+ void setDialogFolder(const QUrl &folder);
+
+ 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:
+ QQuickDialog *dialog = nullptr;
+ QList<QString> folderPaths;
+ QQmlComponent *buttonDelegate = nullptr;
+ QQmlComponent *separatorDelegate = nullptr;
+ QQuickDeferredPointer<QQuickAbstractButton> upButton;
+ QQuickDeferredPointer<QQuickTextField> textField;
+ int editPathToggleShortcutId = 0;
+ int goUpShortcutId = 0;
+ int upButtonSpacing = 0;
+ bool repopulating = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFOLDERBREADCRUMBBAR_P_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl.cpp
new file mode 100644
index 0000000000..25d82a17aa
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl.cpp
@@ -0,0 +1,374 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickfolderdialogimpl_p.h"
+#include "qquickfolderdialogimpl_p_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
+
+#include "qquickfiledialogdelegate_p.h"
+#include "qquickfolderbreadcrumbbar_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcFolderDialogCurrentFolder, "qt.quick.dialogs.quickfolderdialogimpl.currentFolder")
+Q_LOGGING_CATEGORY(lcFolderDialogSelectedFolder, "qt.quick.dialogs.quickfolderdialogimpl.selectedFolder")
+Q_LOGGING_CATEGORY(lcFolderDialogOptions, "qt.quick.dialogs.quickfolderdialogimpl.options")
+
+QQuickFolderDialogImplPrivate::QQuickFolderDialogImplPrivate()
+{
+}
+
+void QQuickFolderDialogImplPrivate::updateEnabled()
+{
+ Q_Q(QQuickFolderDialogImpl);
+ if (!buttonBox)
+ return;
+
+ QQuickFolderDialogImplAttached *attached = attachedOrWarn();
+ if (!attached)
+ return;
+
+ auto openButton = 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(!selectedFolder.isEmpty() && attached->breadcrumbBar()
+ && !attached->breadcrumbBar()->textField()->isVisible());
+}
+
+/*!
+ \internal
+
+ Ensures that a folder is always selected after a change in \c currentFolder.
+
+ \a oldFolderPath is the previous value of \c currentFolder.
+*/
+void QQuickFolderDialogImplPrivate::updateSelectedFolder(const QString &oldFolderPath)
+{
+ Q_Q(QQuickFolderDialogImpl);
+ QQuickFolderDialogImplAttached *attached = attachedOrWarn();
+ if (!attached || !attached->folderDialogListView())
+ return;
+
+ QString newSelectedFolderPath;
+ int newSelectedFolderIndex = 0;
+ const QString newFolderPath = QQmlFile::urlToLocalFileOrQrc(currentFolder);
+ if (!oldFolderPath.isEmpty() && !newFolderPath.isEmpty()) {
+ // If the user went up a directory (or several), we should set
+ // selectedFolder 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 selectedFolder 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);
+ newSelectedFolderPath = 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" << newSelectedFolderPath << "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 newSelectedFileInfo(newSelectedFolderPath);
+ // The directory can contain files, but since we put dirs first, that should never affect the indices.
+ newSelectedFolderIndex = dirs.indexOf(newSelectedFileInfo);
+ }
+ }
+
+ if (newSelectedFolderPath.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::NoDotAndDotDot, QDir::DirsFirst);
+ if (!files.isEmpty())
+ newSelectedFolderPath = files.first().absoluteFilePath();
+ }
+ }
+
+ const bool folderSelected = !newSelectedFolderPath.isEmpty();
+ q->setSelectedFolder(folderSelected ? QUrl::fromLocalFile(newSelectedFolderPath) : QUrl());
+ {
+ // Set the appropriate currentIndex for the selected folder. We block signals from ListView
+ // because we don't want folderDialogListViewCurrentIndexChanged to be called, as the file
+ // it gets from the delegate will not be up-to-date (but most importantly because we already
+ // just set the selected folder).
+ QSignalBlocker blocker(attached->folderDialogListView());
+ attached->folderDialogListView()->setCurrentIndex(folderSelected ? newSelectedFolderIndex : -1);
+ }
+ if (folderSelected) {
+ if (QQuickItem *currentItem = attached->folderDialogListView()->currentItem())
+ currentItem->forceActiveFocus();
+ }
+}
+
+void QQuickFolderDialogImplPrivate::handleAccept()
+{
+ // Let handleClick take care of calling accept().
+}
+
+void QQuickFolderDialogImplPrivate::handleClick(QQuickAbstractButton *button)
+{
+ Q_Q(QQuickFolderDialogImpl);
+ if (buttonRole(button) == QPlatformDialogHelper::AcceptRole && selectedFolder.isValid()) {
+ q->setSelectedFolder(selectedFolder);
+ q->accept();
+ }
+}
+
+/*!
+ \class QQuickFolderDialogImpl
+ \internal
+
+ An interface that QQuickFolderDialog can use to access the non-native Qt Quick FolderDialog.
+
+ Both this and the native implementations are created in QQuickAbstractDialog::create().
+*/
+
+QQuickFolderDialogImpl::QQuickFolderDialogImpl(QObject *parent)
+ : QQuickDialog(*(new QQuickFolderDialogImplPrivate), parent)
+{
+}
+
+QQuickFolderDialogImplAttached *QQuickFolderDialogImpl::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickFolderDialogImplAttached(object);
+}
+
+QUrl QQuickFolderDialogImpl::currentFolder() const
+{
+ Q_D(const QQuickFolderDialogImpl);
+ return d->currentFolder;
+}
+
+void QQuickFolderDialogImpl::setCurrentFolder(const QUrl &currentFolder)
+{
+ qCDebug(lcFolderDialogCurrentFolder) << "setCurrentFolder called with" << currentFolder;
+ Q_D(QQuickFolderDialogImpl);
+ if (currentFolder == d->currentFolder)
+ return;
+
+ const QString oldFolderPath = QQmlFile::urlToLocalFileOrQrc(d->currentFolder);
+
+ d->currentFolder = currentFolder;
+ d->updateSelectedFolder(oldFolderPath);
+ emit currentFolderChanged(d->currentFolder);
+}
+
+QUrl QQuickFolderDialogImpl::selectedFolder() const
+{
+ Q_D(const QQuickFolderDialogImpl);
+ return d->selectedFolder;
+}
+
+void QQuickFolderDialogImpl::setSelectedFolder(const QUrl &selectedFolder)
+{
+ Q_D(QQuickFolderDialogImpl);
+ qCDebug(lcFolderDialogSelectedFolder).nospace() << "setSelectedFolder called with selectedFolder "
+ << selectedFolder << " (d->selectedFolder is " << d->selectedFolder << ")";
+ if (selectedFolder == d->selectedFolder)
+ return;
+
+ d->selectedFolder = selectedFolder;
+ d->updateEnabled();
+ emit selectedFolderChanged(selectedFolder);
+}
+
+QSharedPointer<QFileDialogOptions> QQuickFolderDialogImpl::options() const
+{
+ Q_D(const QQuickFolderDialogImpl);
+ return d->options;
+}
+
+void QQuickFolderDialogImpl::setOptions(const QSharedPointer<QFileDialogOptions> &options)
+{
+ qCDebug(lcFolderDialogOptions).nospace() << "setOptions called with:"
+ << " acceptMode=" << options->acceptMode()
+ << " fileMode=" << options->fileMode()
+ << " initialDirectory=" << options->initialDirectory();
+
+ Q_D(QQuickFolderDialogImpl);
+ d->options = options;
+}
+
+/*!
+ \internal
+
+ These allow QQuickPlatformFileDialog::show() to set custom labels on the
+ dialog buttons without having to know about/go through QQuickFolderDialogImplAttached
+ and QQuickDialogButtonBox.
+*/
+void QQuickFolderDialogImpl::setAcceptLabel(const QString &label)
+{
+ Q_D(QQuickFolderDialogImpl);
+ d->acceptLabel = label;
+ QQuickFolderDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ auto acceptButton = d->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 QQuickFolderDialogImpl::setRejectLabel(const QString &label)
+{
+ Q_D(QQuickFolderDialogImpl);
+ d->rejectLabel = label;
+ if (!d->buttonBox)
+ return;
+
+ auto rejectButton = d->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 QQuickFolderDialogImpl::componentComplete()
+{
+ Q_D(QQuickFolderDialogImpl);
+ 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).
+ QQuickFolderDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ Q_ASSERT(d->buttonBox);
+ const int buttonCount = d->buttonBox->count();
+ if (buttonCount == 0)
+ return;
+
+ QQuickAbstractButton *rightMostButton = qobject_cast<QQuickAbstractButton *>(
+ d->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 QQuickFolderDialogImpl::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ Q_D(QQuickFolderDialogImpl);
+ QQuickDialog::itemChange(change, data);
+
+ if (change != QQuickItem::ItemVisibleHasChanged || !isComponentComplete() || !data.boolValue)
+ return;
+
+ QQuickFolderDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ attached->folderDialogListView()->forceActiveFocus();
+ d->updateEnabled();
+}
+
+QQuickFolderDialogImplAttached *QQuickFolderDialogImplPrivate::attachedOrWarn()
+{
+ Q_Q(QQuickFolderDialogImpl);
+ QQuickFolderDialogImplAttached *attached = static_cast<QQuickFolderDialogImplAttached*>(
+ qmlAttachedPropertiesObject<QQuickFolderDialogImpl>(q));
+ if (!attached)
+ qmlWarning(q) << "Expected FileDialogImpl attached object to be present on" << this;
+ return attached;
+}
+
+void QQuickFolderDialogImplAttachedPrivate::folderDialogListViewCurrentIndexChanged()
+{
+ auto folderDialogImpl = qobject_cast<QQuickFolderDialogImpl*>(parent);
+ if (!folderDialogImpl)
+ return;
+
+ auto folderDialogDelegate = qobject_cast<QQuickFileDialogDelegate*>(folderDialogListView->currentItem());
+ if (!folderDialogDelegate)
+ return;
+
+ folderDialogImpl->setSelectedFolder(folderDialogDelegate->file());
+}
+
+QQuickFolderDialogImplAttached::QQuickFolderDialogImplAttached(QObject *parent)
+ : QObject(*(new QQuickFolderDialogImplAttachedPrivate), parent)
+{
+ if (!qobject_cast<QQuickFolderDialogImpl*>(parent)) {
+ qmlWarning(this) << "FolderDialogImpl attached properties should only be "
+ << "accessed through the root FileDialogImpl instance";
+ }
+}
+
+QQuickListView *QQuickFolderDialogImplAttached::folderDialogListView() const
+{
+ Q_D(const QQuickFolderDialogImplAttached);
+ return d->folderDialogListView;
+}
+
+void QQuickFolderDialogImplAttached::setFolderDialogListView(QQuickListView *folderDialogListView)
+{
+ Q_D(QQuickFolderDialogImplAttached);
+ if (folderDialogListView == d->folderDialogListView)
+ return;
+
+ d->folderDialogListView = folderDialogListView;
+
+ QObjectPrivate::connect(d->folderDialogListView, &QQuickListView::currentIndexChanged,
+ d, &QQuickFolderDialogImplAttachedPrivate::folderDialogListViewCurrentIndexChanged);
+
+ emit folderDialogListViewChanged();
+}
+
+QQuickFolderBreadcrumbBar *QQuickFolderDialogImplAttached::breadcrumbBar() const
+{
+ Q_D(const QQuickFolderDialogImplAttached);
+ return d->breadcrumbBar;
+}
+
+void QQuickFolderDialogImplAttached::setBreadcrumbBar(QQuickFolderBreadcrumbBar *breadcrumbBar)
+{
+ Q_D(QQuickFolderDialogImplAttached);
+ if (breadcrumbBar == d->breadcrumbBar)
+ return;
+
+ d->breadcrumbBar = breadcrumbBar;
+ emit breadcrumbBarChanged();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickfolderdialogimpl_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h
new file mode 100644
index 0000000000..021ef5479a
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKFOLDERDIALOGIMPL_P_H
+#define QQUICKFOLDERDIALOGIMPL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This folder is not part of the Qt API. It exists purely as an
+// implementation detail. This header folder 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 QQuickDialogButtonBox;
+
+class QQuickFolderDialogImplAttached;
+class QQuickFolderDialogImplAttachedPrivate;
+class QQuickFolderDialogImplPrivate;
+class QQuickFolderBreadcrumbBar;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderDialogImpl : public QQuickDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL)
+ Q_PROPERTY(QUrl selectedFolder READ selectedFolder WRITE setSelectedFolder NOTIFY selectedFolderChanged FINAL)
+ QML_NAMED_ELEMENT(FolderDialogImpl)
+ QML_ATTACHED(QQuickFolderDialogImplAttached)
+ QML_ADDED_IN_VERSION(6, 3)
+
+public:
+ explicit QQuickFolderDialogImpl(QObject *parent = nullptr);
+
+ static QQuickFolderDialogImplAttached *qmlAttachedProperties(QObject *object);
+
+ QUrl currentFolder() const;
+ void setCurrentFolder(const QUrl &folder);
+
+ QUrl selectedFolder() const;
+ void setSelectedFolder(const QUrl &selectedFolder);
+
+ QSharedPointer<QFileDialogOptions> options() const;
+ void setOptions(const QSharedPointer<QFileDialogOptions> &options);
+
+ void setAcceptLabel(const QString &label);
+ void setRejectLabel(const QString &label);
+
+Q_SIGNALS:
+ void currentFolderChanged(const QUrl &folderUrl);
+ void selectedFolderChanged(const QUrl &folderUrl);
+ void nameFiltersChanged();
+
+private:
+ void componentComplete() override;
+ void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override;
+
+ Q_DISABLE_COPY(QQuickFolderDialogImpl)
+ Q_DECLARE_PRIVATE(QQuickFolderDialogImpl)
+};
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFolderDialogImplAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickListView *folderDialogListView READ folderDialogListView WRITE setFolderDialogListView NOTIFY folderDialogListViewChanged)
+ Q_PROPERTY(QQuickFolderBreadcrumbBar *breadcrumbBar READ breadcrumbBar WRITE setBreadcrumbBar NOTIFY breadcrumbBarChanged)
+ Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>)
+
+public:
+ explicit QQuickFolderDialogImplAttached(QObject *parent = nullptr);
+
+ QQuickListView *folderDialogListView() const;
+ void setFolderDialogListView(QQuickListView *folderDialogListView);
+
+ QQuickFolderBreadcrumbBar *breadcrumbBar() const;
+ void setBreadcrumbBar(QQuickFolderBreadcrumbBar *breadcrumbBar);
+
+Q_SIGNALS:
+ void folderDialogListViewChanged();
+ void breadcrumbBarChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickFolderDialogImplAttached)
+ Q_DECLARE_PRIVATE(QQuickFolderDialogImplAttached)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFolderDialogImpl)
+
+#endif // QQUICKFOLDERDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h
new file mode 100644
index 0000000000..b5abda79ad
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfolderdialogimpl_p_p.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKFOLDERDIALOG_P_P_H
+#define QQUICKFOLDERDIALOG_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This Folder is not part of the Qt API. It exists purely as an
+// implementation detail. This header Folder may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickTemplates2/private/qquickdialog_p_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>
+
+#include "qquickfolderdialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFolderDialogImplPrivate : public QQuickDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickFolderDialogImpl)
+
+public:
+ QQuickFolderDialogImplPrivate();
+
+ static QQuickFolderDialogImplPrivate *get(QQuickFolderDialogImpl *dialog)
+ {
+ return dialog->d_func();
+ }
+
+ QQuickFolderDialogImplAttached *attachedOrWarn();
+
+ void updateEnabled();
+ void updateSelectedFolder(const QString &oldFolderPath);
+
+ void handleAccept() override;
+ void handleClick(QQuickAbstractButton *button) override;
+
+ QSharedPointer<QFileDialogOptions> options;
+ QUrl currentFolder;
+ QUrl selectedFolder;
+ QString acceptLabel;
+ QString rejectLabel;
+};
+
+class QQuickFolderDialogImplAttachedPrivate : public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQuickFolderDialogImplAttached)
+
+ void folderDialogListViewCurrentIndexChanged();
+
+ QPointer<QQuickDialogButtonBox> buttonBox;
+ QPointer<QQuickListView> folderDialogListView;
+ QPointer<QQuickFolderBreadcrumbBar> breadcrumbBar;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFOLDERDIALOG_P_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl.cpp
new file mode 100644
index 0000000000..5dcde5a81e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl.cpp
@@ -0,0 +1,837 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickfontdialogimpl_p.h"
+#include "qquickfontdialogimpl_p_p.h"
+
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
+#include <private/qfontdatabase_p.h>
+
+#include <QRegularExpression>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcAttachedProperty, "qt.quick.dialogs.quickfontdialogimpl.attachedOrWarn")
+
+QQuickFontDialogImplPrivate::QQuickFontDialogImplPrivate()
+{
+}
+
+QQuickFontDialogImplAttached *QQuickFontDialogImplPrivate::attachedOrWarn()
+{
+ Q_Q(QQuickFontDialogImpl);
+ QQuickFontDialogImplAttached *attached = static_cast<QQuickFontDialogImplAttached *>(
+ qmlAttachedPropertiesObject<QQuickFontDialogImpl>(q));
+ if (!attached) {
+ qCWarning(lcAttachedProperty)
+ << "Expected FontDialogImpl attached object to be present on" << this;
+ }
+ return attached;
+}
+
+void QQuickFontDialogImplPrivate::handleAccept() { }
+
+void QQuickFontDialogImplPrivate::handleClick(QQuickAbstractButton *button)
+{
+ Q_Q(QQuickFontDialogImpl);
+ if (buttonRole(button) == QPlatformDialogHelper::AcceptRole) {
+ q->accept();
+ QQuickDialogPrivate::handleClick(button);
+ }
+}
+
+QQuickFontDialogImpl::QQuickFontDialogImpl(QObject *parent)
+ : QQuickDialog(*(new QQuickFontDialogImplPrivate), parent)
+{
+}
+
+QQuickFontDialogImplAttached *QQuickFontDialogImpl::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickFontDialogImplAttached(object);
+}
+
+QSharedPointer<QFontDialogOptions> QQuickFontDialogImpl::options() const
+{
+ Q_D(const QQuickFontDialogImpl);
+
+ return d->options;
+}
+
+void QQuickFontDialogImpl::setOptions(const QSharedPointer<QFontDialogOptions> &options)
+{
+ Q_D(QQuickFontDialogImpl);
+
+ if (options == d->options)
+ return;
+
+ d->options = options;
+
+ emit optionsChanged();
+}
+
+QFont QQuickFontDialogImpl::currentFont() const
+{
+ Q_D(const QQuickFontDialogImpl);
+ return d->currentFont;
+}
+
+void QQuickFontDialogImpl::setCurrentFont(const QFont &font, bool selectInListViews)
+{
+ Q_D(QQuickFontDialogImpl);
+
+ if (font == d->currentFont)
+ return;
+
+ d->currentFont = font;
+
+ emit currentFontChanged(font);
+
+ if (!selectInListViews)
+ return;
+
+ QQuickFontDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ if (!attached->familyListView()->model().isValid()) {
+ const QSignalBlocker blocker(attached->sampleEdit());
+ attached->updateFamilies();
+ }
+
+ attached->selectFontInListViews(font);
+}
+
+void QQuickFontDialogImpl::init()
+{
+ Q_D(QQuickFontDialogImpl);
+ QQuickFontDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ if (!attached->familyListView()->model().isValid())
+ attached->updateFamilies();
+
+ attached->buttonBox()->setVisible(!(options()->options() & QFontDialogOptions::NoButtons));
+}
+
+void QQuickFontDialogImpl::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QQuickFontDialogImpl);
+
+ QQuickDialog::keyReleaseEvent(event);
+
+ QQuickFontDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ // The family and style text edits are read-only so that they
+ // can show the current selection but also allow key input to "search".
+ // This is why we handle just the release event, and don't accept it.
+ if (window()->activeFocusItem() == attached->familyEdit())
+ attached->searchFamily(event->text());
+ else if (window()->activeFocusItem() == attached->styleEdit())
+ attached->searchStyle(event->text());
+}
+
+void QQuickFontDialogImpl::focusOutEvent(QFocusEvent *event)
+{
+ Q_D(QQuickFontDialogImpl);
+
+ QQuickDialog::focusOutEvent(event);
+
+ QQuickFontDialogImplAttached *attached = d->attachedOrWarn();
+ if (!attached)
+ return;
+
+ attached->clearSearch();
+}
+
+QQuickFontDialogImplAttached::QQuickFontDialogImplAttached(QObject *parent)
+ : QObject(*(new QQuickFontDialogImplAttachedPrivate), parent),
+ m_writingSystem(QFontDatabase::Any),
+ m_selectedSize(-1),
+ m_smoothlyScalable(false),
+ m_ignoreFamilyUpdate(false),
+ m_ignoreStyleUpdate(false)
+{
+ if (!qobject_cast<QQuickFontDialogImpl *>(parent)) {
+ qmlWarning(this) << "FontDialogImpl attached properties should only be "
+ << "accessed through the root FileDialogImpl instance";
+ }
+}
+
+QQuickListView *QQuickFontDialogImplAttached::familyListView() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->familyListView;
+}
+
+void QQuickFontDialogImplAttached::setFamilyListView(QQuickListView *familyListView)
+{
+ Q_D(QQuickFontDialogImplAttached);
+ if (d->familyListView == familyListView)
+ return;
+
+ if (d->familyListView) {
+ disconnect(d->familyListView, &QQuickListView::currentIndexChanged,
+ this, &QQuickFontDialogImplAttached::_q_familyChanged);
+ }
+
+ d->familyListView = familyListView;
+
+ if (familyListView) {
+ connect(d->familyListView, &QQuickListView::currentIndexChanged,
+ this, &QQuickFontDialogImplAttached::_q_familyChanged);
+ }
+
+ emit familyListViewChanged();
+}
+
+QQuickListView *QQuickFontDialogImplAttached::styleListView() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->styleListView;
+}
+
+void QQuickFontDialogImplAttached::setStyleListView(QQuickListView *styleListView)
+{
+ Q_D(QQuickFontDialogImplAttached);
+ if (d->styleListView == styleListView)
+ return;
+
+ if (d->styleListView) {
+ disconnect(d->styleListView, &QQuickListView::currentIndexChanged,
+ this, &QQuickFontDialogImplAttached::_q_styleChanged);
+ }
+
+ d->styleListView = styleListView;
+
+ if (styleListView) {
+ connect(d->styleListView, &QQuickListView::currentIndexChanged,
+ this, &QQuickFontDialogImplAttached::_q_styleChanged);
+ }
+
+ emit styleListViewChanged();
+}
+
+QQuickListView *QQuickFontDialogImplAttached::sizeListView() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->sizeListView;
+}
+
+void QQuickFontDialogImplAttached::setSizeListView(QQuickListView *sizeListView)
+{
+ Q_D(QQuickFontDialogImplAttached);
+ if (d->sizeListView == sizeListView)
+ return;
+
+ if (d->sizeListView) {
+ disconnect(d->sizeListView, &QQuickListView::currentIndexChanged,
+ this, &QQuickFontDialogImplAttached::_q_sizeChanged);
+ }
+
+ d->sizeListView = sizeListView;
+
+ if (d->sizeListView) {
+ connect(d->sizeListView, &QQuickListView::currentIndexChanged,
+ this, &QQuickFontDialogImplAttached::_q_sizeChanged);
+ }
+
+ emit sizeListViewChanged();
+}
+
+QQuickTextEdit *QQuickFontDialogImplAttached::sampleEdit() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->sampleEdit;
+}
+
+void QQuickFontDialogImplAttached::setSampleEdit(QQuickTextEdit *sampleEdit)
+{
+ Q_D(QQuickFontDialogImplAttached);
+
+ if (d->sampleEdit == sampleEdit)
+ return;
+
+ if (d->sampleEdit) {
+ QObjectPrivate::disconnect(d->sampleEdit, &QQuickTextEdit::fontChanged,
+ d, &QQuickFontDialogImplAttachedPrivate::currentFontChanged);
+ }
+
+ d->sampleEdit = sampleEdit;
+
+ if (d->sampleEdit) {
+ QObjectPrivate::connect(d->sampleEdit, &QQuickTextEdit::fontChanged,
+ d, &QQuickFontDialogImplAttachedPrivate::currentFontChanged);
+
+ d->sampleEdit->setText(QFontDatabase::writingSystemSample(m_writingSystem));
+ }
+
+ emit sampleEditChanged();
+}
+
+QQuickDialogButtonBox *QQuickFontDialogImplAttached::buttonBox() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->buttonBox;
+}
+
+void QQuickFontDialogImplAttached::setButtonBox(QQuickDialogButtonBox *buttonBox)
+{
+ Q_D(QQuickFontDialogImplAttached);
+ if (buttonBox == d->buttonBox)
+ return;
+
+ if (d->buttonBox) {
+ QQuickFontDialogImpl *fontDialogImpl = qobject_cast<QQuickFontDialogImpl *>(parent());
+ if (fontDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(fontDialogImpl);
+ 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) {
+ QQuickFontDialogImpl *fontDialogImpl = qobject_cast<QQuickFontDialogImpl *>(parent());
+ if (fontDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(fontDialogImpl);
+ 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 *QQuickFontDialogImplAttached::writingSystemComboBox() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->writingSystemComboBox;
+}
+
+void QQuickFontDialogImplAttached::setWritingSystemComboBox(QQuickComboBox *writingSystemComboBox)
+{
+ Q_D(QQuickFontDialogImplAttached);
+
+ if (d->writingSystemComboBox == writingSystemComboBox)
+ return;
+
+ if (d->writingSystemComboBox) {
+ disconnect(d->writingSystemComboBox, &QQuickComboBox::activated,
+ this, &QQuickFontDialogImplAttached::_q_writingSystemChanged);
+ }
+
+ d->writingSystemComboBox = writingSystemComboBox;
+
+ if (d->writingSystemComboBox) {
+ QStringList writingSystemModel;
+ for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
+ QFontDatabase::WritingSystem ws = QFontDatabase::WritingSystem(i);
+ QString wsName = QFontDatabase::writingSystemName(ws);
+ if (wsName.isEmpty())
+ break;
+ writingSystemModel.append(wsName);
+ }
+
+ d->writingSystemComboBox->setModel(writingSystemModel);
+
+ connect(d->writingSystemComboBox, &QQuickComboBox::activated,
+ this, &QQuickFontDialogImplAttached::_q_writingSystemChanged);
+ }
+
+ emit writingSystemComboBoxChanged();
+}
+
+QQuickCheckBox *QQuickFontDialogImplAttached::underlineCheckBox() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->underlineCheckBox;
+}
+
+void QQuickFontDialogImplAttached::setUnderlineCheckBox(QQuickCheckBox *underlineCheckBox)
+{
+ Q_D(QQuickFontDialogImplAttached);
+
+ if (d->underlineCheckBox == underlineCheckBox)
+ return;
+
+ if (d->underlineCheckBox) {
+ disconnect(d->underlineCheckBox, &QQuickCheckBox::checkStateChanged,
+ this, &QQuickFontDialogImplAttached::_q_updateSample);
+ }
+
+ d->underlineCheckBox = underlineCheckBox;
+
+ if (d->underlineCheckBox) {
+ connect(d->underlineCheckBox, &QQuickCheckBox::checkStateChanged,
+ this, &QQuickFontDialogImplAttached::_q_updateSample);
+ }
+
+ emit underlineCheckBoxChanged();
+}
+
+QQuickCheckBox *QQuickFontDialogImplAttached::strikeoutCheckBox() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->strikeoutCheckBox;
+}
+
+void QQuickFontDialogImplAttached::setStrikeoutCheckBox(QQuickCheckBox *strikeoutCheckBox)
+{
+ Q_D(QQuickFontDialogImplAttached);
+
+ if (d->strikeoutCheckBox == strikeoutCheckBox)
+ return;
+
+ if (d->strikeoutCheckBox) {
+ disconnect(d->strikeoutCheckBox, &QQuickCheckBox::checkStateChanged,
+ this, &QQuickFontDialogImplAttached::_q_updateSample);
+ }
+
+ d->strikeoutCheckBox = strikeoutCheckBox;
+
+ if (d->strikeoutCheckBox) {
+ connect(d->strikeoutCheckBox, &QQuickCheckBox::checkStateChanged,
+ this, &QQuickFontDialogImplAttached::_q_updateSample);
+ }
+
+ emit strikeoutCheckBoxChanged();
+}
+
+QQuickTextField *QQuickFontDialogImplAttached::familyEdit() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->familyEdit;
+}
+
+void QQuickFontDialogImplAttached::setFamilyEdit(QQuickTextField *familyEdit)
+{
+ Q_D(QQuickFontDialogImplAttached);
+
+ if (d->familyEdit == familyEdit)
+ return;
+
+ d->familyEdit = familyEdit;
+
+ emit familyEditChanged();
+}
+
+QQuickTextField *QQuickFontDialogImplAttached::styleEdit() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->styleEdit;
+}
+
+void QQuickFontDialogImplAttached::setStyleEdit(QQuickTextField *styleEdit)
+{
+ Q_D(QQuickFontDialogImplAttached);
+
+ if (d->styleEdit == styleEdit)
+ return;
+
+ d->styleEdit = styleEdit;
+
+ emit styleEditChanged();
+}
+
+QQuickTextField *QQuickFontDialogImplAttached::sizeEdit() const
+{
+ Q_D(const QQuickFontDialogImplAttached);
+ return d->sizeEdit;
+}
+
+void QQuickFontDialogImplAttached::setSizeEdit(QQuickTextField *sizeEdit)
+{
+ Q_D(QQuickFontDialogImplAttached);
+
+ if (d->sizeEdit == sizeEdit)
+ return;
+
+ if (d->sizeEdit) {
+ disconnect(d->sizeEdit, &QQuickTextField::textChanged,
+ this, &QQuickFontDialogImplAttached::_q_sizeEdited);
+ }
+
+ d->sizeEdit = sizeEdit;
+
+ if (d->sizeEdit) {
+ connect(d->sizeEdit, &QQuickTextField::textChanged,
+ this, &QQuickFontDialogImplAttached::_q_sizeEdited);
+ }
+
+ emit sizeEditChanged();
+}
+
+static int findFamilyInModel(const QString &selectedFamily, const QStringList &model)
+{
+ enum match_t { MATCH_NONE = 0, MATCH_LAST_RESORT = 1, MATCH_APP = 2, MATCH_FAMILY = 3 };
+ QString foundryName1, familyName1, foundryName2, familyName2;
+ int bestFamilyMatch = -1;
+ match_t bestFamilyType = MATCH_NONE;
+ const QFont f;
+
+ QFontDatabasePrivate::parseFontName(selectedFamily, foundryName1, familyName1);
+
+ int i = 0;
+ for (auto it = model.constBegin(); it != model.constEnd(); ++it, ++i) {
+ QFontDatabasePrivate::parseFontName(*it, foundryName2, familyName2);
+
+ if (familyName1 == familyName2) {
+ bestFamilyType = MATCH_FAMILY;
+ if (foundryName1 == foundryName2)
+ return i;
+ else
+ bestFamilyMatch = i;
+ }
+
+ // fallbacks
+ match_t type = MATCH_NONE;
+ if (bestFamilyType <= MATCH_NONE && familyName2 == QStringLiteral("helvetica"))
+ type = MATCH_LAST_RESORT;
+ if (bestFamilyType <= MATCH_LAST_RESORT && familyName2 == f.families().constFirst())
+ type = MATCH_APP;
+ if (type != MATCH_NONE) {
+ bestFamilyType = type;
+ bestFamilyMatch = i;
+ }
+ }
+
+ return bestFamilyMatch;
+}
+
+static int findStyleInModel(const QString &selectedStyle, const QStringList &model)
+{
+ if (model.isEmpty())
+ return -1;
+
+ if (!selectedStyle.isEmpty()) {
+ const int idx = model.indexOf(QRegularExpression(QRegularExpression::escape(selectedStyle), QRegularExpression::CaseInsensitiveOption));
+ if (idx >= 0)
+ return idx;
+
+ enum class StyleClass {Unknown, Normal, Italic};
+ auto classifyStyleFallback = [](const QString & style) {
+ if (style.toLower() == QLatin1String("italic") || style.toLower() == QLatin1String("oblique"))
+ return StyleClass::Italic;
+ if (style.toLower() == QLatin1String("normal") || style.toLower() == QLatin1String("regular"))
+ return StyleClass::Normal;
+ return StyleClass::Unknown;
+ };
+
+ auto styleClass = classifyStyleFallback(selectedStyle);
+
+ if (styleClass != StyleClass::Unknown)
+ for (int i = 0; i < model.size(); ++i)
+ if (classifyStyleFallback(model.at(i)) == styleClass)
+ return i;
+ }
+ return 0;
+}
+
+/*!
+ \internal
+
+ Updates the model for the family list view, and attempt
+ to reselect the previously selected font family.
+ */
+void QQuickFontDialogImplAttached::updateFamilies()
+{
+ const QFontDialogOptions::FontDialogOptions scalableMask(
+ QFontDialogOptions::ScalableFonts | QFontDialogOptions::NonScalableFonts);
+
+ const QFontDialogOptions::FontDialogOptions spacingMask(QFontDialogOptions::ProportionalFonts
+ | QFontDialogOptions::MonospacedFonts);
+
+ const auto p = qobject_cast<QQuickFontDialogImpl *>(parent());
+
+ const auto options = p->options()->options();
+
+ QStringList familyNames;
+ const auto families = QFontDatabase::families(m_writingSystem);
+ for (const auto &family : families) {
+ if (QFontDatabase::isPrivateFamily(family))
+ continue;
+
+ if ((options & scalableMask) && (options & scalableMask) != scalableMask) {
+ if (bool(options & QFontDialogOptions::ScalableFonts)
+ != QFontDatabase::isSmoothlyScalable(family))
+ continue;
+ }
+
+ if ((options & spacingMask) && (options & scalableMask) != spacingMask) {
+ if (bool(options & QFontDialogOptions::MonospacedFonts)
+ != QFontDatabase::isFixedPitch(family))
+ continue;
+ }
+
+ familyNames << family;
+ }
+
+ auto listView = familyListView();
+
+ // Index will be set to -1 on empty model, and 0 for non empty models.
+ m_ignoreFamilyUpdate = !m_selectedFamily.isEmpty();
+ listView->setModel(familyNames);
+ m_ignoreFamilyUpdate = false;
+
+ // Will overwrite selectedFamily and selectedStyle
+ listView->setCurrentIndex(findFamilyInModel(m_selectedFamily, familyNames));
+
+ if (familyNames.isEmpty())
+ _q_familyChanged();
+}
+
+/*!
+ \internal
+
+ Updates the model for the style list view, and
+ attempt to reselect the style that was previously selected.
+
+ Calls updateSizes()
+ */
+void QQuickFontDialogImplAttached::updateStyles()
+{
+ const QString family = familyListView()->currentIndex() >= 0 ? m_selectedFamily : QString();
+ const QStringList styles = QFontDatabase::styles(family);
+
+ auto listView = styleListView();
+
+ m_ignoreStyleUpdate = !m_selectedStyle.isEmpty();
+ listView->setModel(styles);
+
+ if (styles.isEmpty()) {
+ styleEdit()->clear();
+ m_smoothlyScalable = false;
+ } else {
+ int newIndex = findStyleInModel(m_selectedStyle, styles);
+
+ listView->setCurrentIndex(newIndex);
+
+ m_selectedStyle = styles.at(newIndex);
+ styleEdit()->setText(m_selectedStyle);
+
+ m_smoothlyScalable = QFontDatabase::isSmoothlyScalable(m_selectedFamily, m_selectedStyle);
+ }
+
+ m_ignoreStyleUpdate = false;
+
+ updateSizes();
+}
+
+/*!
+ \internal
+
+ Updates the model for the size list view,
+ and attempts to reselect the size that was previously selected
+ */
+void QQuickFontDialogImplAttached::updateSizes()
+{
+ if (!m_selectedFamily.isEmpty()) {
+ const QList<int> sizes = QFontDatabase::pointSizes(m_selectedFamily, m_selectedStyle);
+
+ QStringList str_sizes;
+
+ str_sizes.reserve(sizes.size());
+
+ int idx = 0, current = -1;
+ for (QList<int>::const_iterator it = sizes.constBegin(); it != sizes.constEnd(); it++) {
+ str_sizes.append(QString::number(*it));
+ if (current == -1 && m_selectedSize == *it) {
+ current = idx;
+ }
+ ++idx;
+ }
+
+ auto listView = sizeListView();
+
+ // only select the first element in the model when this function is first called and the new model isn't empty
+ listView->setModel(str_sizes);
+
+ if (current != -1)
+ listView->setCurrentIndex(current);
+
+ sizeEdit()->setText(!m_smoothlyScalable && listView->currentIndex() > 0
+ ? str_sizes.at(listView->currentIndex())
+ : QString::number(m_selectedSize));
+ } else {
+ qCWarning(lcAttachedProperty) << "Warning! selectedFamily is empty";
+ sizeEdit()->clear();
+ }
+
+ _q_updateSample();
+}
+
+void QQuickFontDialogImplAttached::_q_updateSample()
+{
+ if (m_selectedFamily.isEmpty())
+ return;
+
+ const int pSize = sizeEdit()->text().toInt();
+
+ QFont newFont = QFontDatabase::font(m_selectedFamily, m_selectedStyle, pSize);
+
+ newFont.setUnderline(underlineCheckBox()->isChecked());
+ newFont.setStrikeOut(strikeoutCheckBox()->isChecked());
+
+ sampleEdit()->setFont(newFont);
+}
+
+void QQuickFontDialogImplAttached::_q_writingSystemChanged(int index)
+{
+ m_writingSystem = QFontDatabase::WritingSystem(index);
+ sampleEdit()->setText(QFontDatabase::writingSystemSample(m_writingSystem));
+
+ updateFamilies();
+}
+
+void QQuickFontDialogImplAttached::searchListView(const QString &s, QQuickListView *listView)
+{
+ if (s.isEmpty())
+ return;
+
+ const QStringList model = listView->model().toStringList();
+
+ bool redo = false;
+
+ do {
+ m_search.append(s);
+
+ for (int i = 0; i < model.size(); ++i) {
+ if (model.at(i).startsWith(m_search, Qt::CaseInsensitive)) {
+ listView->setCurrentIndex(i);
+ return;
+ }
+ }
+
+ clearSearch();
+
+ redo = !redo;
+ } while (redo);
+}
+
+void QQuickFontDialogImplAttached::clearSearch()
+{
+ m_search.clear();
+}
+
+void QQuickFontDialogImplAttached::_q_familyChanged()
+{
+ if (m_ignoreFamilyUpdate)
+ return;
+
+ const int index = familyListView()->currentIndex();
+
+ if (index < 0) {
+ familyEdit()->clear();
+ } else {
+ m_selectedFamily = familyListView()->model().toStringList().at(index);
+ familyEdit()->setText(m_selectedFamily);
+ }
+
+ updateStyles();
+}
+
+void QQuickFontDialogImplAttached::_q_styleChanged()
+{
+ if (m_ignoreStyleUpdate)
+ return;
+
+ const int index = styleListView()->currentIndex();
+
+ if (index < 0) {
+ qCWarning(lcAttachedProperty) << "currentIndex changed to -1";
+ return;
+ }
+
+ m_selectedStyle = styleListView()->model().toStringList().at(index);
+ styleEdit()->setText(m_selectedStyle);
+ m_smoothlyScalable = QFontDatabase::isSmoothlyScalable(m_selectedFamily, m_selectedStyle);
+
+ updateSizes();
+}
+
+void QQuickFontDialogImplAttached::_q_sizeEdited()
+{
+ const int size = qAbs(sizeEdit()->text().toInt());
+
+ if (size == m_selectedSize)
+ return;
+
+ m_selectedSize = size;
+
+ if (sizeListView()->count()) {
+ auto model = sizeListView()->model().toStringList();
+
+ int i;
+ for (i = 0; i < model.size() - 1; ++i) {
+ if (model.at(i).toInt() >= size)
+ break;
+ }
+
+ QSignalBlocker blocker(sizeListView());
+ if (model.at(i).toInt() == size)
+ sizeListView()->setCurrentIndex(i);
+ else
+ sizeListView()->setCurrentIndex(-1);
+ }
+
+ _q_updateSample();
+}
+
+void QQuickFontDialogImplAttached::_q_sizeChanged()
+{
+ const int index = sizeListView()->currentIndex();
+
+ if (index < 0) {
+ qCWarning(lcAttachedProperty) << "currentIndex changed to -1";
+ return;
+ }
+
+ const QString s = sizeListView()->model().toStringList().at(index);
+ m_selectedSize = s.toInt();
+
+ sizeEdit()->setText(s);
+
+ _q_updateSample();
+}
+
+void QQuickFontDialogImplAttachedPrivate::currentFontChanged(const QFont &font)
+{
+ auto fontDialogImpl = qobject_cast<QQuickFontDialogImpl *>(parent);
+
+ if (!fontDialogImpl) {
+ return;
+ }
+
+ fontDialogImpl->setCurrentFont(font);
+}
+
+void QQuickFontDialogImplAttached::selectFontInListViews(const QFont &font)
+{
+ {
+ QSignalBlocker blocker(sampleEdit());
+ familyListView()->setCurrentIndex(findFamilyInModel(font.families().constFirst(), familyListView()->model().toStringList()));
+ styleListView()->setCurrentIndex(findStyleInModel(QFontDatabase::styleString(font), styleListView()->model().toStringList()));
+ sizeEdit()->setText(QString::number(font.pointSize()));
+
+ underlineCheckBox()->setChecked(font.underline());
+ strikeoutCheckBox()->setChecked(font.strikeOut());
+ }
+
+ _q_updateSample();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickfontdialogimpl_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h
new file mode 100644
index 0000000000..6c30eb2474
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p.h
@@ -0,0 +1,183 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKFONTDIALOGIMPL_P_H
+#define QQUICKFONTDIALOGIMPL_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/qfontdatabase.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuick/private/qquicktextedit_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
+#include <QtQuickTemplates2/private/qquickcombobox_p.h>
+#include <QtQuickTemplates2/private/qquickcheckbox_p.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickDialogButtonBox;
+
+class QQuickFontDialogImplAttached;
+class QQuickFontDialogImplAttachedPrivate;
+class QQuickFontDialogImplPrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFontDialogImpl : public QQuickDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont NOTIFY currentFontChanged FINAL)
+ QML_NAMED_ELEMENT(FontDialogImpl)
+ QML_ATTACHED(QQuickFontDialogImplAttached)
+ QML_ADDED_IN_VERSION(6, 2)
+
+public:
+ explicit QQuickFontDialogImpl(QObject *parent = nullptr);
+
+ static QQuickFontDialogImplAttached *qmlAttachedProperties(QObject *object);
+
+ QSharedPointer<QFontDialogOptions> options() const;
+ void setOptions(const QSharedPointer<QFontDialogOptions> &options);
+
+ QFont currentFont() const;
+ void setCurrentFont(const QFont &font, bool selectInListViews = false);
+
+ void init();
+
+Q_SIGNALS:
+ void optionsChanged();
+ void currentFontChanged(const QFont &font);
+
+private:
+ void keyReleaseEvent(QKeyEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+
+ Q_DISABLE_COPY(QQuickFontDialogImpl)
+ Q_DECLARE_PRIVATE(QQuickFontDialogImpl)
+};
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFontDialogImplAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickListView *familyListView READ familyListView WRITE setFamilyListView
+ NOTIFY familyListViewChanged)
+ Q_PROPERTY(QQuickListView *styleListView READ styleListView WRITE setStyleListView
+ NOTIFY styleListViewChanged)
+ Q_PROPERTY(QQuickListView *sizeListView READ sizeListView WRITE setSizeListView
+ NOTIFY sizeListViewChanged)
+ Q_PROPERTY(QQuickTextEdit *sampleEdit READ sampleEdit WRITE setSampleEdit
+ NOTIFY sampleEditChanged)
+ Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox WRITE setButtonBox
+ NOTIFY buttonBoxChanged)
+ Q_PROPERTY(QQuickComboBox *writingSystemComboBox READ writingSystemComboBox
+ WRITE setWritingSystemComboBox NOTIFY writingSystemComboBoxChanged)
+ Q_PROPERTY(QQuickCheckBox *underlineCheckBox READ underlineCheckBox WRITE setUnderlineCheckBox
+ NOTIFY underlineCheckBoxChanged)
+ Q_PROPERTY(QQuickCheckBox *strikeoutCheckBox READ strikeoutCheckBox WRITE setStrikeoutCheckBox
+ NOTIFY strikeoutCheckBoxChanged)
+
+ Q_PROPERTY(QQuickTextField *familyEdit READ familyEdit WRITE setFamilyEdit
+ NOTIFY familyEditChanged)
+ Q_PROPERTY(QQuickTextField *styleEdit READ styleEdit WRITE setStyleEdit NOTIFY styleEditChanged)
+ Q_PROPERTY(QQuickTextField *sizeEdit READ sizeEdit WRITE setSizeEdit NOTIFY sizeEditChanged)
+
+ Q_MOC_INCLUDE(<QtQuickTemplates2 / private / qquickdialogbuttonbox_p.h>)
+
+public:
+ explicit QQuickFontDialogImplAttached(QObject *parent = nullptr);
+
+ QQuickListView *familyListView() const;
+ void setFamilyListView(QQuickListView *familyListView);
+
+ QQuickListView *styleListView() const;
+ void setStyleListView(QQuickListView *styleListView);
+
+ QQuickListView *sizeListView() const;
+ void setSizeListView(QQuickListView *sizeListView);
+
+ QQuickTextEdit *sampleEdit() const;
+ void setSampleEdit(QQuickTextEdit *sampleEdit);
+
+ QQuickDialogButtonBox *buttonBox() const;
+ void setButtonBox(QQuickDialogButtonBox *buttonBox);
+
+ QQuickComboBox *writingSystemComboBox() const;
+ void setWritingSystemComboBox(QQuickComboBox *writingSystemComboBox);
+
+ QQuickCheckBox *underlineCheckBox() const;
+ void setUnderlineCheckBox(QQuickCheckBox *underlineCheckBox);
+
+ QQuickCheckBox *strikeoutCheckBox() const;
+ void setStrikeoutCheckBox(QQuickCheckBox *strikethroughCheckBox);
+
+ QQuickTextField *familyEdit() const;
+ void setFamilyEdit(QQuickTextField *familyEdit);
+
+ QQuickTextField *styleEdit() const;
+ void setStyleEdit(QQuickTextField *styleEdit);
+
+ QQuickTextField *sizeEdit() const;
+ void setSizeEdit(QQuickTextField *sizeEdit);
+
+Q_SIGNALS:
+ void buttonBoxChanged();
+ void familyListViewChanged();
+ void styleListViewChanged();
+ void sizeListViewChanged();
+ void sampleEditChanged();
+ void writingSystemComboBoxChanged();
+ void underlineCheckBoxChanged();
+ void strikeoutCheckBoxChanged();
+ void familyEditChanged();
+ void styleEditChanged();
+ void sizeEditChanged();
+
+public:
+ void searchFamily(const QString &s) { searchListView(s, familyListView()); }
+ void searchStyle(const QString &s) { searchListView(s, styleListView()); }
+ void clearSearch();
+
+ void updateFamilies();
+ void selectFontInListViews(const QFont &font);
+
+private:
+ void updateStyles();
+ void updateSizes();
+
+ void _q_familyChanged();
+ void _q_styleChanged();
+ void _q_sizeEdited();
+ void _q_sizeChanged();
+ void _q_updateSample();
+
+ void _q_writingSystemChanged(int index);
+
+ void searchListView(const QString &s, QQuickListView *listView);
+
+ QFontDatabase::WritingSystem m_writingSystem;
+ QString m_selectedFamily;
+ QString m_selectedStyle;
+ QString m_search;
+ int m_selectedSize;
+ bool m_smoothlyScalable;
+ bool m_ignoreFamilyUpdate;
+ bool m_ignoreStyleUpdate;
+
+ Q_DISABLE_COPY(QQuickFontDialogImplAttached)
+ Q_DECLARE_PRIVATE(QQuickFontDialogImplAttached)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickFontDialogImpl)
+
+#endif // QQUICKFONTDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h
new file mode 100644
index 0000000000..cd2ceb49b4
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfontdialogimpl_p_p.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKFONTDIALOGIMPL_P_P_H
+#define QQUICKFONTDIALOGIMPL_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 "qquickfontdialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFontDialogImplPrivate : public QQuickDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickFontDialogImpl)
+public:
+ QQuickFontDialogImplPrivate();
+
+ static QQuickFontDialogImplPrivate *get(QQuickFontDialogImpl *dialog)
+ {
+ return dialog->d_func();
+ }
+
+ QQuickFontDialogImplAttached *attachedOrWarn();
+
+ void updateEnabled();
+
+ void handleAccept() override;
+ void handleClick(QQuickAbstractButton *button) override;
+
+ QSharedPointer<QFontDialogOptions> options;
+
+ QFont currentFont;
+};
+
+class QQuickFontDialogImplAttachedPrivate : public QObjectPrivate
+{
+ void currentFontChanged(const QFont &font);
+
+public:
+ Q_DECLARE_PUBLIC(QQuickFontDialogImplAttached)
+
+ QPointer<QQuickDialogButtonBox> buttonBox;
+ QPointer<QQuickListView> familyListView;
+ QPointer<QQuickListView> styleListView;
+ QPointer<QQuickListView> sizeListView;
+ QPointer<QQuickTextEdit> sampleEdit;
+ QPointer<QQuickComboBox> writingSystemComboBox;
+ QPointer<QQuickCheckBox> underlineCheckBox;
+ QPointer<QQuickCheckBox> strikeoutCheckBox;
+ QPointer<QQuickTextField> familyEdit;
+ QPointer<QQuickTextField> styleEdit;
+ QPointer<QQuickTextField> sizeEdit;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFONTDIALOGIMPL_P_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp
new file mode 100644
index 0000000000..d80011d127
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl.cpp
@@ -0,0 +1,192 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmessagedialogimpl_p.h"
+#include "qquickmessagedialogimpl_p_p.h"
+
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQuickMessageDialogImplPrivate::QQuickMessageDialogImplPrivate() { }
+
+void QQuickMessageDialogImplPrivate::handleClick(QQuickAbstractButton *button)
+{
+ Q_Q(QQuickMessageDialogImpl);
+
+ if (const QQuickMessageDialogImplAttached *attached = attachedOrWarn()) {
+ QPlatformDialogHelper::StandardButton standardButton =
+ QQuickDialogButtonBoxPrivate::get(attached->buttonBox())->standardButton(button);
+ QPlatformDialogHelper::ButtonRole role = buttonRole(button);
+
+ emit q->buttonClicked(standardButton, role);
+ }
+
+ QQuickDialogPrivate::handleClick(button);
+}
+
+QQuickMessageDialogImplAttached *QQuickMessageDialogImplPrivate::attachedOrWarn()
+{
+ Q_Q(QQuickMessageDialogImpl);
+ QQuickMessageDialogImplAttached *attached = static_cast<QQuickMessageDialogImplAttached *>(
+ qmlAttachedPropertiesObject<QQuickMessageDialogImpl>(q));
+ if (!attached)
+ qmlWarning(q) << "Expected MessageDialogImpl attached object to be present on" << this;
+ return attached;
+}
+
+QQuickMessageDialogImpl::QQuickMessageDialogImpl(QObject *parent)
+ : QQuickDialog(*(new QQuickMessageDialogImplPrivate), parent)
+{
+}
+
+QSharedPointer<QMessageDialogOptions> QQuickMessageDialogImpl::options() const
+{
+ Q_D(const QQuickMessageDialogImpl);
+ return d->options;
+}
+
+void QQuickMessageDialogImpl::setOptions(const QSharedPointer<QMessageDialogOptions> &options)
+{
+ Q_D(QQuickMessageDialogImpl);
+ d->options = options;
+
+ QQuickMessageDialogImplAttached *attached = d->attachedOrWarn();
+
+ if (options && attached) {
+ attached->detailedTextButton()->setVisible(!d->options->detailedText().isEmpty());
+ attached->buttonBox()->setStandardButtons(d->options->standardButtons());
+ }
+
+ if (showDetailedText())
+ toggleShowDetailedText();
+
+ emit optionsChanged();
+}
+
+QString QQuickMessageDialogImpl::text() const
+{
+ Q_D(const QQuickMessageDialogImpl);
+ return d->options ? d->options->text() : QString();
+}
+
+QString QQuickMessageDialogImpl::informativeText() const
+{
+ Q_D(const QQuickMessageDialogImpl);
+ return d->options ? d->options->informativeText() : QString();
+}
+
+QString QQuickMessageDialogImpl::detailedText() const
+{
+ Q_D(const QQuickMessageDialogImpl);
+ return d->options ? d->options->detailedText() : QString();
+}
+
+bool QQuickMessageDialogImpl::showDetailedText() const
+{
+ Q_D(const QQuickMessageDialogImpl);
+ return d->m_showDetailedText;
+}
+
+void QQuickMessageDialogImpl::toggleShowDetailedText()
+{
+ Q_D(QQuickMessageDialogImpl);
+ d->m_showDetailedText = !d->m_showDetailedText;
+ emit showDetailedTextChanged();
+}
+
+QQuickMessageDialogImplAttached *QQuickMessageDialogImpl::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickMessageDialogImplAttached(object);
+}
+
+QQuickMessageDialogImplAttached::QQuickMessageDialogImplAttached(QObject *parent)
+ : QObject(*(new QQuickMessageDialogImplAttachedPrivate), parent)
+{
+ if (!qobject_cast<QQuickMessageDialogImpl *>(parent)) {
+ qmlWarning(this) << "MessageDialogImpl attached properties should only be "
+ << "accessed through the root MessageDialogImpl instance";
+ }
+}
+
+QQuickDialogButtonBox *QQuickMessageDialogImplAttached::buttonBox() const
+{
+ Q_D(const QQuickMessageDialogImplAttached);
+ return d->buttonBox;
+}
+
+void QQuickMessageDialogImplAttached::setButtonBox(QQuickDialogButtonBox *buttons)
+{
+ Q_D(QQuickMessageDialogImplAttached);
+ if (d->buttonBox == buttons)
+ return;
+
+ if (d->buttonBox) {
+ QQuickMessageDialogImpl *messageDialogImpl =
+ qobject_cast<QQuickMessageDialogImpl *>(parent());
+ if (messageDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(messageDialogImpl);
+ 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 = buttons;
+
+ if (d->buttonBox) {
+ QQuickMessageDialogImpl *messageDialogImpl =
+ qobject_cast<QQuickMessageDialogImpl *>(parent());
+ if (messageDialogImpl) {
+ auto dialogPrivate = QQuickDialogPrivate::get(messageDialogImpl);
+ 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();
+}
+
+QQuickButton *QQuickMessageDialogImplAttached::detailedTextButton() const
+{
+ Q_D(const QQuickMessageDialogImplAttached);
+ return d->detailedTextButton;
+}
+void QQuickMessageDialogImplAttached::setDetailedTextButton(QQuickButton *detailedTextButton)
+{
+ Q_D(QQuickMessageDialogImplAttached);
+
+ if (d->detailedTextButton == detailedTextButton)
+ return;
+
+ if (d->detailedTextButton) {
+ QQuickMessageDialogImpl *messageDialogImpl =
+ qobject_cast<QQuickMessageDialogImpl *>(parent());
+ if (messageDialogImpl)
+ disconnect(d->detailedTextButton, &QQuickAbstractButton::clicked, messageDialogImpl,
+ &QQuickMessageDialogImpl::toggleShowDetailedText);
+ }
+
+ d->detailedTextButton = detailedTextButton;
+
+ if (d->detailedTextButton) {
+ QQuickMessageDialogImpl *messageDialogImpl =
+ qobject_cast<QQuickMessageDialogImpl *>(parent());
+ if (messageDialogImpl)
+ connect(d->detailedTextButton, &QQuickAbstractButton::clicked, messageDialogImpl,
+ &QQuickMessageDialogImpl::toggleShowDetailedText);
+ }
+
+ emit detailedTextButtonChanged();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickmessagedialogimpl_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h
new file mode 100644
index 0000000000..c7871b8d11
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKMESSAGEDIALOGIMPL_P_H
+#define QQUICKMESSAGEDIALOGIMPL_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/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquicklabel_p.h>
+#include <QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>
+#include <QtQuickTemplates2/private/qquicktextarea_p.h>
+#include <QtQuickTemplates2/private/qquickbutton_p.h>
+
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMessageDialogImplAttached;
+class QQuickMessageDialogImplAttachedPrivate;
+class QQuickMessageDialogImplPrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickMessageDialogImpl : public QQuickDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text NOTIFY optionsChanged)
+ Q_PROPERTY(QString informativeText READ informativeText NOTIFY optionsChanged)
+ Q_PROPERTY(QString detailedText READ detailedText NOTIFY optionsChanged)
+ Q_PROPERTY(bool showDetailedText READ showDetailedText NOTIFY showDetailedTextChanged)
+ QML_NAMED_ELEMENT(MessageDialogImpl)
+ QML_ATTACHED(QQuickMessageDialogImplAttached)
+ QML_ADDED_IN_VERSION(6, 3)
+public:
+ explicit QQuickMessageDialogImpl(QObject *parent = nullptr);
+
+ static QQuickMessageDialogImplAttached *qmlAttachedProperties(QObject *object);
+
+ QSharedPointer<QMessageDialogOptions> options() const;
+ void setOptions(const QSharedPointer<QMessageDialogOptions> &options);
+
+ bool showDetailedText() const;
+ QString text() const;
+ QString informativeText() const;
+ QString detailedText() const;
+
+Q_SIGNALS:
+ void buttonClicked(QPlatformDialogHelper::StandardButton button,
+ QPlatformDialogHelper::ButtonRole role);
+ void showDetailedTextChanged();
+ void optionsChanged();
+
+public Q_SLOTS:
+ void toggleShowDetailedText();
+
+private:
+ Q_DISABLE_COPY(QQuickMessageDialogImpl)
+ Q_DECLARE_PRIVATE(QQuickMessageDialogImpl)
+};
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickMessageDialogImplAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox WRITE setButtonBox NOTIFY
+ buttonBoxChanged)
+ Q_PROPERTY(QQuickButton *detailedTextButton READ detailedTextButton WRITE setDetailedTextButton
+ NOTIFY detailedTextButtonChanged)
+public:
+ explicit QQuickMessageDialogImplAttached(QObject *parent = nullptr);
+
+ QQuickDialogButtonBox *buttonBox() const;
+ void setButtonBox(QQuickDialogButtonBox *buttons);
+
+ QQuickButton *detailedTextButton() const;
+ void setDetailedTextButton(QQuickButton *detailedTextButton);
+
+Q_SIGNALS:
+ void buttonBoxChanged();
+ void detailedTextButtonChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickMessageDialogImplAttached)
+ Q_DECLARE_PRIVATE(QQuickMessageDialogImplAttached)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickMessageDialogImpl)
+
+#endif // QQUICKMESSAGEDIALOGIMPL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h
new file mode 100644
index 0000000000..dca7ad218c
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickmessagedialogimpl_p_p.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKMESSAGEDIALOGIMPL_P_P_H
+#define QQUICKMESSAGEDIALOGIMPL_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This Folder is not part of the Qt API. It exists purely as an
+// implementation detail. This header Folder may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickTemplates2/private/qquickdialog_p_p.h>
+
+#include "qquickmessagedialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMessageDialogImplPrivate : public QQuickDialogPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickMessageDialogImpl)
+
+public:
+ QQuickMessageDialogImplPrivate();
+
+ static QQuickMessageDialogImplPrivate *get(QQuickMessageDialogImpl *dialog)
+ {
+ return dialog->d_func();
+ }
+
+ QQuickMessageDialogImplAttached *attachedOrWarn();
+
+ void handleClick(QQuickAbstractButton *button) override;
+
+ QSharedPointer<QMessageDialogOptions> options;
+ bool m_showDetailedText = false;
+};
+
+class QQuickMessageDialogImplAttachedPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickMessageDialogImplAttached)
+public:
+ QPointer<QQuickDialogButtonBox> buttonBox;
+ QPointer<QQuickButton> detailedTextButton;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKMESSAGEDIALOGIMPL_P_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog.cpp
new file mode 100644
index 0000000000..205b4e0b1b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog.cpp
@@ -0,0 +1,129 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickplatformcolordialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/qwindow.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupanchors_p.h>
+
+#include "qquickcolordialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQuickPlatformColorDialog, "qt.quick.dialogs.quickplatformcolordialog")
+
+QQuickPlatformColorDialog::QQuickPlatformColorDialog(QObject *parent)
+{
+ qCDebug(lcQuickPlatformColorDialog)
+ << "creating non-native Qt Quick ColorDialog 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, however.
+ setParent(parent);
+
+ auto qmlContext = ::qmlContext(parent);
+ if (!qmlContext) {
+ qmlWarning(parent) << "No QQmlContext for QQuickPlatformColorDialog; can't create "
+ "non-native ColorDialog implementation";
+ return;
+ }
+
+ const auto dialogQmlUrl = QUrl(QStringLiteral(
+ "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/qml/ColorDialog.qml"));
+ QQmlComponent colorDialogComponent(qmlContext->engine(), dialogQmlUrl, parent);
+ if (!colorDialogComponent.isReady()) {
+ qmlWarning(parent) << "Failed to load non-native ColorDialog implementation:\n"
+ << colorDialogComponent.errorString();
+ return;
+ }
+
+ m_dialog = qobject_cast<QQuickColorDialogImpl *>(colorDialogComponent.create());
+ if (!m_dialog) {
+ qmlWarning(parent) << "Failed to create an instance of the non-native ColorDialog:\n"
+ << colorDialogComponent.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, &QQuickColorDialogImpl::colorChanged, this,
+ &QQuickPlatformColorDialog::currentColorChanged);
+}
+
+bool QQuickPlatformColorDialog::isValid() const
+{
+ return m_dialog;
+}
+
+void QQuickPlatformColorDialog::setCurrentColor(const QColor &color)
+{
+ if (m_dialog)
+ m_dialog->setColor(color);
+}
+
+QColor QQuickPlatformColorDialog::currentColor() const
+{
+ return m_dialog ? m_dialog->color().toRgb() : QColor();
+}
+
+void QQuickPlatformColorDialog::exec()
+{
+ qCWarning(lcQuickPlatformColorDialog)
+ << "exec() is not supported for the Qt Quick ColorDialog fallback";
+}
+
+bool QQuickPlatformColorDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality,
+ QWindow *parent)
+{
+ qCDebug(lcQuickPlatformColorDialog)
+ << "show called with flags" << flags << "modality" << modality << "parent" << parent;
+
+ if (!m_dialog || !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<QColorDialogOptions> options = QPlatformColorDialogHelper::options();
+
+ m_dialog->setTitle(options->windowTitle());
+ m_dialog->setOptions(options);
+
+ m_dialog->open();
+ return true;
+}
+
+void QQuickPlatformColorDialog::hide()
+{
+ if (!m_dialog)
+ return;
+
+ m_dialog->close();
+}
+
+QQuickColorDialogImpl *QQuickPlatformColorDialog::dialog() const
+{
+ return m_dialog;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h
new file mode 100644
index 0000000000..fa4aef9be2
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformcolordialog_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKPLATFORMCOLORDIALOG_P_H
+#define QQUICKPLATFORMCOLORDIALOG_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 QQuickColorDialogImpl;
+class QWindow;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformColorDialog
+ : public QPlatformColorDialogHelper
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickPlatformColorDialog(QObject *parent);
+ ~QQuickPlatformColorDialog() = default;
+
+ bool isValid() const;
+
+ virtual void setCurrentColor(const QColor &color) override;
+ virtual QColor currentColor() const override;
+
+ void exec() override;
+ bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override;
+ void hide() override;
+
+ QQuickColorDialogImpl *dialog() const;
+
+private:
+ QQuickColorDialogImpl *m_dialog = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPLATFORMCOLORDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog.cpp
new file mode 100644
index 0000000000..1c9174a8e4
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog.cpp
@@ -0,0 +1,217 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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::selectedFileChanged, this, &QQuickPlatformFileDialog::currentChanged);
+ connect(m_dialog, &QQuickFileDialogImpl::currentFolderChanged, this, &QQuickPlatformFileDialog::directoryEntered);
+ connect(m_dialog, &QQuickFileDialogImpl::filterSelected, this, &QQuickPlatformFileDialog::filterSelected);
+}
+
+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;
+
+ if (m_dialog->isVisible()) {
+ qWarning() << "Cannot set an initial selectedFile while FileDialog is open";
+ return;
+ }
+
+ // Since we're only called once each time the FileDialog is shown,
+ // we call setInitialCurrentFolderAndSelectedFile here, which will ensure that
+ // the first currentIndex change (to 0, as a result of the ListView's model changing
+ // as a result of the FolderListModel directory change) is effectively
+ // ignored and the correct index for the initial selectedFile is maintained.
+ m_dialog->setInitialCurrentFolderAndSelectedFile(file);
+}
+
+// TODO: support for multiple selected files
+QList<QUrl> QQuickPlatformFileDialog::selectedFiles() const
+{
+ if (m_dialog->selectedFile().isEmpty())
+ return {};
+
+ return { m_dialog->selectedFile() };
+}
+
+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";
+}
+
+/*!
+ \internal
+
+ This is called after QQuickFileDialog::onShow().
+ Both are called in QQuickAbstractDialog::open().
+*/
+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());
+
+ if (options->initiallySelectedFiles().isEmpty()) {
+ if (m_dialog->currentFolder().isEmpty()) {
+ // The user didn't set an initial selectedFile nor currentFolder, so we'll set it to the working directory.
+ qCDebug(lcQuickPlatformFileDialog) << "- calling setCurrentFolder(QDir()) on quick dialog" << parent;
+ m_dialog->setCurrentFolder(QUrl::fromLocalFile(QDir().absolutePath()));
+ }
+ }
+
+ 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
+
+#include "moc_qquickplatformfiledialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h
new file mode 100644
index 0000000000..0e0dedf71a
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfiledialog_p.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog.cpp
new file mode 100644
index 0000000000..d0ccea353e
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog.cpp
@@ -0,0 +1,176 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickplatformfolderdialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/qwindow.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupanchors_p.h>
+
+#include "qquickfolderdialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQuickPlatformFolderDialog, "qt.quick.dialogs.quickplatformfolderdialog")
+
+/*!
+ \class QQuickPlatformFolderDialog
+ \internal
+
+ An interface that QQuickFolderDialog can use to access the non-native Qt Quick FolderDialog.
+
+ Both this and the native implementations are created in QQuickAbstractDialog::create().
+*/
+QQuickPlatformFolderDialog::QQuickPlatformFolderDialog(QObject *parent)
+{
+ qCDebug(lcQuickPlatformFolderDialog) << "creating non-native Qt Quick FolderDialog 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 QQuickPlatformFolderDialog; can't create non-native FolderDialog implementation";
+ return;
+ }
+
+ const auto dialogQmlUrl = QUrl(QStringLiteral("qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/qml/FolderDialog.qml"));
+ QQmlComponent folderDialogComponent(qmlContext->engine(), dialogQmlUrl, parent);
+ if (!folderDialogComponent.isReady()) {
+ qmlWarning(parent) << "Failed to load non-native FolderDialog implementation:\n" << folderDialogComponent.errorString();
+ return;
+ }
+ m_dialog = qobject_cast<QQuickFolderDialogImpl*>(folderDialogComponent.create());
+ if (!m_dialog) {
+ qmlWarning(parent) << "Failed to create an instance of the non-native FolderDialog:\n" << folderDialogComponent.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, &QQuickFolderDialogImpl::selectedFolderChanged, this, &QQuickPlatformFolderDialog::currentChanged);
+ connect(m_dialog, &QQuickFolderDialogImpl::currentFolderChanged, this, &QQuickPlatformFolderDialog::directoryEntered);
+ connect(m_dialog, &QQuickFolderDialogImpl::selectedFolderChanged, this, &QQuickPlatformFolderDialog::fileSelected);
+
+ // We would do this in QQuickFolderDialogImpl, but we need to ensure that folderChanged()
+ // is connected to directoryEntered() before setting it to ensure that the QQuickFolderDialog is notified.
+ if (m_dialog->currentFolder().isEmpty())
+ m_dialog->setCurrentFolder(QUrl::fromLocalFile(QDir().absolutePath()));
+}
+
+bool QQuickPlatformFolderDialog::isValid() const
+{
+ return m_dialog;
+}
+
+bool QQuickPlatformFolderDialog::defaultNameFilterDisables() const
+{
+ return false;
+}
+
+void QQuickPlatformFolderDialog::setDirectory(const QUrl &directory)
+{
+ if (!m_dialog)
+ return;
+
+ m_dialog->setCurrentFolder(directory);
+}
+
+QUrl QQuickPlatformFolderDialog::directory() const
+{
+ if (!m_dialog)
+ return {};
+
+ return m_dialog->currentFolder();
+}
+
+void QQuickPlatformFolderDialog::selectFile(const QUrl &file)
+{
+ if (!m_dialog)
+ return;
+
+ m_dialog->setSelectedFolder(file);
+}
+
+QList<QUrl> QQuickPlatformFolderDialog::selectedFiles() const
+{
+ // FolderDialog doesn't support multiple selected folders.
+ return { m_dialog->selectedFolder() };
+}
+
+void QQuickPlatformFolderDialog::setFilter()
+{
+}
+
+void QQuickPlatformFolderDialog::selectNameFilter(const QString &/*filter*/)
+{
+}
+
+QString QQuickPlatformFolderDialog::selectedNameFilter() const
+{
+ return QStringLiteral("*.*");
+}
+
+void QQuickPlatformFolderDialog::exec()
+{
+ qCWarning(lcQuickPlatformFolderDialog) << "exec() is not supported for the Qt Quick FolderDialog fallback";
+}
+
+bool QQuickPlatformFolderDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
+{
+ qCDebug(lcQuickPlatformFolderDialog) << "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->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 QQuickPlatformFolderDialog::hide()
+{
+ if (!m_dialog)
+ return;
+
+ m_dialog->close();
+}
+
+QQuickFolderDialogImpl *QQuickPlatformFolderDialog::dialog() const
+{
+ return m_dialog;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickplatformfolderdialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h
new file mode 100644
index 0000000000..73e2b2899b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfolderdialog_p.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKPLATFORMFOLDERDIALOG_P_H
+#define QQUICKPLATFORMFOLDERDIALOG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This folder is not part of the Qt API. It exists purely as an
+// implementation detail. This header folder may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpa/qplatformdialoghelper.h>
+
+#include "qquickfolderdialogimpl_p.h"
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFileDialogImpl;
+class QWindow;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformFolderDialog : public QPlatformFileDialogHelper
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickPlatformFolderDialog(QObject *parent);
+ ~QQuickPlatformFolderDialog() = 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;
+
+ QQuickFolderDialogImpl *dialog() const;
+
+private:
+ QQuickFolderDialogImpl *m_dialog = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPLATFORMFOLDERDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog.cpp
new file mode 100644
index 0000000000..4ef0b1bec6
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog.cpp
@@ -0,0 +1,145 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickplatformfontdialog_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/qwindow.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuickTemplates2/private/qquickdialog_p.h>
+#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupanchors_p.h>
+
+#include "qquickfontdialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQuickPlatformFontDialog, "qt.quick.dialogs.quickplatformfontdialog")
+
+/*!
+ \class QQuickPlatformFontDialog
+ \internal
+
+ An interface that QQuickFontDialog can use to access the non-native Qt Quick FontDialog.
+
+ Both this and the native implementations are created in QQuickAbstractDialog::create().
+
+*/
+QQuickPlatformFontDialog::QQuickPlatformFontDialog(QObject *parent)
+{
+ qCDebug(lcQuickPlatformFontDialog)
+ << "creating non-native Qt Quick FontDialog 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 QQuickPlatformFontDialog; can't create "
+ "non-native FontDialog implementation";
+ return;
+ }
+
+ const auto dialogQmlUrl = QUrl(QStringLiteral(
+ "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/qml/FontDialog.qml"));
+
+ QQmlComponent fontDialogComponent(qmlContext->engine(), dialogQmlUrl, parent);
+ if (!fontDialogComponent.isReady()) {
+ qmlWarning(parent) << "Failed to load non-native FontDialog implementation:\n"
+ << fontDialogComponent.errorString();
+ return;
+ }
+
+ m_dialog = qobject_cast<QQuickFontDialogImpl *>(fontDialogComponent.create());
+
+ if (!m_dialog) {
+ qmlWarning(parent) << "Failed to create an instance of the non-native FontDialog:\n"
+ << fontDialogComponent.errorString();
+ return;
+ }
+
+ m_dialog->setParent(this);
+
+ connect(m_dialog, &QQuickDialog::accepted, this, &QPlatformDialogHelper::accept);
+ connect(m_dialog, &QQuickDialog::rejected, this, &QPlatformDialogHelper::reject);
+
+ connect(m_dialog, &QQuickFontDialogImpl::currentFontChanged,
+ this, &QQuickPlatformFontDialog::currentFontChanged);
+}
+
+bool QQuickPlatformFontDialog::isValid() const
+{
+ return m_dialog;
+}
+
+void QQuickPlatformFontDialog::setCurrentFont(const QFont &font)
+{
+ if (m_dialog) {
+ if (!m_dialog->options())
+ m_dialog->setOptions(QPlatformFontDialogHelper::options());
+ m_dialog->setCurrentFont(font, true);
+ }
+}
+
+QFont QQuickPlatformFontDialog::currentFont() const
+{
+ return m_dialog ? m_dialog->currentFont() : QFont();
+}
+
+void QQuickPlatformFontDialog::exec()
+{
+ qCWarning(lcQuickPlatformFontDialog)
+ << "exec() is not supported for the Qt Quick FontDialog fallback";
+}
+
+bool QQuickPlatformFontDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality,
+ QWindow *parent)
+{
+ qCDebug(lcQuickPlatformFontDialog)
+ << "show called with flags" << flags << "modality" << modality << "parent" << parent;
+
+ if (!isValid())
+ 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<QFontDialogOptions> options = QPlatformFontDialogHelper::options();
+ m_dialog->setTitle(options->windowTitle());
+ m_dialog->setOptions(options);
+
+ m_dialog->init();
+
+ m_dialog->open();
+ return true;
+}
+
+void QQuickPlatformFontDialog::hide()
+{
+ if (isValid())
+ m_dialog->close();
+}
+
+QQuickFontDialogImpl *QQuickPlatformFontDialog::dialog() const
+{
+ return m_dialog;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickplatformfontdialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h
new file mode 100644
index 0000000000..ec7ecc7bfe
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformfontdialog_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKPLATFORMFONTDIALOG_P_H
+#define QQUICKPLATFORMFONTDIALOG_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 QQuickFontDialogImpl;
+class QWindow;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformFontDialog
+ : public QPlatformFontDialogHelper
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickPlatformFontDialog(QObject *parent);
+ ~QQuickPlatformFontDialog() = default;
+
+ bool isValid() const;
+
+ virtual void setCurrentFont(const QFont &font) override;
+ virtual QFont currentFont() const override;
+
+ void exec() override;
+ bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override;
+ void hide() override;
+
+ QQuickFontDialogImpl *dialog() const;
+
+private:
+ QQuickFontDialogImpl *m_dialog = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPLATFORMFONTDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog.cpp
new file mode 100644
index 0000000000..a9f1e858ed
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog.cpp
@@ -0,0 +1,111 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickplatformmessagedialog_p.h"
+
+#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
+#include <QtQuickTemplates2/private/qquickpopupanchors_p.h>
+
+#include "qquickmessagedialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQuickPlatformMessageDialog, "qt.quick.dialogs.quickplatformmessagedialog")
+
+QQuickPlatformMessageDialog::QQuickPlatformMessageDialog(QObject *parent)
+{
+ qCDebug(lcQuickPlatformMessageDialog)
+ << "creating non-native Qt Quick MessageDialog 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 QQuickPlatformMessageDialog; can't create "
+ "non-native MessageDialog implementation";
+ return;
+ }
+
+ const auto dialogQmlUrl = QUrl(QStringLiteral(
+ "qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/qml/MessageDialog.qml"));
+
+ QQmlComponent messageDialogComponent(qmlContext->engine(), dialogQmlUrl, parent);
+ if (!messageDialogComponent.isReady()) {
+ qmlWarning(parent) << "Failed to load non-native MessageBox implementation:\n"
+ << messageDialogComponent.errorString();
+ return;
+ }
+
+ m_dialog = qobject_cast<QQuickMessageDialogImpl *>(messageDialogComponent.create());
+
+ if (!m_dialog) {
+ qmlWarning(parent) << "Failed to create an instance of the non-native MessageBox:\n"
+ << messageDialogComponent.errorString();
+ return;
+ }
+
+ m_dialog->setParent(this);
+
+ connect(m_dialog, &QQuickDialog::accepted, this, &QPlatformDialogHelper::accept);
+ connect(m_dialog, &QQuickDialog::rejected, this, &QPlatformDialogHelper::reject);
+ connect(m_dialog, &QQuickMessageDialogImpl::buttonClicked, this,
+ &QQuickPlatformMessageDialog::clicked);
+}
+
+void QQuickPlatformMessageDialog::exec()
+{
+ qCWarning(lcQuickPlatformMessageDialog)
+ << "exec() is not supported for the Qt Quick MessageDialog fallback";
+}
+
+bool QQuickPlatformMessageDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality,
+ QWindow *parent)
+{
+ qCDebug(lcQuickPlatformMessageDialog)
+ << "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<QMessageDialogOptions> options = QPlatformMessageDialogHelper::options();
+ m_dialog->setTitle(options->windowTitle());
+ m_dialog->setOptions(options);
+
+ m_dialog->open();
+ return true;
+}
+void QQuickPlatformMessageDialog::hide()
+{
+ if (isValid())
+ m_dialog->close();
+}
+
+bool QQuickPlatformMessageDialog::isValid() const
+{
+ return m_dialog;
+}
+
+QQuickMessageDialogImpl *QQuickPlatformMessageDialog::dialog() const
+{
+ return m_dialog;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickplatformmessagedialog_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h
new file mode 100644
index 0000000000..86e44dd85d
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickplatformmessagedialog_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKPLATFORMMESSAGEDIALOG_P_H
+#define QQUICKPLATFORMMESSAGEDIALOG_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"
+#include "qquickmessagedialogimpl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickPlatformMessageDialog
+ : public QPlatformMessageDialogHelper
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickPlatformMessageDialog(QObject *parent);
+ ~QQuickPlatformMessageDialog() = default;
+
+ bool isValid() const;
+
+ void exec() override;
+ bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override;
+ void hide() override;
+
+ QQuickMessageDialogImpl *dialog() const;
+
+private:
+ QQuickMessageDialogImpl *m_dialog = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPLATFORMMESSAGEDIALOG_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker.cpp b/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker.cpp
new file mode 100644
index 0000000000..bacdf915d0
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquicksaturationlightnesspicker_p.h"
+#include "qquickabstractcolorpicker_p_p.h"
+
+#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
+#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSaturationLightnessPickerPrivate : public QQuickAbstractColorPickerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSaturationLightnessPicker)
+
+public:
+ explicit QQuickSaturationLightnessPickerPrivate();
+};
+QQuickSaturationLightnessPickerPrivate::QQuickSaturationLightnessPickerPrivate()
+{
+ m_hsl = true;
+}
+
+QQuickSaturationLightnessPicker::QQuickSaturationLightnessPicker(QQuickItem *parent)
+ : QQuickAbstractColorPicker(*(new QQuickSaturationLightnessPickerPrivate), parent)
+{
+}
+
+QColor QQuickSaturationLightnessPicker::colorAt(const QPointF &pos)
+{
+ const qreal w = width();
+ const qreal h = height();
+ if (w <= 0 || h <= 0)
+ return color();
+ const qreal x = qBound(.0, pos.x(), w);
+ const qreal y = qBound(.0, pos.y(), h);
+ const qreal saturation = 1.0 - (y / h);
+ const qreal lightness = x / w;
+
+ return QColor::fromHslF(hue(), saturation, lightness);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h b/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h
new file mode 100644
index 0000000000..1263e9173f
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qquicksaturationlightnesspicker_p.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKSATURATIONLIGHTNESSPICKER_P_H
+#define QQUICKSATURATIONLIGHTNESSPICKER_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 "qquickabstractcolorpicker_p.h"
+#include "qtquickdialogs2quickimplglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSaturationLightnessPickerPrivate;
+
+class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickSaturationLightnessPicker
+ : public QQuickAbstractColorPicker
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(SaturationLightnessPickerImpl)
+
+public:
+ explicit QQuickSaturationLightnessPicker(QQuickItem *parent = nullptr);
+
+protected:
+ QColor colorAt(const QPointF &pos) override;
+
+private:
+ Q_DISABLE_COPY(QQuickSaturationLightnessPicker)
+ Q_DECLARE_PRIVATE(QQuickSaturationLightnessPicker)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickSaturationLightnessPicker)
+
+#endif // QQUICKSATURATIONLIGHTNESSPICKER_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign.cpp b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign.cpp
new file mode 100644
index 0000000000..6a05b15c27
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtquickdialogs2quickimplforeign_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+#include "moc_qtquickdialogs2quickimplforeign_p.cpp"
diff --git a/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign_p.h b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign_p.h
new file mode 100644
index 0000000000..26537b29cb
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplforeign_p.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 QQuickFileNameFilterQuickDialogs2QuickImplForeign
+{
+ 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/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h
new file mode 100644
index 0000000000..ec593f048d
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/qtquickdialogs2quickimplglobal_p.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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>
+#include <QtQuickDialogs2QuickImpl/private/qtquickdialogs2quickimplexports_p.h>
+
+#endif // QTQUICKDIALOGS2QUICKIMPLGLOBAL_P_H
diff --git a/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag b/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag
new file mode 100644
index 0000000000..eb69d83ca6
--- /dev/null
+++ b/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#version 450
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float hue;
+};
+
+float hueToIntensity(float v1, float v2, float h) {
+ h = fract(h);
+ if (h < 1.0 / 6.0)
+ return v1 + (v2 - v1) * 6.0 * h;
+ else if (h < 1.0 / 2.0)
+ return v2;
+ else if (h < 2.0 / 3.0)
+ return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h);
+
+ return v1;
+}
+
+vec3 HSLtoRGB(vec3 color) {
+ float h = color.x;
+ float l = color.z;
+ float s = color.y;
+
+ if (s < 1.0 / 256.0)
+ return vec3(l, l, l);
+
+ float v1;
+ float v2;
+ if (l < 0.5)
+ v2 = l * (1.0 + s);
+ else
+ v2 = (l + s) - (s * l);
+
+ v1 = 2.0 * l - v2;
+
+ float d = 1.0 / 3.0;
+ float r = hueToIntensity(v1, v2, h + d);
+ float g = hueToIntensity(v1, v2, h);
+ float b = hueToIntensity(v1, v2, h - d);
+ return vec3(r, g, b);
+}
+
+void main() {
+ vec4 c = vec4(1.0);
+ c.rgb = HSLtoRGB(vec3(hue, 1.0 - qt_TexCoord0.t, qt_TexCoord0.s));
+ fragColor = c * qt_Opacity;
+}
diff --git a/src/quickdialogs/quickdialogsutils/CMakeLists.txt b/src/quickdialogs/quickdialogsutils/CMakeLists.txt
new file mode 100644
index 0000000000..32c175241b
--- /dev/null
+++ b/src/quickdialogs/quickdialogsutils/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# 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
+ qquickdialogtype_p.h
+ 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
+ GENERATE_CPP_EXPORTS
+ GENERATE_PRIVATE_CPP_EXPORTS
+)
diff --git a/src/quickdialogs/quickdialogsutils/qquickdialogtype_p.h b/src/quickdialogs/quickdialogsutils/qquickdialogtype_p.h
new file mode 100644
index 0000000000..1778ef3a14
--- /dev/null
+++ b/src/quickdialogs/quickdialogsutils/qquickdialogtype_p.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKDIALOGTYPE_P_H
+#define QQUICKDIALOGTYPE_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/qnamespace.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// We need our own type for the extra FolderDialog value, so that we can load FolderDialog.qml,
+// otherwise we would just use QPlatformTheme::DialogType and then we wouldn't need this.
+enum class QQuickDialogType {
+ FileDialog,
+ ColorDialog,
+ FontDialog,
+ MessageDialog,
+ FolderDialog
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKDIALOGTYPE_P_H
diff --git a/src/quickdialogs/quickdialogsutils/qquickfilenamefilter.cpp b/src/quickdialogs/quickdialogsutils/qquickfilenamefilter.cpp
new file mode 100644
index 0000000000..537ca1f058
--- /dev/null
+++ b/src/quickdialogs/quickdialogsutils/qquickfilenamefilter.cpp
@@ -0,0 +1,127 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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
+
+#include "moc_qquickfilenamefilter_p.cpp"
diff --git a/src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h b/src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h
new file mode 100644
index 0000000000..20722f01fa
--- /dev/null
+++ b/src/quickdialogs/quickdialogsutils/qquickfilenamefilter_p.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h b/src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h
new file mode 100644
index 0000000000..6a59111d14
--- /dev/null
+++ b/src/quickdialogs/quickdialogsutils/qtquickdialogs2utilsglobal_p.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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>
+#include <QtQuickDialogs2Utils/private/qtquickdialogs2utilsexports_p.h>
+
+#endif // QTQUICKDIALOGS2UTILSUTILSGLOBAL_P_H