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 {QtQuick.Controls::Popup::}{popupType}
302 of the non-native quick dialog.
303
304 The available values are:
305 \value Popup.Item The dialog will appear as an item in the window of the nearest parent item.
306 \value Popup.Window The dialog will appear inside its own window.
307 \value Popup.Native This value is not supported. \c Popup.Window will be used instead.
308
309 \note This property has no effect when using a native dialog.
310*/
311QQuickPopup::PopupType QQuickAbstractDialog::popupType() const
312{
313 return m_popupType;
314}
315
316void QQuickAbstractDialog::setPopupType(QQuickPopup::PopupType popupType)
317{
318 if (m_popupType == popupType)
319 return;
320
321 m_popupType = popupType;
322
323 emit popupTypeChanged();
324}
325
326void QQuickAbstractDialog::resetPopupType()
327{
328 setPopupType(QQuickPopup::Window);
329}
330
331/*!
332 \qmlmethod void QtQuick.Dialogs::Dialog::open()
333
334 Opens the dialog.
335
336 \sa visible, close()
337*/
338void QQuickAbstractDialog::open()
339{
340 qCDebug(lcDialogs) << "open called";
341 if (m_visible || !create())
342 return;
343
344 onShow(m_handle.get());
345
346 m_visible = m_handle->show(m_flags, m_modality, windowForOpen());
347 if (!m_visible && useNativeDialog()) {
348 // Fall back to non-native dialog
349 destroy();
350 if (!create(CreateOptions::DontTryNativeDialog))
351 return;
352
353 onShow(m_handle.get());
354 m_visible = m_handle->show(m_flags, m_modality, windowForOpen());
355
356 if (m_visible) {
357 // The conditions that caused the non-native fallback might have
358 // changed the next time open() is called, so we should try again
359 // with a native dialog when that happens.
360 QObject::connect(this, &QQuickAbstractDialog::visibleChanged,
361 m_handle.get(), [this]{ if (!isVisible()) destroy(); });
362 }
363 }
364 if (m_visible) {
365 m_result = Rejected; // in case an accepted dialog gets re-opened, then closed
366 emit visibleChanged();
367 }
368}
369
370/*!
371 \qmlmethod void QtQuick.Dialogs::Dialog::close()
372
373 Closes the dialog and emits either the \l accepted() or \l rejected()
374 signal.
375
376 \sa visible, open()
377*/
378void QQuickAbstractDialog::close()
379{
380 if (!m_handle || !m_visible)
381 return;
382
383 onHide(m_handle.get());
384 m_handle->hide();
385 m_visible = false;
386 if (!m_parentWindowExplicitlySet)
387 m_parentWindow = nullptr;
388 emit visibleChanged();
389
390 if (dialogCode() == Accepted)
391 emit accepted();
392 else if (dialogCode() == Rejected)
393 emit rejected();
394}
395
396/*!
397 \qmlmethod void QtQuick.Dialogs::Dialog::accept()
398
399 Closes the dialog and emits the \l accepted() signal.
400
401 \sa reject()
402*/
403void QQuickAbstractDialog::accept()
404{
405 done(Accepted);
406}
407
408/*!
409 \qmlmethod void QtQuick.Dialogs::Dialog::reject()
410
411 Closes the dialog and emits the \l rejected() signal.
412
413 \sa accept()
414*/
415void QQuickAbstractDialog::reject()
416{
417 done(Rejected);
418}
419
420/*!
421 \qmlmethod void QtQuick.Dialogs::Dialog::done(int result)
422
423 Closes the dialog and sets the \a result.
424
425 \sa accept(), reject(), result
426*/
427void QQuickAbstractDialog::done(int result)
428{
429 setResult(result);
430 close();
431}
432
433void QQuickAbstractDialog::classBegin()
434{
435}
436
437void QQuickAbstractDialog::componentComplete()
438{
439 qCDebug(lcDialogs) << "componentComplete";
440 m_complete = true;
441
442 if (!m_visibleRequested)
443 return;
444
445 m_visibleRequested = false;
446
447 if (windowForOpen()) {
448 open();
449 return;
450 }
451
452 // Since visible were set to true by the user, we want the dialog to be open by default.
453 // There is no guarantee that the dialog will work when it exists in a object tree that lacks a window,
454 // and since qml components are sometimes instantiated before they're given a window
455 // (which is the case when using QQuickView), we want to delay the call to open(), until the window is provided.
456 if (const auto parentItem = findParentItem())
457 connect(parentItem, &QQuickItem::windowChanged, this, &QQuickAbstractDialog::deferredOpen, Qt::SingleShotConnection);
458}
459
460static const char *qmlTypeName(const QObject *object)
461{
462 return object->metaObject()->className() + qstrlen("QQuickPlatform");
463}
464
465QPlatformTheme::DialogType toPlatformDialogType(QQuickDialogType quickDialogType)
466{
467 return quickDialogType == QQuickDialogType::FolderDialog
468 ? QPlatformTheme::FileDialog : static_cast<QPlatformTheme::DialogType>(quickDialogType);
469}
470
471bool QQuickAbstractDialog::create(CreateOptions createOptions)
472{
473 qCDebug(lcDialogs) << qmlTypeName(this) << "attempting to create dialog backend of type"
474 << int(m_type) << "with parent window" << m_parentWindow;
475 if (m_handle)
476 return m_handle.get();
477
478 if ((createOptions != CreateOptions::DontTryNativeDialog) && useNativeDialog()) {
479 qCDebug(lcDialogs) << "- attempting to create a native dialog";
480 m_handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformDialogHelper(
481 toPlatformDialogType(m_type)));
482 }
483
484 if (!m_handle) {
485 qCDebug(lcDialogs) << "- attempting to create a quick dialog";
486 m_handle = QQuickDialogImplFactory::createPlatformDialogHelper(m_type, this);
487 }
488
489 qCDebug(lcDialogs) << qmlTypeName(this) << "created ->" << m_handle.get();
490 if (m_handle) {
491 onCreate(m_handle.get());
492 connect(m_handle.get(), &QPlatformDialogHelper::accept, this, &QQuickAbstractDialog::accept);
493 connect(m_handle.get(), &QPlatformDialogHelper::reject, this, &QQuickAbstractDialog::reject);
494 }
495 return m_handle.get();
496}
497
498void QQuickAbstractDialog::destroy()
499{
500 m_handle.reset();
501}
502
503bool QQuickAbstractDialog::useNativeDialog() const
504{
505 if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)) {
506 qCDebug(lcDialogs) << " - Qt::AA_DontUseNativeDialogs was set; not using native dialog";
507 return false;
508 }
509
510 if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(toPlatformDialogType(m_type))) {
511 qCDebug(lcDialogs) << " - the platform theme told us a native dialog isn't available; not using native dialog";
512 return false;
513 }
514
515 return true;
516}
517
518/*!
519 \internal
520
521 Called at the end of \l create().
522*/
523void QQuickAbstractDialog::onCreate(QPlatformDialogHelper *dialog)
524{
525 Q_UNUSED(dialog);
526}
527
528/*!
529 \internal
530
531 Called by \l open(), after the call to \l create() and before
532 the handle/helper's \c show function is called.
533*/
534void QQuickAbstractDialog::onShow(QPlatformDialogHelper *dialog)
535{
536 Q_UNUSED(dialog);
537 m_firstShow = false;
538}
539
540void QQuickAbstractDialog::onHide(QPlatformDialogHelper *dialog)
541{
542 Q_UNUSED(dialog);
543}
544
545int QQuickAbstractDialog::dialogCode() const { return m_result; }
546
547QQuickItem *QQuickAbstractDialog::findParentItem() const
548{
549 QObject *obj = parent();
550 while (obj) {
551 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
552 if (item)
553 return item;
554 obj = obj->parent();
555 }
556 return nullptr;
557}
558
559QWindow *QQuickAbstractDialog::windowForOpen() const
560{
561 if (m_parentWindowExplicitlySet)
562 return m_parentWindow;
563 else if (auto parentItem = findParentItem())
564 return parentItem->window();
565 return m_parentWindow;
566}
567
568void QQuickAbstractDialog::deferredOpen(QWindow *window)
569{
570 m_parentWindow = window;
571 open();
572}
573
574QT_END_NAMESPACE
575
576#include "moc_qquickabstractdialog_p.cpp"
Combined button and popup list for selecting options.
QPlatformTheme::DialogType toPlatformDialogType(QQuickDialogType quickDialogType)
static const char * qmlTypeName(const QObject *object)