Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickabstractdialog.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtCore/qloggingcategory.h>
8#include <QtGui/private/qguiapplication_p.h>
9#include <QtQuick/qquickitem.h>
10#include <QtQuick/qquickwindow.h>
11#include <QtQuickDialogs2QuickImpl/private/qquickdialogimplfactory_p.h>
12
14
15Q_LOGGING_CATEGORY(lcDialogs, "qt.quick.dialogs")
16
17/*!
18 \internal
19
20 A dialog that can be backed by different implementations.
21
22 Each dialog has a QPlatformDialogHelper handle, which is created in create():
23
24 - First we attempt to create a native dialog (e.g. QWindowsFileDialogHelper) through
25 QGuiApplicationPrivate::platformTheme()->createPlatformDialogHelper().
26 - If that fails, we try to create the Qt Quick fallback dialog (e.g. QQuickPlatformFileDialog)
27 through QQuickDialogImplFactory::createPlatformDialogHelper().
28
29 The handle acts as an intermediary between the QML-facing dialog object
30 and the native/widget/quick implementation:
31
32 +---------------------------+
33 | FileDialog created in QML |
34 +---------------------------+
35 |
36 |
37 v +----------------------+
38 +------------------+ | attempt to create | +------+
39 |useNativeDialog()?|-----false---->| QQuickPlatformDialog |---->| done |
40 +------------------+ | instance and set | +------+
41 | | m_handle to it |
42 | +----------------------+
43 v ^
44 true |
45 | |
46 v |
47 +---------------------+ |
48 | attempt to create | |
49 | QWindowsFileDialog- | |
50 | Helper instance and | |
51 | set m_handle to it | |
52 +---------------------+ |
53 | |
54 v |
55 +-----------------+ |
56 | m_handle valid? |--------------------->false
57 +-----------------+ ^
58 | |
59 v |
60 true |
61 | |
62 +-------------------+ |
63 | m_handle->show()? |------------------->false
64 +-------------------+
65 |
66 v
67 true
68 |
69 +------+
70 | done |
71 +------+
72
73 If QWindowsFileDialogHelper is created, it creates a native dialog.
74 If QQuickPlatformDialog is created, it creates a non-native QQuickFileDialogImpl.
75*/
76
77/*!
78 \qmltype Dialog
79 \inherits QtObject
80//! \nativetype QQuickAbstractDialog
81 \inqmlmodule QtQuick.Dialogs
82 \since 6.2
83 \brief The base class of native dialogs.
84
85 The Dialog type provides common QML API for native platform dialogs.
86 For the non-native dialog, see \l [QML QtQuickControls]{Dialog}.
87
88 To show a native dialog, construct an instance of one of the concrete
89 Dialog implementations, set the desired properties, and call \l open().
90 Dialog emits \l accepted() or \l rejected() when the user is done with
91 the dialog.
92
93 \note This is an internal type that cannot be created in QML.
94*/
95
96/*!
97 \qmlsignal void QtQuick.Dialogs::Dialog::accepted()
98
99 This signal is emitted when the dialog has been accepted either
100 interactively or by calling \l accept().
101
102 \sa rejected()
103*/
104
105/*!
106 \qmlsignal void QtQuick.Dialogs::Dialog::rejected()
107
108 This signal is emitted when the dialog has been rejected either
109 interactively or by calling \l reject().
110
111 This signal is also emitted when closing the dialog with \l close().
112
113 \sa accepted()
114*/
115
116QQuickAbstractDialog::QQuickAbstractDialog(QQuickDialogType type, QObject *parent)
117 : QObject(parent),
118 m_type(type)
119{
120}
121
122QQuickAbstractDialog::~QQuickAbstractDialog()
123{
124 destroy();
125}
126
127QPlatformDialogHelper *QQuickAbstractDialog::handle() const
128{
129 return m_handle.get();
130}
131
132/*!
133 \qmldefault
134 \qmlproperty list<QtObject> QtQuick.Dialogs::Dialog::data
135
136 This default property holds the list of all objects declared as children of
137 the dialog.
138*/
139QQmlListProperty<QObject> QQuickAbstractDialog::data()
140{
141 return QQmlListProperty<QObject>(this, &m_data);
142}
143
144/*!
145 \qmlproperty Window QtQuick.Dialogs::Dialog::parentWindow
146
147 This property holds the parent window of the dialog.
148
149 Unless explicitly set, the window is automatically resolved by iterating
150 the QML parent objects until a \l Window or an \l Item that has a window
151 is found.
152*/
153QWindow *QQuickAbstractDialog::parentWindow() const
154{
155 return m_parentWindow;
156}
157
158void QQuickAbstractDialog::setParentWindow(QWindow *window)
159{
160 qCDebug(lcDialogs) << "set parent window to" << window;
161 m_parentWindowExplicitlySet = bool(window);
162
163 if (m_parentWindow == window)
164 return;
165
166 m_parentWindow = window;
167 emit parentWindowChanged();
168}
169
170void QQuickAbstractDialog::resetParentWindow()
171{
172 m_parentWindowExplicitlySet = false;
173
174 if (!m_parentWindow)
175 return;
176
177 m_parentWindow = nullptr;
178 emit parentWindowChanged();
179}
180
181/*!
182 \qmlproperty string QtQuick.Dialogs::Dialog::title
183
184 This property holds the title of the dialog.
185*/
186QString QQuickAbstractDialog::title() const
187{
188 return m_title;
189}
190
191void QQuickAbstractDialog::setTitle(const QString &title)
192{
193 if (m_title == title)
194 return;
195
196 m_title = title;
197 emit titleChanged();
198}
199
200/*!
201 \qmlproperty Qt::WindowFlags QtQuick.Dialogs::Dialog::flags
202
203 This property holds the window flags of the dialog. The default value is \c Qt.Dialog.
204*/
205Qt::WindowFlags QQuickAbstractDialog::flags() const
206{
207 return m_flags;
208}
209
210void QQuickAbstractDialog::setFlags(Qt::WindowFlags flags)
211{
212 if (m_flags == flags)
213 return;
214
215 m_flags = flags;
216 emit flagsChanged();
217}
218
219/*!
220 \qmlproperty Qt::WindowModality QtQuick.Dialogs::Dialog::modality
221
222 This property holds the modality of the dialog. The default value is \c Qt.WindowModal.
223
224 Available values:
225 \value Qt.NonModal The dialog is not modal and does not block input to other windows.
226 \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.
227 \value Qt.ApplicationModal The dialog is modal to the application and blocks input to all windows.
228*/
229Qt::WindowModality QQuickAbstractDialog::modality() const
230{
231 return m_modality;
232}
233
234void QQuickAbstractDialog::setModality(Qt::WindowModality modality)
235{
236 if (m_modality == modality)
237 return;
238
239 m_modality = modality;
240 emit modalityChanged();
241}
242
243/*!
244 \qmlproperty bool QtQuick.Dialogs::Dialog::visible
245
246 This property holds the visibility of the dialog. The default value is \c false.
247
248 \sa open(), close()
249*/
250bool QQuickAbstractDialog::isVisible() const
251{
252 return m_handle && m_visible;
253}
254
255void QQuickAbstractDialog::setVisible(bool visible)
256{
257 qCDebug(lcDialogs) << "setVisible called with" << visible;
258
259 if (visible) {
260 // Don't try to open before component completion, as we won't have a window yet,
261 // and open() sets m_visible to false if it fails.
262 if (!m_complete)
263 m_visibleRequested = true;
264 else
265 open();
266 } else {
267 close();
268 }
269}
270
271/*!
272 \qmlproperty int QtQuick.Dialogs::Dialog::result
273
274 This property holds the result code.
275
276 Standard result codes:
277 \value Dialog.Accepted
278 \value Dialog.Rejected
279
280 \note MessageDialog sets the result to the value of the clicked standard
281 button instead of using the standard result codes.
282*/
283int QQuickAbstractDialog::result() const
284{
285 return m_result;
286}
287
288void QQuickAbstractDialog::setResult(int result)
289{
290 if (m_result == result)
291 return;
292
293 m_result = result;
294 emit resultChanged();
295}
296
297/*!
298 \qmlproperty enumeration QtQuick.Dialogs::Dialog::popupType
299 \since 6.10
300
301 This property can be used to change the \l {QQuickPopup::}{popupType} of the non-native quick dialog.
302
303 The available values are:
304 \value Popup.Item The dialog will appear as an item in the window of the nearest parent item.
305 \value Popup.Window The dialog will appear inside its own window.
306 \value Popup.Native This value is not supported. \c Popup.Window will be used instead.
307
308 \note This property has no effect when using a native dialog.
309*/
310QQuickPopup::PopupType QQuickAbstractDialog::popupType() const
311{
312 return m_popupType;
313}
314
315void QQuickAbstractDialog::setPopupType(QQuickPopup::PopupType popupType)
316{
317 if (m_popupType == popupType)
318 return;
319
320 m_popupType = popupType;
321
322 emit popupTypeChanged();
323}
324
325void QQuickAbstractDialog::resetPopupType()
326{
327 setPopupType(QQuickPopup::Window);
328}
329
330/*!
331 \qmlmethod void QtQuick.Dialogs::Dialog::open()
332
333 Opens the dialog.
334
335 \sa visible, close()
336*/
337void QQuickAbstractDialog::open()
338{
339 qCDebug(lcDialogs) << "open called";
340 if (m_visible || !create())
341 return;
342
343 onShow(m_handle.get());
344
345 m_visible = m_handle->show(m_flags, m_modality, windowForOpen());
346 if (!m_visible && useNativeDialog()) {
347 // Fall back to non-native dialog
348 destroy();
349 if (!create(CreateOptions::DontTryNativeDialog))
350 return;
351
352 onShow(m_handle.get());
353 m_visible = m_handle->show(m_flags, m_modality, windowForOpen());
354
355 if (m_visible) {
356 // The conditions that caused the non-native fallback might have
357 // changed the next time open() is called, so we should try again
358 // with a native dialog when that happens.
359 QObject::connect(this, &QQuickAbstractDialog::visibleChanged,
360 m_handle.get(), [this]{ if (!isVisible()) destroy(); });
361 }
362 }
363 if (m_visible) {
364 m_result = Rejected; // in case an accepted dialog gets re-opened, then closed
365 emit visibleChanged();
366 }
367}
368
369/*!
370 \qmlmethod void QtQuick.Dialogs::Dialog::close()
371
372 Closes the dialog and emits either the \l accepted() or \l rejected()
373 signal.
374
375 \sa visible, open()
376*/
377void QQuickAbstractDialog::close()
378{
379 if (!m_handle || !m_visible)
380 return;
381
382 onHide(m_handle.get());
383 m_handle->hide();
384 m_visible = false;
385 if (!m_parentWindowExplicitlySet)
386 m_parentWindow = nullptr;
387 emit visibleChanged();
388
389 if (dialogCode() == Accepted)
390 emit accepted();
391 else if (dialogCode() == Rejected)
392 emit rejected();
393}
394
395/*!
396 \qmlmethod void QtQuick.Dialogs::Dialog::accept()
397
398 Closes the dialog and emits the \l accepted() signal.
399
400 \sa reject()
401*/
402void QQuickAbstractDialog::accept()
403{
404 done(Accepted);
405}
406
407/*!
408 \qmlmethod void QtQuick.Dialogs::Dialog::reject()
409
410 Closes the dialog and emits the \l rejected() signal.
411
412 \sa accept()
413*/
414void QQuickAbstractDialog::reject()
415{
416 done(Rejected);
417}
418
419/*!
420 \qmlmethod void QtQuick.Dialogs::Dialog::done(int result)
421
422 Closes the dialog and sets the \a result.
423
424 \sa accept(), reject(), result
425*/
426void QQuickAbstractDialog::done(int result)
427{
428 setResult(result);
429 close();
430}
431
432void QQuickAbstractDialog::classBegin()
433{
434}
435
436void QQuickAbstractDialog::componentComplete()
437{
438 qCDebug(lcDialogs) << "componentComplete";
439 m_complete = true;
440
441 if (!m_visibleRequested)
442 return;
443
444 m_visibleRequested = false;
445
446 if (windowForOpen()) {
447 open();
448 return;
449 }
450
451 // Since visible were set to true by the user, we want the dialog to be open by default.
452 // There is no guarantee that the dialog will work when it exists in a object tree that lacks a window,
453 // and since qml components are sometimes instantiated before they're given a window
454 // (which is the case when using QQuickView), we want to delay the call to open(), until the window is provided.
455 if (const auto parentItem = findParentItem())
456 connect(parentItem, &QQuickItem::windowChanged, this, &QQuickAbstractDialog::deferredOpen, Qt::SingleShotConnection);
457}
458
459static const char *qmlTypeName(const QObject *object)
460{
461 return object->metaObject()->className() + qstrlen("QQuickPlatform");
462}
463
464QPlatformTheme::DialogType toPlatformDialogType(QQuickDialogType quickDialogType)
465{
466 return quickDialogType == QQuickDialogType::FolderDialog
467 ? QPlatformTheme::FileDialog : static_cast<QPlatformTheme::DialogType>(quickDialogType);
468}
469
470bool QQuickAbstractDialog::create(CreateOptions createOptions)
471{
472 qCDebug(lcDialogs) << qmlTypeName(this) << "attempting to create dialog backend of type"
473 << int(m_type) << "with parent window" << m_parentWindow;
474 if (m_handle)
475 return m_handle.get();
476
477 if ((createOptions != CreateOptions::DontTryNativeDialog) && useNativeDialog()) {
478 qCDebug(lcDialogs) << "- attempting to create a native dialog";
479 m_handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformDialogHelper(
480 toPlatformDialogType(m_type)));
481 }
482
483 if (!m_handle) {
484 qCDebug(lcDialogs) << "- attempting to create a quick dialog";
485 m_handle = QQuickDialogImplFactory::createPlatformDialogHelper(m_type, this);
486 }
487
488 qCDebug(lcDialogs) << qmlTypeName(this) << "created ->" << m_handle.get();
489 if (m_handle) {
490 onCreate(m_handle.get());
491 connect(m_handle.get(), &QPlatformDialogHelper::accept, this, &QQuickAbstractDialog::accept);
492 connect(m_handle.get(), &QPlatformDialogHelper::reject, this, &QQuickAbstractDialog::reject);
493 }
494 return m_handle.get();
495}
496
497void QQuickAbstractDialog::destroy()
498{
499 m_handle.reset();
500}
501
502bool QQuickAbstractDialog::useNativeDialog() const
503{
504 if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)) {
505 qCDebug(lcDialogs) << " - Qt::AA_DontUseNativeDialogs was set; not using native dialog";
506 return false;
507 }
508
509 if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(toPlatformDialogType(m_type))) {
510 qCDebug(lcDialogs) << " - the platform theme told us a native dialog isn't available; not using native dialog";
511 return false;
512 }
513
514 return true;
515}
516
517/*!
518 \internal
519
520 Called at the end of \l create().
521*/
522void QQuickAbstractDialog::onCreate(QPlatformDialogHelper *dialog)
523{
524 Q_UNUSED(dialog);
525}
526
527/*!
528 \internal
529
530 Called by \l open(), after the call to \l create() and before
531 the handle/helper's \c show function is called.
532*/
533void QQuickAbstractDialog::onShow(QPlatformDialogHelper *dialog)
534{
535 Q_UNUSED(dialog);
536 m_firstShow = false;
537}
538
539void QQuickAbstractDialog::onHide(QPlatformDialogHelper *dialog)
540{
541 Q_UNUSED(dialog);
542}
543
544int QQuickAbstractDialog::dialogCode() const { return m_result; }
545
546QQuickItem *QQuickAbstractDialog::findParentItem() const
547{
548 QObject *obj = parent();
549 while (obj) {
550 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
551 if (item)
552 return item;
553 obj = obj->parent();
554 }
555 return nullptr;
556}
557
558QWindow *QQuickAbstractDialog::windowForOpen() const
559{
560 if (m_parentWindowExplicitlySet)
561 return m_parentWindow;
562 else if (auto parentItem = findParentItem())
563 return parentItem->window();
564 return m_parentWindow;
565}
566
567void QQuickAbstractDialog::deferredOpen(QWindow *window)
568{
569 m_parentWindow = window;
570 open();
571}
572
573QT_END_NAMESPACE
574
575#include "moc_qquickabstractdialog_p.cpp"
QPlatformTheme::DialogType toPlatformDialogType(QQuickDialogType quickDialogType)
static const char * qmlTypeName(const QObject *object)