/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qquickabstractdialog_p.h" #include #include #include #include #include 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(). \note This signal is \e not emitted when closing the dialog with \l close(). \sa rejected() */ /*! \qmlsignal void QtQuick.Dialogs::Dialog::rejected() This signal is emitted when the dialog has been rejected either interactively or by calling \l reject(). \note This signal is \e not emitted when closing the dialog with \l close(). \sa accepted() */ Q_DECLARE_LOGGING_CATEGORY(lcDialogs) QQuickAbstractDialog::QQuickAbstractDialog(QQuickDialogType type, QObject *parent) : QObject(parent), m_type(type) { } QQuickAbstractDialog::~QQuickAbstractDialog() { destroy(); } QPlatformDialogHelper *QQuickAbstractDialog::handle() const { return m_handle.get(); } /*! \qmldefault \qmlproperty list QtQuick.Dialogs::Dialog::data This default property holds the list of all objects declared as children of the dialog. */ QQmlListProperty QQuickAbstractDialog::data() { return QQmlListProperty(this, &m_data); } /*! \qmlproperty Window QtQuick.Dialogs::Dialog::parentWindow This property holds the parent window of the dialog. Unless explicitly set, the window is automatically resolved by iterating the QML parent objects until a \l Window or an \l Item that has a window is found. */ QWindow *QQuickAbstractDialog::parentWindow() const { return m_parentWindow; } void QQuickAbstractDialog::setParentWindow(QWindow *window) { qCDebug(lcDialogs) << "set parent window to" << window; if (m_parentWindow == window) return; m_parentWindow = window; emit parentWindowChanged(); } /*! \qmlproperty string QtQuick.Dialogs::Dialog::title This property holds the title of the dialog. */ QString QQuickAbstractDialog::title() const { return m_title; } void QQuickAbstractDialog::setTitle(const QString &title) { if (m_title == title) return; m_title = title; emit titleChanged(); } /*! \qmlproperty Qt::WindowFlags QtQuick.Dialogs::Dialog::flags This property holds the window flags of the dialog. The default value is \c Qt.Dialog. */ Qt::WindowFlags QQuickAbstractDialog::flags() const { return m_flags; } void QQuickAbstractDialog::setFlags(Qt::WindowFlags flags) { if (m_flags == flags) return; m_flags = flags; emit flagsChanged(); } /*! \qmlproperty Qt::WindowModality QtQuick.Dialogs::Dialog::modality This property holds the modality of the dialog. The default value is \c Qt.WindowModal. Available values: \value Qt.NonModal The dialog is not modal and does not block input to other windows. \value Qt.WindowModal The dialog is modal to a single window hierarchy and blocks input to its parent window, all grandparent windows, and all siblings of its parent and grandparent windows. \value Qt.ApplicationModal The dialog is modal to the application and blocks input to all windows. */ Qt::WindowModality QQuickAbstractDialog::modality() const { return m_modality; } void QQuickAbstractDialog::setModality(Qt::WindowModality modality) { if (m_modality == modality) return; m_modality = modality; emit modalityChanged(); } /*! \qmlproperty bool QtQuick.Dialogs::Dialog::visible This property holds the visibility of the dialog. The default value is \c false. \sa open(), close() */ bool QQuickAbstractDialog::isVisible() const { return m_handle && m_visible; } void QQuickAbstractDialog::setVisible(bool visible) { qCDebug(lcDialogs) << "setVisible called with" << visible; if (visible) { // Don't try to open before component completion, as we won't have a window yet, // and open() sets m_visible to false if it fails. if (!m_complete) m_visibleRequested = true; else open(); } else { close(); } } /*! \qmlproperty StandardCode QtQuick.Dialogs::Dialog::result This property holds the result code. Standard result codes: \value Dialog.Accepted \value Dialog.Rejected \note MessageDialog sets the result to the value of the clicked standard button instead of using the standard result codes. */ QQuickAbstractDialog::StandardCode QQuickAbstractDialog::result() const { return m_result; } void QQuickAbstractDialog::setResult(StandardCode result) { if (m_result == result) return; m_result = result; emit resultChanged(); } /*! \qmlmethod void QtQuick.Dialogs::Dialog::open() Opens the dialog. \sa visible, close() */ void QQuickAbstractDialog::open() { qCDebug(lcDialogs) << "open called"; if (m_visible || !create()) return; onShow(m_handle.get()); m_visible = m_handle->show(m_flags, m_modality, m_parentWindow); if (m_visible) emit visibleChanged(); } /*! \qmlmethod void QtQuick.Dialogs::Dialog::close() Closes the dialog. \sa visible, open() */ void QQuickAbstractDialog::close() { if (!m_handle || !m_visible) return; onHide(m_handle.get()); m_handle->hide(); m_visible = false; emit visibleChanged(); } /*! \qmlmethod void QtQuick.Dialogs::Dialog::accept() Closes the dialog and emits the \l accepted() signal. \sa reject() */ void QQuickAbstractDialog::accept() { done(Accepted); } /*! \qmlmethod void QtQuick.Dialogs::Dialog::reject() Closes the dialog and emits the \l rejected() signal. \sa accept() */ void QQuickAbstractDialog::reject() { done(Rejected); } /*! \qmlmethod void QtQuick.Dialogs::Dialog::done(StandardCode result) Closes the dialog and sets the \a result. \sa accept(), reject(), result */ void QQuickAbstractDialog::done(StandardCode result) { close(); setResult(result); if (result == Accepted) emit accepted(); else if (result == Rejected) emit rejected(); } void QQuickAbstractDialog::classBegin() { } void QQuickAbstractDialog::componentComplete() { qCDebug(lcDialogs) << "componentComplete"; m_complete = true; if (!m_parentWindow) { qCDebug(lcDialogs) << "- no parent window; searching for one"; setParentWindow(findParentWindow()); } if (m_visibleRequested) { qCDebug(lcDialogs) << "visible was bound to true before component completion; opening dialog"; open(); m_visibleRequested = false; } } static const char *qmlTypeName(const QObject *object) { return object->metaObject()->className() + qstrlen("QQuickPlatform"); } QPlatformTheme::DialogType toPlatformDialogType(QQuickDialogType quickDialogType) { return quickDialogType == QQuickDialogType::FolderDialog ? QPlatformTheme::FileDialog : static_cast(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(obj); if (window) return window; QQuickItem *item = qobject_cast(obj); if (item && item->window()) return item->window(); obj = obj->parent(); } return nullptr; } QT_END_NAMESPACE #include "moc_qquickabstractdialog_p.cpp"