diff options
Diffstat (limited to 'src/quickdialogs/quickdialogsquickimpl')
165 files changed, 14893 insertions, 0 deletions
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 Binary files differnew file mode 100644 index 0000000000..55ed3f6cf2 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/checkers.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.png Binary files differnew file mode 100644 index 0000000000..aaa5a3e431 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round.png 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 Binary files differnew file mode 100644 index 0000000000..3f66fe172b --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@3x.png Binary files differnew file mode 100644 index 0000000000..24d9f6a64e --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@4x.png Binary files differnew file mode 100644 index 0000000000..1dc83ef09f --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-round@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.png Binary files differnew file mode 100644 index 0000000000..1f7ac63e7f --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square.png 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 Binary files differnew file mode 100644 index 0000000000..524872c1e9 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@3x.png Binary files differnew file mode 100644 index 0000000000..33bce847c8 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@4x.png Binary files differnew file mode 100644 index 0000000000..185c3eb7ae --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/crumb-separator-icon-square@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.png b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.png Binary files differnew file mode 100644 index 0000000000..6aefbc8f59 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper.png 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 Binary files differnew file mode 100644 index 0000000000..69a2dac2cc --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@3x.png Binary files differnew file mode 100644 index 0000000000..2cf32fd0cb --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@4x.png Binary files differnew file mode 100644 index 0000000000..2a6ed16458 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/eye-dropper@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.png Binary files differnew file mode 100644 index 0000000000..c2a4928daf --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round.png 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 Binary files differnew file mode 100644 index 0000000000..86af70d1cf --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@3x.png Binary files differnew file mode 100644 index 0000000000..06fea29ed0 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@4x.png Binary files differnew file mode 100644 index 0000000000..09f8787314 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-round@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.png Binary files differnew file mode 100644 index 0000000000..9e8f3ddd8f --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square.png 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 Binary files differnew file mode 100644 index 0000000000..8d33067744 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@3x.png Binary files differnew file mode 100644 index 0000000000..2987d2caed --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@4x.png Binary files differnew file mode 100644 index 0000000000..69d76176e9 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/file-icon-square@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.png Binary files differnew file mode 100644 index 0000000000..60bc6c72c1 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round.png 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 Binary files differnew file mode 100644 index 0000000000..e28734374f --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@3x.png Binary files differnew file mode 100644 index 0000000000..5ee70a955e --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@4x.png Binary files differnew file mode 100644 index 0000000000..47d28a4b75 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-round@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.png Binary files differnew file mode 100644 index 0000000000..b84a90aecb --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square.png 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 Binary files differnew file mode 100644 index 0000000000..9bf320bbf0 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@3x.png Binary files differnew file mode 100644 index 0000000000..9f12edb6ee --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@4x.png Binary files differnew file mode 100644 index 0000000000..9ec5b389bd --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/folder-icon-square@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled.9.png Binary files differnew file mode 100644 index 0000000000..ce48ee7422 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled.9.png 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 Binary files differnew file mode 100644 index 0000000000..c7abb65c3f --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@2x.9.png 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 Binary files differnew file mode 100644 index 0000000000..46b84d7da4 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@3x.9.png 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 Binary files differnew file mode 100644 index 0000000000..f4dfd338f9 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-disabled@4x.9.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused.9.png Binary files differnew file mode 100644 index 0000000000..39fa866442 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused.9.png 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 Binary files differnew file mode 100644 index 0000000000..6b61562c14 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@2x.9.png 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 Binary files differnew file mode 100644 index 0000000000..e46c0bf1d9 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@3x.9.png 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 Binary files differnew file mode 100644 index 0000000000..010444e8e1 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-focused@4x.9.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png Binary files differnew file mode 100644 index 0000000000..6f565e8be2 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted.9.png 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 Binary files differnew file mode 100644 index 0000000000..9fd0a434bb --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@2x.9.png 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 Binary files differnew file mode 100644 index 0000000000..62b7435b84 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@3x.9.png 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 Binary files differnew file mode 100644 index 0000000000..96444b8905 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-highlighted@4x.9.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed.9.png Binary files differnew file mode 100644 index 0000000000..39fa866442 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed.9.png 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 Binary files differnew file mode 100644 index 0000000000..6b61562c14 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@2x.9.png 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 Binary files differnew file mode 100644 index 0000000000..e46c0bf1d9 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@3x.9.png 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 Binary files differnew file mode 100644 index 0000000000..010444e8e1 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background-pressed@4x.9.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.9.png Binary files differnew file mode 100644 index 0000000000..cef1bafab3 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background.9.png 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 Binary files differnew file mode 100644 index 0000000000..5a136a0ca9 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@2x.9.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@3x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@3x.9.png Binary files differnew file mode 100644 index 0000000000..f47a366b7b --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@3x.9.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@4x.9.png b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@4x.9.png Binary files differnew file mode 100644 index 0000000000..9ecb680f20 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/imagine/filedialogdelegate-background@4x.9.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.png Binary files differnew file mode 100644 index 0000000000..a4b7bc0383 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round.png 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 Binary files differnew file mode 100644 index 0000000000..25ee970a17 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@3x.png Binary files differnew file mode 100644 index 0000000000..486f2f46a9 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@4x.png Binary files differnew file mode 100644 index 0000000000..585402531c --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-round@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.png Binary files differnew file mode 100644 index 0000000000..f7d4151a5a --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square.png 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 Binary files differnew file mode 100644 index 0000000000..2e2a3d94eb --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@3x.png Binary files differnew file mode 100644 index 0000000000..8965148c90 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@4x.png Binary files differnew file mode 100644 index 0000000000..0a56bbcc0a --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-square@4x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.png Binary files differnew file mode 100644 index 0000000000..3f9f87624a --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square.png 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 Binary files differnew file mode 100644 index 0000000000..b4efe13228 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@2x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@3x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@3x.png Binary files differnew file mode 100644 index 0000000000..2e8302b1f0 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@3x.png diff --git a/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@4x.png b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@4x.png Binary files differnew file mode 100644 index 0000000000..5cf05ab305 --- /dev/null +++ b/src/quickdialogs/quickdialogsquickimpl/images/up-icon-thick-square@4x.png 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 ¤tFolder, 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 ¤tFolder, 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 ¤tFolder) +{ + 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; +} |