diff options
Diffstat (limited to 'src/quickdialogs/quickdialogs')
27 files changed, 2743 insertions, 0 deletions
diff --git a/src/quickdialogs/quickdialogs/CMakeLists.txt b/src/quickdialogs/quickdialogs/CMakeLists.txt new file mode 100644 index 0000000000..529d021d3c --- /dev/null +++ b/src/quickdialogs/quickdialogs/CMakeLists.txt @@ -0,0 +1,57 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## QuickDialogs2 Module: +##################################################################### + +qt_internal_add_qml_module(QuickDialogs2 + URI "QtQuick.Dialogs" + VERSION "${PROJECT_VERSION}" + CLASS_NAME QtQuickDialogsPlugin + PLUGIN_TARGET qtquickdialogsplugin + DEPENDENCIES + QtQuick/auto + SOURCES + qquickabstractdialog.cpp + qquickabstractdialog_p.h + qquickcolordialog.cpp + qquickcolordialog_p.h + qquickfiledialog.cpp + qquickfiledialog_p.h + qquickfolderdialog.cpp + qquickfolderdialog_p.h + qquickfontdialog.cpp + qquickfontdialog_p.h + qquickmessagedialog.cpp + qquickmessagedialog_p.h + qtquickdialogs2foreign.cpp + qtquickdialogs2foreign_p.h + qtquickdialogs2global_p.h + DEFINES + QT_BUILD_QUICKDIALOGS2_LIB + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR} + LIBRARIES + Qt::CorePrivate + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickPrivate + Qt::QuickControls2Impl + Qt::QuickDialogs2Utils + Qt::QuickDialogs2UtilsPrivate + Qt::QuickDialogs2QuickImpl + Qt::QuickDialogs2QuickImplPrivate + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + Qt::Quick + GENERATE_CPP_EXPORTS + GENERATE_PRIVATE_CPP_EXPORTS +) + +qt_internal_add_docs(QuickDialogs2 + doc/qtquickdialogs.qdocconf +) diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-colordialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-colordialog-gtk.png Binary files differnew file mode 100644 index 0000000000..12197f7f74 --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-colordialog-gtk.png diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-filedialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-filedialog-gtk.png Binary files differnew file mode 100644 index 0000000000..9360d747a2 --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-filedialog-gtk.png diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-folderdialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-folderdialog-gtk.png Binary files differnew file mode 100644 index 0000000000..45f0585c5d --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-folderdialog-gtk.png diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-fontdialog-gtk.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-fontdialog-gtk.png Binary files differnew file mode 100644 index 0000000000..0c6217bdb4 --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-fontdialog-gtk.png diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-android.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-android.png Binary files differnew file mode 100644 index 0000000000..3986694f7d --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-android.png diff --git a/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-informative-android.png b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-informative-android.png Binary files differnew file mode 100644 index 0000000000..b2d3cd37cb --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/images/qtquickdialogs-messagedialog-informative-android.png diff --git a/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf b/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf new file mode 100644 index 0000000000..d4f7595f0f --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/qtquickdialogs.qdocconf @@ -0,0 +1,41 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtquickcontrols2.qdocconf) + +project = QtQuickDialogs +description = Qt Quick Dialogs Reference Documentation +version = $QT_VERSION + +qhp.projects = QtQuickDialogs + +qhp.QtQuickDialogs.file = qtquickdialogs.qhp +qhp.QtQuickDialogs.namespace = org.qt-project.qtquickdialogs.$QT_VERSION_TAG +qhp.QtQuickDialogs.virtualFolder = qtquickdialogs +qhp.QtQuickDialogs.indexTitle = Qt Quick Dialogs +qhp.QtQuickDialogs.indexRoot = + +qhp.QtQuickDialogs.subprojects = qmltypes +qhp.QtQuickDialogs.subprojects.qmltypes.title = QML Types +qhp.QtQuickDialogs.subprojects.qmltypes.indexTitle = Qt Quick Dialogs QML Types +qhp.QtQuickDialogs.subprojects.qmltypes.selectors = qmlclass +qhp.QtQuickDialogs.subprojects.qmltypes.sortPages = true + +depends = qtcore qtqmlcore qtgui qtdoc qtqml qtquick qtquickcontrols qtlabsplatform + +# This module has no documented C++ types, clear the module header +moduleheader = + +exampledirs += snippets + +headerdirs += .. +sourcedirs += .. \ + src + +imagedirs += images + +navigation.landingpage = "Qt Quick Dialogs" +navigation.qmltypespage = "Qt Quick Dialogs QML Types" + +tagfile = qtquickdialogs.tags + +# Fail the documentation build if there are more warnings than the limit +warninglimit = 0 diff --git a/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml b/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml new file mode 100644 index 0000000000..ab3f33f910 --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/snippets/qtquickdialogs-filedialog.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +//! [file] +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +ApplicationWindow { + width: 640 + height: 480 + visible: true + + header: ToolBar { + Button { + text: qsTr("Choose Image...") + onClicked: fileDialog.open() + } + } + + Image { + id: image + anchors.fill: parent + fillMode: Image.PreserveAspectFit + } + + FileDialog { + id: fileDialog + currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0] + onAccepted: image.source = selectedFile + } +} +//! [file] diff --git a/src/quickdialogs/quickdialogs/doc/src/includes/fallback.qdocinc b/src/quickdialogs/quickdialogs/doc/src/includes/fallback.qdocinc new file mode 100644 index 0000000000..f3a4a1805b --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/src/includes/fallback.qdocinc @@ -0,0 +1 @@ +Qt Quick Dialogs uses a Qt Quick implementation as a fallback on platforms that do not have a native implementation available. diff --git a/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-index.qdoc b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-index.qdoc new file mode 100644 index 0000000000..bb9f9fc026 --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-index.qdoc @@ -0,0 +1,39 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtquickdialogs-index.html + \title Qt Quick Dialogs + + \brief Provides QML types for creating and interacting with system dialogs. + + The Qt Quick Dialogs module allows you to create and interact with system + dialogs from QML. The module was introduced in Qt 6.2. + + \section1 Using the Module + + \include {module-use.qdocinc} {using the qml api} {QtQuick.Dialogs} + + \section1 Reference + + \list + \li \l{Qt Quick Dialogs QML Types}{QML Types} + \endlist + + \section1 Related Modules + + \list + \li \l{Qt Quick} + \li \l{Qt Quick Controls} + \li \l{Qt Quick Templates 2} + \li \l{Qt Labs Platform} + \endlist + + \section1 License and Attributions + + Qt Quick Dialogs is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under the + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. +*/ diff --git a/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-qmltypes.qdoc b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-qmltypes.qdoc new file mode 100644 index 0000000000..422b824ec3 --- /dev/null +++ b/src/quickdialogs/quickdialogs/doc/src/qtquickdialogs-qmltypes.qdoc @@ -0,0 +1,22 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \qmlmodule QtQuick.Dialogs + \title Qt Quick Dialogs QML Types + \ingroup qmlmodules + \brief Provides QML types for creating and interacting with system dialogs. + + The Qt Quick Dialogs module allows to create and interact with system dialogs + from QML. The module was introduced in Qt 6.2. + + The QML types can be imported into your + application using the following import statement in your \c {.qml} file: + + \qml + import QtQuick.Dialogs + \endqml + + \section1 QML Types + +*/ diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp new file mode 100644 index 0000000000..857a75c7bd --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickabstractdialog.cpp @@ -0,0 +1,481 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickabstractdialog_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuickDialogs2QuickImpl/private/qquickdialogimplfactory_p.h> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcDialogs, "qt.quick.dialogs") + +/*! + \internal + + A dialog that can be backed by different implementations. + + Each dialog has a QPlatformDialogHelper handle, which is created in create(): + + - First we attempt to create a native dialog (e.g. QWindowsFileDialogHelper) through + QGuiApplicationPrivate::platformTheme()->createPlatformDialogHelper(). + - If that fails, we try to create the Qt Quick fallback dialog (e.g. QQuickPlatformFileDialog) + through QQuickDialogImplFactory::createPlatformDialogHelper(). + + The handle acts as an intermediary between the QML-facing dialog object + and the native/widget/quick implementation: + + +---------------------------+ + | FileDialog created in QML | + +---------------------------+ + | + | + v +----------------------+ + +------------------+ | attempt to create | +------+ + |useNativeDialog()?|-----false---->| QQuickPlatformDialog |---->| done | + +------------------+ | instance and set | +------+ + | | m_handle to it | + | +----------------------+ + v ^ + true | + | | + v | + +---------------------+ | + | attempt to create | | + | QWindowsFileDialog- | | + | Helper instance and | | + | set m_handle to it | | + +---------------------+ | + | | + v | + +-----------------+ | + | m_handle valid? |--------------------->false + +-----------------+ + | + v + true + | + +------+ + | done | + +------+ + + If QWindowsFileDialogHelper is created, it creates a native dialog. + If QQuickPlatformDialog is created, it creates a non-native QQuickFileDialogImpl. +*/ + +/*! + \qmltype Dialog + \inherits QtObject +//! \instantiates QQuickAbstractDialog + \inqmlmodule QtQuick.Dialogs + \since 6.2 + \brief The base class of native dialogs. + + The Dialog type provides common QML API for native platform dialogs. + For the non-native dialog, see \l [QML QtQuickControls]{Dialog}. + + To show a native dialog, construct an instance of one of the concrete + Dialog implementations, set the desired properties, and call \l open(). + Dialog emits \l accepted() or \l rejected() when the user is done with + the dialog. +*/ + +/*! + \qmlsignal void QtQuick.Dialogs::Dialog::accepted() + + This signal is emitted when the dialog has been accepted either + interactively or by calling \l accept(). + + \sa rejected() +*/ + +/*! + \qmlsignal void QtQuick.Dialogs::Dialog::rejected() + + This signal is emitted when the dialog has been rejected either + interactively or by calling \l reject(). + + This signal is also emitted when closing the dialog with \l close(). + + \sa accepted() +*/ + +Q_DECLARE_LOGGING_CATEGORY(lcDialogs) + +QQuickAbstractDialog::QQuickAbstractDialog(QQuickDialogType type, QObject *parent) + : QObject(parent), + m_type(type) +{ +} + +QQuickAbstractDialog::~QQuickAbstractDialog() +{ + destroy(); +} + +QPlatformDialogHelper *QQuickAbstractDialog::handle() const +{ + return m_handle.get(); +} + +/*! + \qmldefault + \qmlproperty list<QtObject> QtQuick.Dialogs::Dialog::data + + This default property holds the list of all objects declared as children of + the dialog. +*/ +QQmlListProperty<QObject> QQuickAbstractDialog::data() +{ + return QQmlListProperty<QObject>(this, &m_data); +} + +/*! + \qmlproperty Window QtQuick.Dialogs::Dialog::parentWindow + + This property holds the parent window of the dialog. + + Unless explicitly set, the window is automatically resolved by iterating + the QML parent objects until a \l Window or an \l Item that has a window + is found. +*/ +QWindow *QQuickAbstractDialog::parentWindow() const +{ + return m_parentWindow; +} + +void QQuickAbstractDialog::setParentWindow(QWindow *window) +{ + qCDebug(lcDialogs) << "set parent window to" << window; + if (m_parentWindow == window) + return; + + m_parentWindow = window; + emit parentWindowChanged(); +} + +/*! + \qmlproperty string QtQuick.Dialogs::Dialog::title + + This property holds the title of the dialog. +*/ +QString QQuickAbstractDialog::title() const +{ + return m_title; +} + +void QQuickAbstractDialog::setTitle(const QString &title) +{ + if (m_title == title) + return; + + m_title = title; + emit titleChanged(); +} + +/*! + \qmlproperty Qt::WindowFlags QtQuick.Dialogs::Dialog::flags + + This property holds the window flags of the dialog. The default value is \c Qt.Dialog. +*/ +Qt::WindowFlags QQuickAbstractDialog::flags() const +{ + return m_flags; +} + +void QQuickAbstractDialog::setFlags(Qt::WindowFlags flags) +{ + if (m_flags == flags) + return; + + m_flags = flags; + emit flagsChanged(); +} + +/*! + \qmlproperty Qt::WindowModality QtQuick.Dialogs::Dialog::modality + + This property holds the modality of the dialog. The default value is \c Qt.WindowModal. + + Available values: + \value Qt.NonModal The dialog is not modal and does not block input to other windows. + \value Qt.WindowModal The dialog is modal to a single window hierarchy and blocks input to its parent window, all grandparent windows, and all siblings of its parent and grandparent windows. + \value Qt.ApplicationModal The dialog is modal to the application and blocks input to all windows. +*/ +Qt::WindowModality QQuickAbstractDialog::modality() const +{ + return m_modality; +} + +void QQuickAbstractDialog::setModality(Qt::WindowModality modality) +{ + if (m_modality == modality) + return; + + m_modality = modality; + emit modalityChanged(); +} + +/*! + \qmlproperty bool QtQuick.Dialogs::Dialog::visible + + This property holds the visibility of the dialog. The default value is \c false. + + \sa open(), close() +*/ +bool QQuickAbstractDialog::isVisible() const +{ + return m_handle && m_visible; +} + +void QQuickAbstractDialog::setVisible(bool visible) +{ + qCDebug(lcDialogs) << "setVisible called with" << visible; + + if (visible) { + // Don't try to open before component completion, as we won't have a window yet, + // and open() sets m_visible to false if it fails. + if (!m_complete) + m_visibleRequested = true; + else + open(); + } else { + close(); + } +} + +/*! + \qmlproperty StandardCode QtQuick.Dialogs::Dialog::result + + This property holds the result code. + + Standard result codes: + \value Dialog.Accepted + \value Dialog.Rejected + + \note MessageDialog sets the result to the value of the clicked standard + button instead of using the standard result codes. +*/ +QQuickAbstractDialog::StandardCode QQuickAbstractDialog::result() const +{ + return m_result; +} + +void QQuickAbstractDialog::setResult(StandardCode result) +{ + if (m_result == result) + return; + + m_result = result; + emit resultChanged(); +} + +/*! + \qmlmethod void QtQuick.Dialogs::Dialog::open() + + Opens the dialog. + + \sa visible, close() +*/ +void QQuickAbstractDialog::open() +{ + qCDebug(lcDialogs) << "open called"; + if (m_visible || !create()) + return; + + onShow(m_handle.get()); + m_visible = m_handle->show(m_flags, m_modality, m_parentWindow); + if (m_visible) { + m_result = Rejected; // in case an accepted dialog gets re-opened, then closed + emit visibleChanged(); + } +} + +/*! + \qmlmethod void QtQuick.Dialogs::Dialog::close() + + Closes the dialog and emits either the \l accepted() or \l rejected() + signal. + + \sa visible, open() +*/ +void QQuickAbstractDialog::close() +{ + if (!m_handle || !m_visible) + return; + + onHide(m_handle.get()); + m_handle->hide(); + m_visible = false; + emit visibleChanged(); + + if (m_result == Accepted) + emit accepted(); + else // if (m_result == Rejected) + emit rejected(); +} + +/*! + \qmlmethod void QtQuick.Dialogs::Dialog::accept() + + Closes the dialog and emits the \l accepted() signal. + + \sa reject() +*/ +void QQuickAbstractDialog::accept() +{ + done(Accepted); +} + +/*! + \qmlmethod void QtQuick.Dialogs::Dialog::reject() + + Closes the dialog and emits the \l rejected() signal. + + \sa accept() +*/ +void QQuickAbstractDialog::reject() +{ + done(Rejected); +} + +/*! + \qmlmethod void QtQuick.Dialogs::Dialog::done(StandardCode result) + + Closes the dialog and sets the \a result. + + \sa accept(), reject(), result +*/ +void QQuickAbstractDialog::done(StandardCode result) +{ + setResult(result); + close(); +} + +void QQuickAbstractDialog::classBegin() +{ +} + +void QQuickAbstractDialog::componentComplete() +{ + qCDebug(lcDialogs) << "componentComplete"; + m_complete = true; + + if (!m_parentWindow) { + qCDebug(lcDialogs) << "- no parent window; searching for one"; + setParentWindow(findParentWindow()); + } + + if (m_visibleRequested) { + qCDebug(lcDialogs) << "visible was bound to true before component completion; opening dialog"; + open(); + m_visibleRequested = false; + } +} + +static const char *qmlTypeName(const QObject *object) +{ + return object->metaObject()->className() + qstrlen("QQuickPlatform"); +} + +QPlatformTheme::DialogType toPlatformDialogType(QQuickDialogType quickDialogType) +{ + return quickDialogType == QQuickDialogType::FolderDialog + ? QPlatformTheme::FileDialog : static_cast<QPlatformTheme::DialogType>(quickDialogType); +} + +bool QQuickAbstractDialog::create() +{ + qCDebug(lcDialogs) << qmlTypeName(this) << "attempting to create dialog backend of type" + << int(m_type) << "with parent window" << m_parentWindow; + if (m_handle) + return m_handle.get(); + + qCDebug(lcDialogs) << "- attempting to create a native dialog"; + if (useNativeDialog()) { + m_handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformDialogHelper( + toPlatformDialogType(m_type))); + } + + if (!m_handle) { + qCDebug(lcDialogs) << "- attempting to create a quick dialog"; + m_handle = QQuickDialogImplFactory::createPlatformDialogHelper(m_type, this); + } + + qCDebug(lcDialogs) << qmlTypeName(this) << "created ->" << m_handle.get(); + if (m_handle) { + onCreate(m_handle.get()); + connect(m_handle.get(), &QPlatformDialogHelper::accept, this, &QQuickAbstractDialog::accept); + connect(m_handle.get(), &QPlatformDialogHelper::reject, this, &QQuickAbstractDialog::reject); + } + return m_handle.get(); +} + +void QQuickAbstractDialog::destroy() +{ + m_handle.reset(); +} + +bool QQuickAbstractDialog::useNativeDialog() const +{ + if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)) { + qCDebug(lcDialogs) << " - Qt::AA_DontUseNativeDialogs was set; not using native dialog"; + return false; + } + + if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(toPlatformDialogType(m_type))) { + qCDebug(lcDialogs) << " - the platform theme told us a native dialog isn't available; not using native dialog"; + return false; + } + + return true; +} + +/*! + \internal + + Called at the end of \l create(). +*/ +void QQuickAbstractDialog::onCreate(QPlatformDialogHelper *dialog) +{ + Q_UNUSED(dialog); +} + +/*! + \internal + + Called by \l open(), after the call to \l create() and before + the handle/helper's \c show function is called. +*/ +void QQuickAbstractDialog::onShow(QPlatformDialogHelper *dialog) +{ + Q_UNUSED(dialog); + m_firstShow = false; +} + +void QQuickAbstractDialog::onHide(QPlatformDialogHelper *dialog) +{ + Q_UNUSED(dialog); +} + +QWindow *QQuickAbstractDialog::findParentWindow() const +{ + QObject *obj = parent(); + while (obj) { + QWindow *window = qobject_cast<QWindow *>(obj); + if (window) + return window; + QQuickItem *item = qobject_cast<QQuickItem *>(obj); + if (item && item->window()) + return item->window(); + obj = obj->parent(); + } + return nullptr; +} + +QT_END_NAMESPACE + +#include "moc_qquickabstractdialog_p.cpp" diff --git a/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h new file mode 100644 index 0000000000..f1d046eb89 --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickabstractdialog_p.h @@ -0,0 +1,129 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKABSTRACTDIALOG_P_H +#define QQUICKABSTRACTDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <memory> + +#include <QtCore/qobject.h> +#include <QtGui/qpa/qplatformtheme.h> +#include <QtGui/qpa/qplatformdialoghelper.h> +#include <QtQml/qqmlparserstatus.h> +#include <QtQml/qqmllist.h> +#include <QtQml/qqml.h> +#include <QtQuickDialogs2Utils/private/qquickdialogtype_p.h> + +#include "qtquickdialogs2global_p.h" + +QT_BEGIN_NAMESPACE + +class QWindow; +class QPlatformDialogHelper; + +class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickAbstractDialog : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QQmlListProperty<QObject> data READ data FINAL) + Q_PROPERTY(QWindow *parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged FINAL) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL) + Q_PROPERTY(Qt::WindowFlags flags READ flags WRITE setFlags NOTIFY flagsChanged FINAL) + Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged FINAL) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL) + Q_PROPERTY(StandardCode result READ result WRITE setResult NOTIFY resultChanged FINAL) + Q_CLASSINFO("DefaultProperty", "data") + Q_MOC_INCLUDE(<QtGui/qwindow.h>) + QML_ANONYMOUS + QML_ADDED_IN_VERSION(6, 2) + +public: + explicit QQuickAbstractDialog(QQuickDialogType type, QObject *parent = nullptr); + ~QQuickAbstractDialog(); + + QPlatformDialogHelper *handle() const; + + QQmlListProperty<QObject> data(); + + QWindow *parentWindow() const; + void setParentWindow(QWindow *window); + + QString title() const; + void setTitle(const QString &title); + + Qt::WindowFlags flags() const; + void setFlags(Qt::WindowFlags flags); + + Qt::WindowModality modality() const; + void setModality(Qt::WindowModality modality); + + bool isVisible() const; + void setVisible(bool visible); + + enum StandardCode { Rejected, Accepted }; + Q_ENUM(StandardCode) + + StandardCode result() const; + void setResult(StandardCode result); + +public Q_SLOTS: + void open(); + void close(); + virtual void accept(); + virtual void reject(); + virtual void done(StandardCode result); + +Q_SIGNALS: + void accepted(); + void rejected(); + void parentWindowChanged(); + void titleChanged(); + void flagsChanged(); + void modalityChanged(); + void visibleChanged(); + void resultChanged(); + +protected: + void classBegin() override; + void componentComplete() override; + + bool create(); + void destroy(); + + virtual bool useNativeDialog() const; + virtual void onCreate(QPlatformDialogHelper *dialog); + virtual void onShow(QPlatformDialogHelper *dialog); + virtual void onHide(QPlatformDialogHelper *dialog); + + QWindow *findParentWindow() const; + + bool m_visibleRequested = false; + bool m_visible = false; + bool m_complete = false; + bool m_firstShow = true; + StandardCode m_result = Rejected; + QWindow *m_parentWindow = nullptr; + QString m_title; + Qt::WindowFlags m_flags = Qt::Dialog; + Qt::WindowModality m_modality = Qt::WindowModal; + QQuickDialogType m_type = QQuickDialogType::FileDialog; + QList<QObject *> m_data; + std::unique_ptr<QPlatformDialogHelper> m_handle; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickAbstractDialog) + +#endif // QQUICKABSTRACTDIALOG_P_H diff --git a/src/quickdialogs/quickdialogs/qquickcolordialog.cpp b/src/quickdialogs/quickdialogs/qquickcolordialog.cpp new file mode 100644 index 0000000000..e91965e69d --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickcolordialog.cpp @@ -0,0 +1,152 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickcolordialog_p.h" + +#include <QtCore/qloggingcategory.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ColorDialog + \inherits Dialog + \inqmlmodule QtQuick.Dialogs + \since 6.4 + \brief A color dialog. + + The ColorDialog type provides a QML API for color dialogs. + + \image qtquickdialogs-colordialog-gtk.png + + To show a color dialog, construct an instance of ColorDialog, set the + desired properties, and call \l {Dialog::}{open()}. The \l selectedColor + property can be used to determine the initially selected color in the + dialog. + + \code + MenuItem { + text: qsTr("Color") + onTriggered: colorDialog.open() + } + + ColorDialog { + id: colorDialog + selectedColor: document.color + onAccepted: document.color = selectedColor + } + + MyDocument { + id: document + } + \endcode + + \section2 Availability + + A native platform color dialog is currently available on the following platforms: + + \list + \li macOS + \li Linux (when running with the GTK+ platform theme) + \endlist + + \include includes/fallback.qdocinc +*/ + +QQuickColorDialog::QQuickColorDialog(QObject *parent) + : QQuickAbstractDialog(QQuickDialogType::ColorDialog, parent), + m_options(QColorDialogOptions::create()), + m_selectedColor(QColorConstants::White) +{ +} + +/*! + \qmlproperty color QtQuick.Dialogs::ColorDialog::selectedColor + + This property holds the currently selected color in the dialog. + + The \l {Dialog::}{accepted()} signal can be handled to get the final selection. + When the user has clicked \uicontrol Open to accept a color, a signal handler + for the \l {Dialog::}{accepted()} signal can query the selectedColor property to + get the final color that was selected by the user. + + \sa {Dialog::}{accepted()} +*/ +QColor QQuickColorDialog::selectedColor() const +{ + return m_selectedColor; +} + +void QQuickColorDialog::setSelectedColor(const QColor &color) +{ + if (color == m_selectedColor) + return; + + m_selectedColor = color; + + emit selectedColorChanged(); +} + +/*! + \qmlproperty flags QtQuick.Dialogs::ColorDialog::options + + This property holds the various options that affect the look and feel of the dialog. + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the dialog is + visible is not guaranteed to have an immediate effect on the dialog (depending on + the option and on the platform). + + Available options: + \value ColorDialog.ShowAlphaChannel Show a slider and additional input fields for the alpha value. + \value ColorDialog.NoButtons Don't display \uicontrol Open and \uicontrol Cancel buttons (useful + for "live dialogs"). + \value ColorDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation. +*/ + +QColorDialogOptions::ColorDialogOptions QQuickColorDialog::options() const +{ + return m_options->options(); +} + +void QQuickColorDialog::setOptions(QColorDialogOptions::ColorDialogOptions options) +{ + if (options == m_options->options()) + return; + + m_options->setOptions(options); + emit optionsChanged(); +} + +void QQuickColorDialog::resetOptions() +{ + setOptions({}); +} + +bool QQuickColorDialog::useNativeDialog() const +{ + return QQuickAbstractDialog::useNativeDialog() + && !(m_options->testOption(QColorDialogOptions::DontUseNativeDialog)); +} + +void QQuickColorDialog::onCreate(QPlatformDialogHelper *dialog) +{ + if (auto colorDialog = qobject_cast<QPlatformColorDialogHelper *>(dialog)) { + connect(colorDialog, &QPlatformColorDialogHelper::currentColorChanged, this, + [this, colorDialog]() { setSelectedColor(colorDialog->currentColor()); }); + colorDialog->setOptions(m_options); + } +} + +void QQuickColorDialog::onShow(QPlatformDialogHelper *dialog) +{ + m_options->setWindowTitle(title()); + if (auto colorDialog = qobject_cast<QPlatformColorDialogHelper *>(dialog)) { + colorDialog->setOptions(m_options); + colorDialog->setCurrentColor(m_selectedColor); + } + + QQuickAbstractDialog::onShow(dialog); +} + +QT_END_NAMESPACE diff --git a/src/quickdialogs/quickdialogs/qquickcolordialog_p.h b/src/quickdialogs/quickdialogs/qquickcolordialog_p.h new file mode 100644 index 0000000000..9b424d6b7e --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickcolordialog_p.h @@ -0,0 +1,61 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKCOLORDIALOG_P_H +#define QQUICKCOLORDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickabstractdialog_p.h" + +#include <QtGui/qcolor.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickColorDialog : public QQuickAbstractDialog +{ + Q_OBJECT + Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged) + Q_PROPERTY(QColorDialogOptions::ColorDialogOptions options READ options WRITE setOptions RESET resetOptions NOTIFY optionsChanged) + Q_FLAGS(QColorDialogOptions::ColorDialogOptions) + QML_NAMED_ELEMENT(ColorDialog) + QML_ADDED_IN_VERSION(6, 4) + +public: + explicit QQuickColorDialog(QObject *parent = nullptr); + + QColor selectedColor() const; + void setSelectedColor(const QColor &color); + + QColorDialogOptions::ColorDialogOptions options() const; + void setOptions(QColorDialogOptions::ColorDialogOptions options); + void resetOptions(); + +Q_SIGNALS: + void selectedColorChanged(); + void optionsChanged(); + +protected: + bool useNativeDialog() const override; + void onCreate(QPlatformDialogHelper *dialog) override; + void onShow(QPlatformDialogHelper *dialog) override; + +private: + QSharedPointer<QColorDialogOptions> m_options; + QColor m_selectedColor; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickColorDialog) + +#endif // QQUICKCOLORDIALOG_P_H diff --git a/src/quickdialogs/quickdialogs/qquickfiledialog.cpp b/src/quickdialogs/quickdialogs/qquickfiledialog.cpp new file mode 100644 index 0000000000..6b1cdb860c --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickfiledialog.cpp @@ -0,0 +1,601 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickfiledialog_p.h" + +#include <QtCore/qlist.h> +#include <QtCore/qloggingcategory.h> +#include <QtQml/qqmlfile.h> +#include <QtQml/qqmlinfo.h> + +#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +Q_DECLARE_LOGGING_CATEGORY(lcDialogs) +Q_LOGGING_CATEGORY(lcFileDialog, "qt.quick.dialogs.filedialog") + +/*! + \qmltype FileDialog + \inherits Dialog +//! \instantiates QQuickFileDialog + \inqmlmodule QtQuick.Dialogs + \since 6.2 + \brief A file dialog. + + The FileDialog type provides a QML API for file dialogs. + + \image qtquickdialogs-filedialog-gtk.png + + To show a file dialog, construct an instance of FileDialog, set the + desired properties, and call \l {Dialog::}{open()}. The \l currentFile + or \l currentFiles properties can be used to determine the currently + selected file(s) in the dialog. The \l selectedFile and \l selectedFiles + properties are updated only after the final selection has been made by + accepting the dialog. + + \snippet qtquickdialogs-filedialog.qml file + + \section2 Availability + + A native platform file dialog is currently available on the following platforms: + + \list + \li iOS + \li Android + \li Linux (when running with the GTK+ platform theme) + \li macOS + \li Windows + \endlist + + \include includes/fallback.qdocinc + + \sa FolderDialog, {QtCore::}{StandardPaths} +*/ + +QQuickFileDialog::QQuickFileDialog(QObject *parent) + : QQuickAbstractDialog(QQuickDialogType::FileDialog, parent), + m_fileMode(OpenFile), + m_options(QFileDialogOptions::create()), + m_selectedNameFilter(nullptr) +{ + m_options->setFileMode(QFileDialogOptions::ExistingFile); + m_options->setAcceptMode(QFileDialogOptions::AcceptOpen); +} + +/*! + \qmlproperty enumeration QtQuick.Dialogs::FileDialog::fileMode + + This property holds the mode of the dialog. + + Available values: + \value FileDialog.OpenFile The dialog is used to select an existing file (default). + \value FileDialog.OpenFiles The dialog is used to select multiple existing files. + \value FileDialog.SaveFile The dialog is used to select any file. The file does not have to exist. +*/ +QQuickFileDialog::FileMode QQuickFileDialog::fileMode() const +{ + return m_fileMode; +} + +void QQuickFileDialog::setFileMode(FileMode mode) +{ + qCDebug(lcFileDialog) << "setFileMode called with" << mode; + if (mode == m_fileMode) + return; + + switch (mode) { + case OpenFile: + m_options->setFileMode(QFileDialogOptions::ExistingFile); + m_options->setAcceptMode(QFileDialogOptions::AcceptOpen); + break; + case OpenFiles: + m_options->setFileMode(QFileDialogOptions::ExistingFiles); + m_options->setAcceptMode(QFileDialogOptions::AcceptOpen); + break; + case SaveFile: + m_options->setFileMode(QFileDialogOptions::AnyFile); + m_options->setAcceptMode(QFileDialogOptions::AcceptSave); + break; + default: + break; + } + + m_fileMode = mode; + emit fileModeChanged(); +} + +/*! + \qmlproperty url QtQuick.Dialogs::FileDialog::selectedFile + + This property holds the last file that was selected in the dialog. + + It can be set to control the file that is selected when the dialog is + opened. + + If there are multiple selected files, this property refers to the first + file. + + The value of this property is updated each time the user selects a file in + the dialog, and when the dialog is accepted. Handle the + \l {Dialog::}{accepted()} signal to get the final selection. + + \sa selectedFiles, {Dialog::}{accepted()}, currentFolder +*/ +QUrl QQuickFileDialog::selectedFile() const +{ + return addDefaultSuffix(m_selectedFiles.value(0)); +} + +void QQuickFileDialog::setSelectedFile(const QUrl &selectedFile) +{ + setSelectedFiles({ selectedFile }); +} + +/*! + \qmlproperty list<url> QtQuick.Dialogs::FileDialog::selectedFiles + + This property holds the last files that were selected in the dialog. + + The value of this property is updated each time the user selects files in + the dialog, and when the dialog is accepted. Handle the + \l {Dialog::}{accepted()} signal to get the final selection. + + \sa {Dialog::}{accepted()}, currentFolder +*/ +QList<QUrl> QQuickFileDialog::selectedFiles() const +{ + return addDefaultSuffixes(m_selectedFiles); +} + +void QQuickFileDialog::setSelectedFiles(const QList<QUrl> &selectedFiles) +{ + qCDebug(lcFileDialog) << "setSelectedFiles called with" << selectedFiles; + if (m_selectedFiles == selectedFiles) + return; + + if (m_fileMode == SaveFile && selectedFiles.size() > 1) { + qmlWarning(this) << "Cannot set more than one selected file when fileMode is SaveFile"; + return; + } + + if (m_fileMode != SaveFile) { + for (const auto &selectedFile : selectedFiles) { + const QString selectedFilePath = QQmlFile::urlToLocalFileOrQrc(selectedFile); + if (!QFileInfo::exists(selectedFilePath)) { + qmlWarning(this) << "Cannot set " << selectedFilePath + << " as a selected file because it doesn't exist"; + return; + } + } + } + + const auto newFirstSelectedFile = selectedFiles.value(0); + const bool firstChanged = m_selectedFiles.value(0) != newFirstSelectedFile; + m_selectedFiles = selectedFiles; + m_options->setInitiallySelectedFiles(m_selectedFiles); + if (firstChanged) { + emit selectedFileChanged(); + emit currentFileChanged(); + } + emit selectedFilesChanged(); + emit currentFilesChanged(); +} + +/*! + \qmlproperty url QtQuick.Dialogs::FileDialog::currentFile + \deprecated [6.3] Use \l selectedFile instead. + + This property holds the currently selected file in the dialog. + + \sa selectedFile, currentFiles, currentFolder +*/ +QUrl QQuickFileDialog::currentFile() const +{ + return selectedFile(); +} + +void QQuickFileDialog::setCurrentFile(const QUrl &file) +{ + setSelectedFiles(QList<QUrl>() << file); +} + +/*! + \qmlproperty list<url> QtQuick.Dialogs::FileDialog::currentFiles + \deprecated [6.3] Use \l selectedFiles instead. + + This property holds the currently selected files in the dialog. + + \sa selectedFiles, currentFile, currentFolder +*/ +QList<QUrl> QQuickFileDialog::currentFiles() const +{ + return selectedFiles(); +} + +void QQuickFileDialog::setCurrentFiles(const QList<QUrl> ¤tFiles) +{ + setSelectedFiles(currentFiles); +} + +/*! + \qmlproperty url QtQuick.Dialogs::FileDialog::currentFolder + + This property holds the folder where files are selected. It can be set to + control the initial directory that is shown when the dialog is opened. + + For selecting a folder, use \l FolderDialog instead. +*/ +QUrl QQuickFileDialog::currentFolder() const +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) + return fileDialog->directory(); + + // If we're not using a native file dialog and the folder is invalid, + // return the current directory. + if (!m_options->initialDirectory().isValid()) + return QUrl::fromLocalFile(QDir::currentPath()); + + return m_options->initialDirectory(); +} + +void QQuickFileDialog::setCurrentFolder(const QUrl ¤tFolder) +{ + qCDebug(lcFileDialog) << "setCurrentFolder called with" << currentFolder; + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) + fileDialog->setDirectory(currentFolder); + m_options->setInitialDirectory(currentFolder); +} + +/*! + \qmlproperty flags QtQuick.Dialogs::FileDialog::options + + This property holds the various options that affect the look and feel of the dialog. + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the dialog is + visible is not guaranteed to have an immediate effect on the dialog (depending on + the option and on the platform). + + Available options: + \value FileDialog.DontResolveSymlinks Don't resolve symlinks in the file dialog. By default symlinks are resolved. + \value FileDialog.DontConfirmOverwrite Don't ask for confirmation if an existing file is selected. By default confirmation is requested. + \value FileDialog.ReadOnly Indicates that the dialog doesn't allow creating directories. + \value FileDialog.HideNameFilterDetails Indicates if the file name filter details are hidden or not. + \value FileDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation. +*/ +QFileDialogOptions::FileDialogOptions QQuickFileDialog::options() const +{ + return m_options->options(); +} + +void QQuickFileDialog::setOptions(QFileDialogOptions::FileDialogOptions options) +{ + if (options == m_options->options()) + return; + + m_options->setOptions(options); + emit optionsChanged(); +} + +void QQuickFileDialog::resetOptions() +{ + setOptions({}); +} + +/*! + \qmlproperty list<string> QtQuick.Dialogs::FileDialog::nameFilters + + This property holds the filters that restrict the types of files that + can be selected. + + \code + FileDialog { + nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)"] + } + \endcode + + Different platforms may restrict the files that can be selected in + different ways. For example, macOS will disable file entries that do not + match the filters, whereas Windows will hide them. + + \note \b{*.*} is not a portable filter, because the historical assumption + that the file extension determines the file type is not consistent on every + operating system. It is possible to have a file with no dot in its name (for + example, \c Makefile). In a native Windows file dialog, \b{*.*} will match + such files, while in other types of file dialogs it may not. So it is better + to use \b{*} if you mean to select any file. + + \sa selectedNameFilter +*/ +QStringList QQuickFileDialog::nameFilters() const +{ + return m_options->nameFilters(); +} + +void QQuickFileDialog::setNameFilters(const QStringList &filters) +{ + qCDebug(lcFileDialog).nospace() << "setNameFilters called with " << filters + << " (old filters were: " << m_options->nameFilters() << ")"; + if (filters == m_options->nameFilters()) + return; + + m_options->setNameFilters(filters); + if (m_selectedNameFilter) { + int index = m_selectedNameFilter->index(); + if (index < 0 || index >= filters.size()) + index = 0; + m_selectedNameFilter->update(filters.value(index)); + } + emit nameFiltersChanged(); +} + +void QQuickFileDialog::resetNameFilters() +{ + setNameFilters(QStringList()); +} + +/*! + \qmlproperty int QtQuick.Dialogs::FileDialog::selectedNameFilter.index + \qmlproperty string QtQuick.Dialogs::FileDialog::selectedNameFilter.name + \qmlproperty list<string> QtQuick.Dialogs::FileDialog::selectedNameFilter.extensions + \qmlproperty list<string> QtQuick.Dialogs::FileDialog::selectedNameFilter.globs + + These properties hold the currently selected name filter. + + \table + \header + \li Name + \li Description + \row + \li \b index : int + \li This property determines which \l {nameFilters}{name filter} is selected. + The specified filter is selected when the dialog is opened. The value is + updated when the user selects another filter. + \row + \li [read-only] \b name : string + \li This property holds the name of the selected filter. In the + example below, the name of the first filter is \c {"Text files"} + and the second is \c {"HTML files"}. + \row + \li [read-only] \b extensions : list<string> + \li This property holds the list of extensions of the selected filter. + In the example below, the list of extensions of the first filter is + \c {["txt"]} and the second is \c {["html", "htm"]}. + \row + \li [read-only] \b globs : list<string> + \li This property holds the list of globs of the selected filter. + In the example below, the list of globs of the first filter is + \c {["*.txt"]} and the second is \c {["*.html", "*.htm"]}. + + This property is useful in conjunction with \l {FolderListModel}'s + \l {FolderListModel::}{nameFilters} property, for example. + \endtable + + \code + FileDialog { + id: fileDialog + selectedNameFilter.index: 1 + nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)"] + } + + MyDocument { + id: document + fileType: fileDialog.selectedNameFilter.extensions[0] + } + \endcode + + \sa nameFilters +*/ +QQuickFileNameFilter *QQuickFileDialog::selectedNameFilter() const +{ + if (!m_selectedNameFilter) { + QQuickFileDialog *that = const_cast<QQuickFileDialog *>(this); + m_selectedNameFilter = new QQuickFileNameFilter(that); + m_selectedNameFilter->setOptions(m_options); + } + return m_selectedNameFilter; +} + +/*! + \qmlproperty string QtQuick.Dialogs::FileDialog::defaultSuffix + + This property holds a suffix that is added to selected files that have + no suffix specified. The suffix is typically used to indicate the file + type (e.g. "txt" indicates a text file). + + If the first character is a dot ('.'), it is removed. +*/ +QString QQuickFileDialog::defaultSuffix() const +{ + return m_options->defaultSuffix(); +} + +void QQuickFileDialog::setDefaultSuffix(const QString &suffix) +{ + if (suffix == m_options->defaultSuffix()) + return; + + m_options->setDefaultSuffix(suffix); + emit defaultSuffixChanged(); +} + +void QQuickFileDialog::resetDefaultSuffix() +{ + setDefaultSuffix(QString()); +} + +/*! + \qmlproperty string QtQuick.Dialogs::FileDialog::acceptLabel + + This property holds the label text shown on the button that accepts the dialog. + + When set to an empty string, the default label of the underlying platform is used. + The default label is typically \uicontrol Open or \uicontrol Save depending on which + \l fileMode the dialog is used in. + + The default value is an empty string. + + \sa rejectLabel +*/ +QString QQuickFileDialog::acceptLabel() const +{ + return m_options->labelText(QFileDialogOptions::Accept); +} + +void QQuickFileDialog::setAcceptLabel(const QString &label) +{ + if (label == m_options->labelText(QFileDialogOptions::Accept)) + return; + + m_options->setLabelText(QFileDialogOptions::Accept, label); + emit acceptLabelChanged(); +} + +void QQuickFileDialog::resetAcceptLabel() +{ + setAcceptLabel(QString()); +} + +/*! + \qmlproperty string QtQuick.Dialogs::FileDialog::rejectLabel + + This property holds the label text shown on the button that rejects the dialog. + + When set to an empty string, the default label of the underlying platform is used. + The default label is typically \uicontrol Cancel. + + The default value is an empty string. + + \sa acceptLabel +*/ +QString QQuickFileDialog::rejectLabel() const +{ + return m_options->labelText(QFileDialogOptions::Reject); +} + +void QQuickFileDialog::setRejectLabel(const QString &label) +{ + if (label == m_options->labelText(QFileDialogOptions::Reject)) + return; + + m_options->setLabelText(QFileDialogOptions::Reject, label); + emit rejectLabelChanged(); +} + +void QQuickFileDialog::resetRejectLabel() +{ + setRejectLabel(QString()); +} + +bool QQuickFileDialog::useNativeDialog() const +{ + if (!QQuickAbstractDialog::useNativeDialog()) + return false; + + if (m_options->testOption(QFileDialogOptions::DontUseNativeDialog)) { + qCDebug(lcDialogs) << " - the FileDialog was told not to use a native dialog; not using native dialog"; + return false; + } + + return true; +} + +void QQuickFileDialog::onCreate(QPlatformDialogHelper *dialog) +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) { + connect(fileDialog, &QPlatformFileDialogHelper::currentChanged, + this, [this, fileDialog](){ setSelectedFiles(fileDialog->selectedFiles()); }); + connect(fileDialog, &QPlatformFileDialogHelper::directoryEntered, this, &QQuickFileDialog::currentFolderChanged); + fileDialog->setOptions(m_options); + + // If the user didn't set an initial selectedFile, ensure that we are synced + // with the underlying dialog in case it has set an initially selected file + // (as QQuickFileDialogImplPrivate::updateSelectedFile does). + if (m_options->initiallySelectedFiles().isEmpty()) { + const auto selectedFiles = fileDialog->selectedFiles(); + if (!selectedFiles.isEmpty()) + setSelectedFiles(selectedFiles); + } + } +} + +void QQuickFileDialog::onShow(QPlatformDialogHelper *dialog) +{ + m_options->setWindowTitle(title()); + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) { + // Ensure that a name filter is always selected. + int index = selectedNameFilter()->index(); + if (index == -1) + index = 0; + const QString filter = m_options->nameFilters().value(index); + m_options->setInitiallySelectedNameFilter(filter); + + fileDialog->setOptions(m_options); // setOptions only assigns a member and isn't virtual + + connect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickFileNameFilter::update); + fileDialog->selectNameFilter(filter); + + // If both selectedFile and currentFolder are set, prefer the former. + if (!m_options->initiallySelectedFiles().isEmpty()) { + // The user set an initial selectedFile. + const QUrl selectedFile = m_options->initiallySelectedFiles().first(); + fileDialog->selectFile(selectedFile); + } else { + // The user set an initial currentFolder. + const QUrl initialDir = m_options->initialDirectory(); + // If it's not valid, or it's a file and not a directory, we shouldn't set it. + if (m_firstShow && initialDir.isValid() && QDir(QQmlFile::urlToLocalFileOrQrc(initialDir)).exists()) + fileDialog->setDirectory(m_options->initialDirectory()); + } + } + QQuickAbstractDialog::onShow(dialog); +} + +void QQuickFileDialog::onHide(QPlatformDialogHelper *dialog) +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) { + if (m_selectedNameFilter) + disconnect(fileDialog, &QPlatformFileDialogHelper::filterSelected, m_selectedNameFilter, &QQuickFileNameFilter::update); + } +} + +QUrl QQuickFileDialog::addDefaultSuffix(const QUrl &file) const +{ + QUrl url = file; + const QString path = url.path(); + const QString suffix = m_options->defaultSuffix(); + // Urls with "content" scheme do not require suffixes. Such schemes are + // used on Android. + const bool isContentScheme = url.scheme() == u"content"_s; + if (!isContentScheme && !suffix.isEmpty() && !path.endsWith(QLatin1Char('/')) + && path.lastIndexOf(QLatin1Char('.')) == -1) { + url.setPath(path + QLatin1Char('.') + suffix); + } + return url; +} + +void QQuickFileDialog::accept() +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) { + // Take the currently selected files and make them the final set of files. + setSelectedFiles(fileDialog->selectedFiles()); + } + QQuickAbstractDialog::accept(); +} + +QList<QUrl> QQuickFileDialog::addDefaultSuffixes(const QList<QUrl> &files) const +{ + QList<QUrl> urls; + urls.reserve(files.size()); + for (const QUrl &file : files) + urls += addDefaultSuffix(file); + return urls; +} + +QT_END_NAMESPACE + +#include "moc_qquickfiledialog_p.cpp" diff --git a/src/quickdialogs/quickdialogs/qquickfiledialog_p.h b/src/quickdialogs/quickdialogs/qquickfiledialog_p.h new file mode 100644 index 0000000000..7214c907f3 --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickfiledialog_p.h @@ -0,0 +1,132 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKFILEDIALOG_P_H +#define QQUICKFILEDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qurl.h> +#include <QtQml/qqml.h> + +#include "qquickabstractdialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickFileNameFilter; + +class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFileDialog : public QQuickAbstractDialog +{ + Q_OBJECT + Q_PROPERTY(FileMode fileMode READ fileMode WRITE setFileMode NOTIFY fileModeChanged FINAL) + Q_PROPERTY(QUrl selectedFile READ selectedFile WRITE setSelectedFile NOTIFY selectedFileChanged FINAL) + Q_PROPERTY(QList<QUrl> selectedFiles READ selectedFiles NOTIFY selectedFilesChanged FINAL) + Q_PROPERTY(QUrl currentFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged FINAL) + Q_PROPERTY(QList<QUrl> currentFiles READ currentFiles WRITE setCurrentFiles NOTIFY currentFilesChanged FINAL) + Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL) + Q_PROPERTY(QFileDialogOptions::FileDialogOptions options READ options WRITE setOptions RESET resetOptions NOTIFY optionsChanged FINAL) + Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters RESET resetNameFilters NOTIFY nameFiltersChanged FINAL) + Q_PROPERTY(QQuickFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT) + Q_PROPERTY(QString defaultSuffix READ defaultSuffix WRITE setDefaultSuffix RESET resetDefaultSuffix NOTIFY defaultSuffixChanged FINAL) + Q_PROPERTY(QString acceptLabel READ acceptLabel WRITE setAcceptLabel RESET resetAcceptLabel NOTIFY acceptLabelChanged FINAL) + Q_PROPERTY(QString rejectLabel READ rejectLabel WRITE setRejectLabel RESET resetRejectLabel NOTIFY rejectLabelChanged FINAL) + Q_FLAGS(QFileDialogOptions::FileDialogOptions) + QML_NAMED_ELEMENT(FileDialog) + QML_ADDED_IN_VERSION(6, 2) + Q_MOC_INCLUDE(<QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h>) + +public: + explicit QQuickFileDialog(QObject *parent = nullptr); + + enum FileMode { + OpenFile, + OpenFiles, + SaveFile + }; + Q_ENUM(FileMode) + + FileMode fileMode() const; + void setFileMode(FileMode fileMode); + + QUrl selectedFile() const; + void setSelectedFile(const QUrl &selectedFile); + + QList<QUrl> selectedFiles() const; + + QUrl currentFile() const; + void setCurrentFile(const QUrl &file); + + QList<QUrl> currentFiles() const; + void setCurrentFiles(const QList<QUrl> ¤tFiles); + + QUrl currentFolder() const; + void setCurrentFolder(const QUrl ¤tFolder); + + QFileDialogOptions::FileDialogOptions options() const; + void setOptions(QFileDialogOptions::FileDialogOptions options); + void resetOptions(); + + QStringList nameFilters() const; + void setNameFilters(const QStringList &filters); + void resetNameFilters(); + + QQuickFileNameFilter *selectedNameFilter() const; + + QString defaultSuffix() const; + void setDefaultSuffix(const QString &suffix); + void resetDefaultSuffix(); + + QString acceptLabel() const; + void setAcceptLabel(const QString &label); + void resetAcceptLabel(); + + QString rejectLabel() const; + void setRejectLabel(const QString &label); + void resetRejectLabel(); + +Q_SIGNALS: + void fileModeChanged(); + void selectedFileChanged(); + void selectedFilesChanged(); + void currentFileChanged(); + void currentFilesChanged(); + void currentFolderChanged(); + void optionsChanged(); + void nameFiltersChanged(); + void defaultSuffixChanged(); + void acceptLabelChanged(); + void rejectLabelChanged(); + +protected: + bool useNativeDialog() const override; + void onCreate(QPlatformDialogHelper *dialog) override; + void onShow(QPlatformDialogHelper *dialog) override; + void onHide(QPlatformDialogHelper *dialog) override; + void accept() override; + +private: + QUrl addDefaultSuffix(const QUrl &file) const; + QList<QUrl> addDefaultSuffixes(const QList<QUrl> &files) const; + + void setSelectedFiles(const QList<QUrl> &selectedFiles); + + FileMode m_fileMode; + QList<QUrl> m_selectedFiles; + QSharedPointer<QFileDialogOptions> m_options; + mutable QQuickFileNameFilter *m_selectedNameFilter; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFileDialog) + +#endif // QQUICKFILEDIALOG_P_H diff --git a/src/quickdialogs/quickdialogs/qquickfolderdialog.cpp b/src/quickdialogs/quickdialogs/qquickfolderdialog.cpp new file mode 100644 index 0000000000..692a68c7a6 --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickfolderdialog.cpp @@ -0,0 +1,264 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickfolderdialog_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtQml/qqmlfile.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcDialogs) + +/*! + \qmltype FolderDialog + \inherits Dialog +//! \instantiates QQuickFolderDialog + \inqmlmodule QtQuick.Dialogs + \since 6.3 + \brief A native folder dialog. + + The FolderDialog type provides a QML API for native platform folder dialogs. + + \image qtquickdialogs-folderdialog-gtk.png + + To show a folder dialog, construct an instance of FolderDialog, set the + desired properties, and call \l {Dialog::}{open()}. The \l currentFolder + property can be used to determine the folder that is currently being + displayed in the dialog. The \l selectedFolder property can be used to + determine the last folder that was selected in the dialog. + + \code + MenuItem { + text: qsTr("Open...") + onTriggered: folderDialog.open() + } + + FolderDialog { + id: folderDialog + currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0] + selectedFolder: viewer.folder + } + + MyViewer { + id: viewer + folder: folderDialog.selectedFolder + } + \endcode + + \section2 Availability + + A native platform folder dialog is currently available on the following platforms: + + \list + \li iOS + \li Linux (when running with the GTK+ platform theme) + \li macOS + \li Windows + \endlist + + \include includes/fallback.qdocinc + + \sa FileDialog, {QtCore::}{StandardPaths} +*/ + +QQuickFolderDialog::QQuickFolderDialog(QObject *parent) + : QQuickAbstractDialog(QQuickDialogType::FolderDialog, parent), + m_options(QFileDialogOptions::create()) +{ + m_options->setFileMode(QFileDialogOptions::Directory); + m_options->setAcceptMode(QFileDialogOptions::AcceptOpen); + m_options->setInitialDirectory(QUrl::fromLocalFile(QDir::currentPath())); +} + +/*! + \qmlproperty url QtQuick.Dialogs::FolderDialog::currentFolder + + This property holds the folder that is currently being displayed in the dialog. + + \sa selectedFolder +*/ +QUrl QQuickFolderDialog::currentFolder() const +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) + return fileDialog->directory(); + return m_options->initialDirectory(); +} + +void QQuickFolderDialog::setCurrentFolder(const QUrl &folder) +{ + if (folder == m_options->initialDirectory()) + return; + + m_options->setInitialDirectory(folder); + emit currentFolderChanged(); +} + +/*! + \qmlproperty url QtQuick.Dialogs::FolderDialog::selectedFolder + + This property holds the last folder that was selected in the dialog. + + The value of this property is updated each time the user selects a folder + in the dialog, and when the dialog is accepted. Alternatively, the + \l {Dialog::}{accepted()} signal can be handled to get the final selection. + + \sa currentFolder, {Dialog::}{accepted()} +*/ +QUrl QQuickFolderDialog::selectedFolder() const +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) { + const QList<QUrl> selectedFiles = fileDialog->selectedFiles(); + if (!selectedFiles.isEmpty()) + return selectedFiles.first(); + } + return QUrl(); +} + +void QQuickFolderDialog::setSelectedFolder(const QUrl &folder) +{ + if (folder == selectedFolder()) + return; + + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(handle())) { + fileDialog->selectFile(folder); + emit selectedFolderChanged(); + } +} + +/*! + \qmlproperty flags QtQuick.Dialogs::FolderDialog::options + + This property holds the various options that affect the look and feel of the dialog. + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the dialog is + visible is not guaranteed to have an immediate effect on the dialog (depending on + the option and on the platform). + + Available options: + \value FolderDialog.DontResolveSymlinks Don't resolve symlinks in the folder dialog. By default symlinks are resolved. + \value FolderDialog.ReadOnly Indicates that the dialog doesn't allow creating directories. + \value FolderDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation. +*/ +QFileDialogOptions::FileDialogOptions QQuickFolderDialog::options() const +{ + return m_options->options(); +} + +void QQuickFolderDialog::setOptions(QFileDialogOptions::FileDialogOptions options) +{ + if (options == m_options->options()) + return; + + m_options->setOptions(options); + emit optionsChanged(); +} + +void QQuickFolderDialog::resetOptions() +{ + setOptions({}); +} + +/*! + \qmlproperty string QtQuick.Dialogs::FolderDialog::acceptLabel + + This property holds the label text shown on the button that accepts the dialog. + + When set to an empty string, the default label of the underlying platform is used. + The default label is typically \uicontrol Open. + + The default value is an empty string. + + \sa rejectLabel +*/ +QString QQuickFolderDialog::acceptLabel() const +{ + return m_options->labelText(QFileDialogOptions::Accept); +} + +void QQuickFolderDialog::setAcceptLabel(const QString &label) +{ + if (label == m_options->labelText(QFileDialogOptions::Accept)) + return; + + m_options->setLabelText(QFileDialogOptions::Accept, label); + emit acceptLabelChanged(); +} + +void QQuickFolderDialog::resetAcceptLabel() +{ + setAcceptLabel(QString()); +} + +/*! + \qmlproperty string QtQuick.Dialogs::FolderDialog::rejectLabel + + This property holds the label text shown on the button that rejects the dialog. + + When set to an empty string, the default label of the underlying platform is used. + The default label is typically \uicontrol Cancel. + + The default value is an empty string. + + \sa acceptLabel +*/ +QString QQuickFolderDialog::rejectLabel() const +{ + return m_options->labelText(QFileDialogOptions::Reject); +} + +void QQuickFolderDialog::setRejectLabel(const QString &label) +{ + if (label == m_options->labelText(QFileDialogOptions::Reject)) + return; + + m_options->setLabelText(QFileDialogOptions::Reject, label); + emit rejectLabelChanged(); +} + +void QQuickFolderDialog::resetRejectLabel() +{ + setRejectLabel(QString()); +} + +bool QQuickFolderDialog::useNativeDialog() const +{ + if (!QQuickAbstractDialog::useNativeDialog()) + return false; + + if (m_options->testOption(QFileDialogOptions::DontUseNativeDialog)) { + qCDebug(lcDialogs) << " - the FolderDialog was told not to use a native dialog; not using native dialog"; + return false; + } + + return true; +} + +void QQuickFolderDialog::onCreate(QPlatformDialogHelper *dialog) +{ + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) { + connect(fileDialog, &QPlatformFileDialogHelper::directoryEntered, this, &QQuickFolderDialog::currentFolderChanged); + connect(fileDialog, &QPlatformFileDialogHelper::currentChanged, this, &QQuickFolderDialog::selectedFolderChanged); + fileDialog->setOptions(m_options); + } +} + +void QQuickFolderDialog::onShow(QPlatformDialogHelper *dialog) +{ + m_options->setWindowTitle(title()); + if (QPlatformFileDialogHelper *fileDialog = qobject_cast<QPlatformFileDialogHelper *>(dialog)) { + fileDialog->setOptions(m_options); + + const QUrl initialDir = m_options->initialDirectory(); + // If it's not valid, we shouldn't set it. + if (m_firstShow && initialDir.isValid() && QDir(QQmlFile::urlToLocalFileOrQrc(initialDir)).exists()) + fileDialog->setDirectory(m_options->initialDirectory()); + } + QQuickAbstractDialog::onShow(dialog); +} + +QT_END_NAMESPACE + +#include "moc_qquickfolderdialog_p.cpp" diff --git a/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h b/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h new file mode 100644 index 0000000000..e1231ab3a5 --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickfolderdialog_p.h @@ -0,0 +1,80 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKFOLDERDIALOG_P_H +#define QQUICKFOLDERDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qurl.h> +#include <QtQml/qqml.h> + +#include "qquickabstractdialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickFileNameFilter; + +class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFolderDialog : public QQuickAbstractDialog +{ + Q_OBJECT + Q_PROPERTY(QUrl currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged FINAL) + Q_PROPERTY(QUrl selectedFolder READ selectedFolder WRITE setSelectedFolder NOTIFY selectedFolderChanged FINAL) + Q_PROPERTY(QFileDialogOptions::FileDialogOptions options READ options WRITE setOptions RESET resetOptions NOTIFY optionsChanged FINAL) + Q_PROPERTY(QString acceptLabel READ acceptLabel WRITE setAcceptLabel RESET resetAcceptLabel NOTIFY acceptLabelChanged FINAL) + Q_PROPERTY(QString rejectLabel READ rejectLabel WRITE setRejectLabel RESET resetRejectLabel NOTIFY rejectLabelChanged FINAL) + Q_FLAGS(QFileDialogOptions::FileDialogOptions) + QML_NAMED_ELEMENT(FolderDialog) + QML_ADDED_IN_VERSION(6, 3) + +public: + explicit QQuickFolderDialog(QObject *parent = nullptr); + + QUrl currentFolder() const; + void setCurrentFolder(const QUrl &folder); + + QUrl selectedFolder() const; + void setSelectedFolder(const QUrl &folder); + + QFileDialogOptions::FileDialogOptions options() const; + void setOptions(QFileDialogOptions::FileDialogOptions options); + void resetOptions(); + + QString acceptLabel() const; + void setAcceptLabel(const QString &label); + void resetAcceptLabel(); + + QString rejectLabel() const; + void setRejectLabel(const QString &label); + void resetRejectLabel(); + +Q_SIGNALS: + void currentFolderChanged(); + void selectedFolderChanged(); + void optionsChanged(); + void acceptLabelChanged(); + void rejectLabelChanged(); + +protected: + bool useNativeDialog() const override; + void onCreate(QPlatformDialogHelper *dialog) override; + void onShow(QPlatformDialogHelper *dialog) override; + +private: + QSharedPointer<QFileDialogOptions> m_options; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFolderDialog) + +#endif // QQUICKFOLDERDIALOG_P_H diff --git a/src/quickdialogs/quickdialogs/qquickfontdialog.cpp b/src/quickdialogs/quickdialogs/qquickfontdialog.cpp new file mode 100644 index 0000000000..ff3e73b3c2 --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickfontdialog.cpp @@ -0,0 +1,189 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickfontdialog_p.h" + +#include <QtCore/qloggingcategory.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype FontDialog + \inherits Dialog +//! \instantiates QQuickFontDialog + \inqmlmodule QtQuick.Dialogs + \since 6.2 + \brief A font dialog. + + The FontDialog type provides a QML API for font dialogs. + + \image qtquickdialogs-fontdialog-gtk.png + + To show a font dialog, construct an instance of FontDialog, set the + desired properties, and call \l {Dialog::}{open()}. The \l currentFont + property can be used to determine the currently selected font in the + dialog. The \l selectedFont property is updated only after the final selection + has been made by accepting the dialog. + + \code + MenuItem { + text: "Font" + onTriggered: fontDialog.open() + } + + FontDialog { + id: fontDialog + currentFont.family: document.font + } + + MyDocument { + id: document + font: fontDialog.selectedFont + } + \endcode + + \section2 Availability + + A native platform font dialog is currently available on the following platforms: + + \list + \li macOS + \li Linux (when running with the GTK+ platform theme) + \endlist + + \include includes/fallback.qdocinc +*/ + +Q_LOGGING_CATEGORY(lcFontDialog, "qt.quick.dialogs.fontdialog") + +QQuickFontDialog::QQuickFontDialog(QObject *parent) + : QQuickAbstractDialog(QQuickDialogType::FontDialog, parent), + m_options(QFontDialogOptions::create()) +{ +} + +/*! + \qmlproperty font QtQuick.Dialogs::FontDialog::currentFont + \deprecated [6.3] Use \l selectedFont instead. + + This property holds the currently selected font in the dialog. + + The \c currentFont property is updated while the user is selecting + fonts in the dialog, even before the final selection has been made. + + \sa selectedFont +*/ + +QFont QQuickFontDialog::currentFont() const +{ + return selectedFont(); +} + +void QQuickFontDialog::setCurrentFont(const QFont &font) +{ + setSelectedFont(font); +} + +/*! + \qmlproperty font QtQuick.Dialogs::FontDialog::selectedFont + + This property holds the currently selected font in the dialog. + + The \c selectedFont property is updated while the user is selecting + fonts in the dialog, even before the final selection has been made. + + The \l {Dialog::}{accepted()} signal can be handled to get the final selection. + When the user has clicked \uicontrol Open to accept a font, a signal handler + for the \l {Dialog::}{accepted()} signal can query the selectedFont property to + get the final font that was selected by the user. + + \sa currentFont, {Dialog::}{accepted()} +*/ + +QFont QQuickFontDialog::selectedFont() const +{ + return m_selectedFont; +} + +void QQuickFontDialog::setSelectedFont(const QFont &font) +{ + if (font == m_selectedFont) + return; + + m_selectedFont = font; + + emit selectedFontChanged(); + emit currentFontChanged(); +} + +/*! + \qmlproperty flags QtQuick.Dialogs::FontDialog::options + + This property holds the various options that affect the look and feel of the dialog. + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the dialog is + visible is not guaranteed to have an immediate effect on the dialog (depending on + the option and on the platform). + + Available options: + \value FontDialog.ScalableFonts Show scalable fonts. + \value FontDialog.NonScalableFonts Show non-scalable fonts. + \value FontDialog.MonospacedFonts Show monospaced fonts. + \value FontDialog.ProportionalFonts Show proportional fonts. + \value FontDialog.NoButtons Don't display \uicontrol Open and \uicontrol Cancel buttons (useful + for "live dialogs"). + \value FontDialog.DontUseNativeDialog Forces the dialog to use a non-native quick implementation. +*/ + +QFontDialogOptions::FontDialogOptions QQuickFontDialog::options() const +{ + return m_options->options(); +} + +void QQuickFontDialog::setOptions(QFontDialogOptions::FontDialogOptions options) +{ + if (options == m_options->options()) + return; + + m_options->setOptions(options); + emit optionsChanged(); +} + +void QQuickFontDialog::resetOptions() +{ + setOptions({}); +} + +bool QQuickFontDialog::useNativeDialog() const +{ + return QQuickAbstractDialog::useNativeDialog() + && !(m_options->testOption(QFontDialogOptions::DontUseNativeDialog)); +} + +void QQuickFontDialog::onCreate(QPlatformDialogHelper *dialog) +{ + if (QPlatformFontDialogHelper *fontDialog = qobject_cast<QPlatformFontDialogHelper *>(dialog)) { + connect(fontDialog, &QPlatformFontDialogHelper::currentFontChanged, this, + [this, fontDialog]() { setSelectedFont(fontDialog->currentFont()); }); + connect(this, &QQuickFontDialog::selectedFontChanged, this, + [this, fontDialog]() { fontDialog->setCurrentFont(m_selectedFont); }); + fontDialog->setOptions(m_options); + } +} + +void QQuickFontDialog::onShow(QPlatformDialogHelper *dialog) +{ + m_options->setWindowTitle(title()); + if (QPlatformFontDialogHelper *fontDialog = qobject_cast<QPlatformFontDialogHelper *>(dialog)) { + fontDialog->setOptions(m_options); // setOptions only assigns a member and isn't virtual + fontDialog->setCurrentFont(m_selectedFont); + } + + QQuickAbstractDialog::onShow(dialog); +} + +QT_END_NAMESPACE + +#include "moc_qquickfontdialog_p.cpp" diff --git a/src/quickdialogs/quickdialogs/qquickfontdialog_p.h b/src/quickdialogs/quickdialogs/qquickfontdialog_p.h new file mode 100644 index 0000000000..f5df6d2a9f --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickfontdialog_p.h @@ -0,0 +1,67 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKFONTDIALOG_P_H +#define QQUICKFONTDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qfont.h> + +#include "qquickabstractdialog_p.h" + +QT_BEGIN_NAMESPACE + +class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickFontDialog : public QQuickAbstractDialog +{ + Q_OBJECT + Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont NOTIFY selectedFontChanged) + Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont NOTIFY currentFontChanged FINAL) + Q_PROPERTY(QFontDialogOptions::FontDialogOptions options READ options WRITE setOptions + RESET resetOptions NOTIFY optionsChanged) + Q_FLAGS(QFontDialogOptions::FontDialogOptions) + QML_NAMED_ELEMENT(FontDialog) + QML_ADDED_IN_VERSION(6, 2) + +public: + explicit QQuickFontDialog(QObject *parent = nullptr); + + void setCurrentFont(const QFont &font); + QFont currentFont() const; + + void setSelectedFont(const QFont &font); + QFont selectedFont() const; + + QFontDialogOptions::FontDialogOptions options() const; + void setOptions(QFontDialogOptions::FontDialogOptions options); + void resetOptions(); + +Q_SIGNALS: + void selectedFontChanged(); + void currentFontChanged(); + void optionsChanged(); + +protected: + bool useNativeDialog() const override; + void onCreate(QPlatformDialogHelper *dialog) override; + void onShow(QPlatformDialogHelper *dialog) override; + +private: + QSharedPointer<QFontDialogOptions> m_options; + QFont m_selectedFont; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFontDialog) + +#endif // QQUICKFONTDIALOG_P_H diff --git a/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp new file mode 100644 index 0000000000..10f7abe17b --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp @@ -0,0 +1,244 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickmessagedialog_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MessageDialog + \inherits Dialog +//! \instantiates QQuickMessageDialog + \inqmlmodule QtQuick.Dialogs + \since 6.3 + \brief A message dialog. + + The MessageDialog type provides a QML API for message dialogs. + + \image qtquickdialogs-messagedialog-android.png + + A message dialog is used to inform the user, or ask the user a question. + A message dialog displays a primary \l text to alert the user to a situation, + an \l {informativeText}{informative text} to further explain the alert or to + ask the user a question, and an optional \l {detailedText}{detailed text} to + provide even more data if the user requests it. A message box can also display + a configurable set of \l buttons for accepting a user response. + + To show a message dialog, construct an instance of MessageDialog, set the + desired properties, and call \l {Dialog::}{open()}. + + \code + MessageDialog { + buttons: MessageDialog.Ok + text: "The document has been modified." + } + \endcode + + The user must click the \uicontrol OK button to dismiss the message dialog. + + A more elaborate approach than just alerting the user to an event is to + also ask the user what to do about it. Store the question in the + \l {informativeText}{informative text} property, and specify the \l buttons + property to the set of buttons you want as the set of user responses. The + buttons are specified by combining values using the bitwise OR operator. + + \code + MessageDialog { + text: "The document has been modified." + informativeText: "Do you want to save your changes?" + buttons: MessageDialog.Ok | MessageDialog.Cancel + + onAccepted: document.save() + } + \endcode + + \image qtquickdialogs-messagedialog-informative-android.png + + + \section2 Availability + + A native platform message dialog is currently available on the following platforms: + + \list + \li iOS + \li Android + \endlist + + \include includes/fallback.qdocinc +*/ + +/*! + \qmlsignal QtQuick.Dialogs::MessageDialog::buttonClicked(QPlatformDialogHelper::StandardButton button, QPlatformDialogHelper::ButtonRole role) + + This signal is emitted when a \a button with the specified \a role is + clicked. + + By giving this signal a handler, you can respond to any custom button being pressed. + The \a button argument tells which button was clicked, while the \a role argument tells the functional role of that button. + + \code + MessageDialog { + id: dialog + text: qsTr("The document has been modified.") + informativeText: qsTr("Do you want to save your changes?") + buttons: MessageDialog.Ok | MessageDialog.Cancel + onButtonClicked: function (button, role) { + switch (button) { + case MessageDialog.Ok: + document.save() + break; + } + } + } + \endcode + + \sa buttons +*/ + +QQuickMessageDialog::QQuickMessageDialog(QObject *parent) + : QQuickAbstractDialog(QQuickDialogType::MessageDialog, parent), + m_options(QMessageDialogOptions::create()) +{ +} + +/*! + \qmlproperty string QtQuick.Dialogs::MessageDialog::text + + This property holds the text to be displayed on the message dialog. + + \sa informativeText, detailedText +*/ +QString QQuickMessageDialog::text() const +{ + return m_options->text(); +} + +void QQuickMessageDialog::setText(const QString &text) +{ + if (m_options->text() == text) + return; + + m_options->setText(text); + + emit textChanged(); +} + +/*! + \qmlproperty string QtQuick.Dialogs::MessageDialog::informativeText + + This property holds the informative text that provides a fuller description for the message. + + Informative text can be used to expand upon the \l text to give more information to the user. + + \sa text, detailedText +*/ +QString QQuickMessageDialog::informativeText() const +{ + return m_options->informativeText(); +} + +void QQuickMessageDialog::setInformativeText(const QString &text) +{ + if (m_options->informativeText() == text) + return; + + m_options->setInformativeText(text); + + emit informativeTextChanged(); +} + +/*! + \qmlproperty string QtQuick.Dialogs::MessageDialog::detailedText + + This property holds the text to be displayed in the details area. + + \sa text, informativeText +*/ +QString QQuickMessageDialog::detailedText() const +{ + return m_options->detailedText(); +} + +void QQuickMessageDialog::setDetailedText(const QString &text) +{ + if (m_options->detailedText() == text) + return; + + m_options->setDetailedText(text); + + emit detailedTextChanged(); +} + +/*! + \qmlproperty flags QtQuick.Dialogs::MessageDialog::buttons + + This property holds a combination of buttons that are used by the message dialog. + The default value is \c MessageDialog.NoButton. + + Possible flags: + \value MessageDialog.Ok An "OK" button defined with the \c AcceptRole. + \value MessageDialog.Open An "Open" button defined with the \c AcceptRole. + \value MessageDialog.Save A "Save" button defined with the \c AcceptRole. + \value MessageDialog.Cancel A "Cancel" button defined with the \c RejectRole. + \value MessageDialog.Close A "Close" button defined with the \c RejectRole. + \value MessageDialog.Discard A "Discard" or "Don't Save" button, depending on the platform, defined with the \c DestructiveRole. + \value MessageDialog.Apply An "Apply" button defined with the \c ApplyRole. + \value MessageDialog.Reset A "Reset" button defined with the \c ResetRole. + \value MessageDialog.RestoreDefaults A "Restore Defaults" button defined with the \c ResetRole. + \value MessageDialog.Help A "Help" button defined with the \c HelpRole. + \value MessageDialog.SaveAll A "Save All" button defined with the \c AcceptRole. + \value MessageDialog.Yes A "Yes" button defined with the \c YesRole. + \value MessageDialog.YesToAll A "Yes to All" button defined with the \c YesRole. + \value MessageDialog.No A "No" button defined with the \c NoRole. + \value MessageDialog.NoToAll A "No to All" button defined with the \c NoRole. + \value MessageDialog.Abort An "Abort" button defined with the \c RejectRole. + \value MessageDialog.Retry A "Retry" button defined with the \c AcceptRole. + \value MessageDialog.Ignore An "Ignore" button defined with the \c AcceptRole. + \value MessageDialog.NoButton The dialog has no buttons. + + \sa buttonClicked() +*/ + +QPlatformDialogHelper::StandardButtons QQuickMessageDialog::buttons() const +{ + return m_options->standardButtons(); +} + +void QQuickMessageDialog::setButtons(QPlatformDialogHelper::StandardButtons buttons) +{ + if (m_options->standardButtons() == buttons) + return; + + m_options->setStandardButtons(buttons); + + emit buttonsChanged(); +} + +void QQuickMessageDialog::handleClick(QPlatformDialogHelper::StandardButton button, + QPlatformDialogHelper::ButtonRole role) +{ + emit buttonClicked(button, role); +} + +void QQuickMessageDialog::onCreate(QPlatformDialogHelper *dialog) +{ + if (QPlatformMessageDialogHelper *messageDialog = + qobject_cast<QPlatformMessageDialogHelper *>(dialog)) { + connect(messageDialog, &QPlatformMessageDialogHelper::clicked, this, + &QQuickMessageDialog::handleClick); + messageDialog->setOptions(m_options); + } +} + +void QQuickMessageDialog::onShow(QPlatformDialogHelper *dialog) +{ + m_options->setWindowTitle(title()); + + if (QPlatformMessageDialogHelper *messageDialog = + qobject_cast<QPlatformMessageDialogHelper *>(dialog)) + messageDialog->setOptions(m_options); // setOptions only assigns a member and isn't virtual +} + +QT_END_NAMESPACE + +#include "moc_qquickmessagedialog_p.cpp" diff --git a/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h b/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h new file mode 100644 index 0000000000..40a2b6da8d --- /dev/null +++ b/src/quickdialogs/quickdialogs/qquickmessagedialog_p.h @@ -0,0 +1,75 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQUICKMESSAGEDIALOG_P_H +#define QQUICKMESSAGEDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickabstractdialog_p.h" + +QT_BEGIN_NAMESPACE + +class Q_QUICKDIALOGS2_PRIVATE_EXPORT QQuickMessageDialog : public QQuickAbstractDialog +{ + Q_OBJECT + +private: + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL) + Q_PROPERTY(QString informativeText READ informativeText WRITE setInformativeText NOTIFY informativeTextChanged FINAL) + Q_PROPERTY(QString detailedText READ detailedText WRITE setDetailedText NOTIFY detailedTextChanged FINAL) + Q_PROPERTY(QPlatformDialogHelper::StandardButtons buttons READ buttons WRITE setButtons NOTIFY buttonsChanged FINAL) + QML_EXTENDED_NAMESPACE(QPlatformDialogHelper) + QML_NAMED_ELEMENT(MessageDialog) + QML_ADDED_IN_VERSION(6, 3) + +public: + explicit QQuickMessageDialog(QObject *parent = nullptr); + + QString text() const; + void setText(const QString &text); + + QString informativeText() const; + void setInformativeText(const QString &text); + + QString detailedText() const; + void setDetailedText(const QString &text); + + QPlatformDialogHelper::StandardButtons buttons() const; + void setButtons(QPlatformDialogHelper::StandardButtons buttons); + +Q_SIGNALS: + void textChanged(); + void informativeTextChanged(); + void detailedTextChanged(); + void buttonsChanged(); + + void buttonClicked(QPlatformDialogHelper::StandardButton button, + QPlatformDialogHelper::ButtonRole role); + +private Q_SLOTS: + void handleClick(QPlatformDialogHelper::StandardButton button, + QPlatformDialogHelper::ButtonRole role); + +protected: + void onCreate(QPlatformDialogHelper *dialog) override; + void onShow(QPlatformDialogHelper *dialog) override; + +private: + QSharedPointer<QMessageDialogOptions> m_options; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickMessageDialog) + +#endif // QQUICKMESSAGEDIALOG_P_H diff --git a/src/quickdialogs/quickdialogs/qtquickdialogs2foreign.cpp b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign.cpp new file mode 100644 index 0000000000..78e7dc7ffd --- /dev/null +++ b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qtquickdialogs2foreign_p.h" + +QT_BEGIN_NAMESPACE + +QT_END_NAMESPACE + +#include "moc_qtquickdialogs2foreign_p.cpp" diff --git a/src/quickdialogs/quickdialogs/qtquickdialogs2foreign_p.h b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign_p.h new file mode 100644 index 0000000000..34df201b51 --- /dev/null +++ b/src/quickdialogs/quickdialogs/qtquickdialogs2foreign_p.h @@ -0,0 +1,42 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QTQUICKDIALOGS2FOREIGN_P_H +#define QTQUICKDIALOGS2FOREIGN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qpa/qplatformdialoghelper.h> +#include <QtQml/qqml.h> +#include <QtQuickDialogs2Utils/private/qquickfilenamefilter_p.h> + +QT_BEGIN_NAMESPACE + +struct QPlatformDialogHelperForeign +{ + Q_GADGET + QML_ANONYMOUS + QML_FOREIGN(QPlatformDialogHelper) + QML_ADDED_IN_VERSION(6, 2) +}; + +struct QQuickFileNameFilterQuickDialogs2Foreign +{ + Q_GADGET + QML_ANONYMOUS + QML_FOREIGN(QQuickFileNameFilter) + QML_ADDED_IN_VERSION(6, 2) +}; + +QT_END_NAMESPACE + +#endif // QTQUICKDIALOGS2FOREIGN_P_H diff --git a/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h b/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h new file mode 100644 index 0000000000..85f9ce6dc4 --- /dev/null +++ b/src/quickdialogs/quickdialogs/qtquickdialogs2global_p.h @@ -0,0 +1,22 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QTQUICKDIALOGS2GLOBAL_P_H +#define QTQUICKDIALOGS2GLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtQml/private/qqmlglobal_p.h> +#include <QtQuickDialogs2/private/qtquickdialogs2exports_p.h> + +#endif // QTQUICKDIALOGS2GLOBAL_P_H |