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