1// Copyright (C) 2017 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
4#include "qquickdialog_p.h"
5#include "qquickdialog_p_p.h"
6#include "qquickdialogbuttonbox_p.h"
7#include "qquickabstractbutton_p.h"
8#include "qquickpopupitem_p_p.h"
9#include "qquickpopupwindow_p_p.h"
10#include <qpa/qplatformintegration.h>
11#include <private/qguiapplication_p.h>
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \qmltype Dialog
17 \inherits Popup
18//! \nativetype QQuickDialog
19 \inqmlmodule QtQuick.Controls
20 \ingroup qtquickcontrols-dialogs
21 \ingroup qtquickcontrols-popups
22 \brief Popup dialog with standard buttons and a title, used for short-term interaction with the user.
23 \since 5.8
24
25 A dialog is a popup mostly used for short-term tasks and brief communications
26 with the user. Similarly to \l ApplicationWindow and \l Page, Dialog is organized
27 into three sections: \l header, \l {Popup::}{contentItem}, and \l footer.
28
29 \image qtquickcontrols-page-wireframe.webp
30
31 The \l {Popup::}{padding} properties only affect the contentItem. Use the
32 \l {Popup::}{spacing} property to affect the space between header,
33 contentItem and footer.
34
35 By default, Dialogs have \l {QQuickItem::}{focus}.
36
37 \section1 Dialog Title and Buttons
38
39 Dialog's \l title is displayed by a style-specific title bar that is assigned
40 as a dialog \l header by default.
41
42 Dialog's standard buttons are managed by a \l DialogButtonBox that is assigned
43 as a dialog \l footer by default. The dialog's \l standardButtons property is
44 forwarded to the respective property of the button box. Furthermore, the
45 \l {DialogButtonBox::}{accepted()} and \l {DialogButtonBox::}{rejected()}
46 signals of the button box are connected to the respective signals in Dialog.
47
48 \snippet qtquickcontrols-dialog.qml 1
49
50 \section1 Modal Dialogs
51
52 A \l {Popup::}{modal} dialog blocks input to other content beneath
53 the dialog. When a modal dialog is opened, the user must finish
54 interacting with the dialog and close it before they can access any
55 other content in the same window.
56
57 \snippet qtquickcontrols-dialog-modal.qml 1
58
59 \section1 Modeless Dialogs
60
61 A modeless dialog is a dialog that operates independently of other
62 content around the dialog. When a modeless dialog is opened, the user
63 is allowed to interact with both the dialog and the other content in
64 the same window.
65
66 \snippet qtquickcontrols-dialog-modeless.qml 1
67
68 \sa DialogButtonBox, {Popup Controls}
69*/
70
71/*!
72 \qmlsignal QtQuick.Controls::Dialog::accepted()
73
74 This signal is emitted when the dialog has been accepted either
75 interactively or by calling \l accept().
76
77 \note This signal is \e not emitted when closing the dialog with
78 \l {Popup::}{close()} or setting \l {Popup::}{visible} to \c false.
79
80 \sa rejected()
81*/
82
83/*!
84 \qmlsignal QtQuick.Controls::Dialog::rejected()
85
86 This signal is emitted when the dialog has been rejected either
87 interactively or by calling \l reject().
88
89 \note This signal is \e not emitted when closing the dialog with
90 \l {Popup::}{close()} or setting \l {Popup::}{visible} to \c false.
91
92 \sa accepted()
93*/
94
95/*!
96 \since QtQuick.Controls 2.3 (Qt 5.10)
97 \qmlsignal QtQuick.Controls::Dialog::applied()
98
99 This signal is emitted when the \c Dialog.Apply standard button is clicked.
100
101 \sa discarded(), reset()
102*/
103
104/*!
105 \since QtQuick.Controls 2.3 (Qt 5.10)
106 \qmlsignal QtQuick.Controls::Dialog::reset()
107
108 This signal is emitted when the \c Dialog.Reset standard button is clicked.
109
110 \sa discarded(), applied()
111*/
112
113/*!
114 \since QtQuick.Controls 2.3 (Qt 5.10)
115 \qmlsignal QtQuick.Controls::Dialog::discarded()
116
117 This signal is emitted when the \c Dialog.Discard standard button is clicked.
118
119 \sa reset(), applied()
120*/
121
122/*!
123 \since QtQuick.Controls 2.3 (Qt 5.10)
124 \qmlsignal QtQuick.Controls::Dialog::helpRequested()
125
126 This signal is emitted when the \c Dialog.Help standard button is clicked.
127
128 \sa accepted(), rejected()
129*/
130
131QPlatformDialogHelper::ButtonRole QQuickDialogPrivate::buttonRole(QQuickAbstractButton *button)
132{
133 const QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(object: qmlAttachedPropertiesObject<QQuickDialogButtonBox>(obj: button, create: false));
134 return attached ? attached->buttonRole() : QPlatformDialogHelper::InvalidRole;
135}
136
137void QQuickDialogPrivate::handleAccept()
138{
139 Q_Q(QQuickDialog);
140 q->accept();
141}
142
143void QQuickDialogPrivate::handleReject()
144{
145 Q_Q(QQuickDialog);
146 q->reject();
147}
148
149void QQuickDialogPrivate::handleClick(QQuickAbstractButton *button)
150{
151 Q_Q(QQuickDialog);
152 switch (buttonRole(button)) {
153 case QPlatformDialogHelper::ApplyRole:
154 emit q->applied();
155 break;
156 case QPlatformDialogHelper::ResetRole:
157 emit q->reset();
158 break;
159 case QPlatformDialogHelper::DestructiveRole:
160 emit q->discarded();
161 q->close();
162 break;
163 case QPlatformDialogHelper::HelpRole:
164 emit q->helpRequested();
165 break;
166 default:
167 break;
168 }
169}
170
171Qt::WindowFlags QQuickDialogPrivate::popupWindowType() const
172{
173 return Qt::Dialog;
174}
175
176QQuickDialog::QQuickDialog(QObject *parent)
177 : QQuickDialog(*(new QQuickDialogPrivate), parent)
178{
179}
180
181QQuickDialog::QQuickDialog(QQuickDialogPrivate &dd, QObject *parent)
182 : QQuickPopup(dd, parent)
183{
184 Q_D(QQuickDialog);
185
186 // Dialogs should get active focus when opened so that e.g. Cancel closes them.
187 setFocus(true);
188 QObject::connect(sender: d->popupItem, signal: &QQuickPopupItem::headerChanged, context: this, slot: &QQuickDialog::headerChanged);
189 QObject::connect(sender: d->popupItem, signal: &QQuickPopupItem::footerChanged, context: this, slot: &QQuickDialog::footerChanged);
190 QObject::connect(sender: d->popupItem, signal: &QQuickPopupItem::implicitHeaderWidthChanged, context: this, slot: &QQuickDialog::implicitHeaderWidthChanged);
191 QObject::connect(sender: d->popupItem, signal: &QQuickPopupItem::implicitHeaderHeightChanged, context: this, slot: &QQuickDialog::implicitHeaderHeightChanged);
192 QObject::connect(sender: d->popupItem, signal: &QQuickPopupItem::implicitFooterWidthChanged, context: this, slot: &QQuickDialog::implicitFooterWidthChanged);
193 QObject::connect(sender: d->popupItem, signal: &QQuickPopupItem::implicitFooterHeightChanged, context: this, slot: &QQuickDialog::implicitFooterHeightChanged);
194}
195
196QQuickDialog::~QQuickDialog()
197{
198 Q_D(QQuickDialog);
199 QObject::disconnect(sender: d->popupItem, signal: &QQuickPopupItem::headerChanged, receiver: this, slot: &QQuickDialog::headerChanged);
200 QObject::disconnect(sender: d->popupItem, signal: &QQuickPopupItem::footerChanged, receiver: this, slot: &QQuickDialog::footerChanged);
201 QObject::disconnect(sender: d->popupItem, signal: &QQuickPopupItem::implicitHeaderWidthChanged, receiver: this, slot: &QQuickDialog::implicitHeaderWidthChanged);
202 QObject::disconnect(sender: d->popupItem, signal: &QQuickPopupItem::implicitHeaderHeightChanged, receiver: this, slot: &QQuickDialog::implicitHeaderHeightChanged);
203 QObject::disconnect(sender: d->popupItem, signal: &QQuickPopupItem::implicitFooterWidthChanged, receiver: this, slot: &QQuickDialog::implicitFooterWidthChanged);
204 QObject::disconnect(sender: d->popupItem, signal: &QQuickPopupItem::implicitFooterHeightChanged, receiver: this, slot: &QQuickDialog::implicitFooterHeightChanged);
205}
206
207/*!
208 \qmlproperty string QtQuick.Controls::Dialog::title
209
210 This property holds the dialog title.
211
212 The title is displayed in the dialog header.
213
214 \code
215 Dialog {
216 title: qsTr("About")
217
218 Label {
219 text: "Lorem ipsum..."
220 }
221 }
222 \endcode
223*/
224QString QQuickDialog::title() const
225{
226 Q_D(const QQuickDialog);
227 return d->title;
228}
229
230void QQuickDialog::setTitle(const QString &title)
231{
232 Q_D(QQuickDialog);
233 if (d->title == title)
234 return;
235 d->title = title;
236
237 if (d->popupWindow)
238 d->popupWindow->setTitle(title);
239 else
240 d->popupItem->setTitle(title);
241
242 emit titleChanged();
243}
244
245/*!
246 \qmlproperty Item QtQuick.Controls::Dialog::header
247
248 This property holds the dialog header item. The header item is positioned to
249 the top, and resized to the width of the dialog. The default value is \c null.
250
251 \note Assigning a \l DialogButtonBox as a dialog header automatically connects
252 its \l {DialogButtonBox::}{accepted()} and \l {DialogButtonBox::}{rejected()}
253 signals to the respective signals in Dialog.
254
255 \note Assigning a \l DialogButtonBox, \l ToolBar, or \l TabBar as a dialog
256 header automatically sets the respective \l DialogButtonBox::position,
257 \l ToolBar::position, or \l TabBar::position property to \c Header.
258
259 \sa footer
260*/
261QQuickItem *QQuickDialog::header() const
262{
263 Q_D(const QQuickDialog);
264 return d->popupItem->header();
265}
266
267void QQuickDialog::setHeader(QQuickItem *header)
268{
269 Q_D(QQuickDialog);
270 QQuickItem *oldHeader = d->popupItem->header();
271 if (oldHeader == header)
272 return;
273
274 QQuickControlPrivate::warnIfCustomizationNotSupported(control: this, item: header, QStringLiteral("header"));
275
276 if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: oldHeader)) {
277 QObjectPrivate::disconnect(sender: buttonBox, signal: &QQuickDialogButtonBox::accepted, receiverPrivate: d, slot: &QQuickDialogPrivate::handleAccept);
278 QObjectPrivate::disconnect(sender: buttonBox, signal: &QQuickDialogButtonBox::rejected, receiverPrivate: d, slot: &QQuickDialogPrivate::handleReject);
279 QObjectPrivate::disconnect(sender: buttonBox, signal: &QQuickDialogButtonBox::clicked, receiverPrivate: d, slot: &QQuickDialogPrivate::handleClick);
280 if (d->buttonBox == buttonBox)
281 d->buttonBox = nullptr;
282 }
283
284 if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: header)) {
285 QObjectPrivate::connect(sender: buttonBox, signal: &QQuickDialogButtonBox::accepted, receiverPrivate: d, slot: &QQuickDialogPrivate::handleAccept);
286 QObjectPrivate::connect(sender: buttonBox, signal: &QQuickDialogButtonBox::rejected, receiverPrivate: d, slot: &QQuickDialogPrivate::handleReject);
287 QObjectPrivate::connect(sender: buttonBox, signal: &QQuickDialogButtonBox::clicked, receiverPrivate: d, slot: &QQuickDialogPrivate::handleClick);
288 d->buttonBox = buttonBox;
289 buttonBox->setStandardButtons(d->standardButtons);
290 }
291
292 d->popupItem->setHeader(header);
293}
294
295/*!
296 \qmlproperty Item QtQuick.Controls::Dialog::footer
297
298 This property holds the dialog footer item. The footer item is positioned to
299 the bottom, and resized to the width of the dialog. The default value is \c null.
300
301 \note Assigning a \l DialogButtonBox as a dialog footer automatically connects
302 its \l {DialogButtonBox::}{accepted()} and \l {DialogButtonBox::}{rejected()}
303 signals to the respective signals in Dialog.
304
305 \note Assigning a \l DialogButtonBox, \l ToolBar, or \l TabBar as a dialog
306 footer automatically sets the respective \l DialogButtonBox::position,
307 \l ToolBar::position, or \l TabBar::position property to \c Footer.
308
309 \sa header
310*/
311QQuickItem *QQuickDialog::footer() const
312{
313 Q_D(const QQuickDialog);
314 return d->popupItem->footer();
315}
316
317void QQuickDialog::setFooter(QQuickItem *footer)
318{
319 Q_D(QQuickDialog);
320 QQuickItem *oldFooter = d->popupItem->footer();
321 if (oldFooter == footer)
322 return;
323
324 QQuickControlPrivate::warnIfCustomizationNotSupported(control: this, item: footer, QStringLiteral("footer"));
325
326 if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: oldFooter)) {
327 QObjectPrivate::disconnect(sender: buttonBox, signal: &QQuickDialogButtonBox::accepted, receiverPrivate: d, slot: &QQuickDialogPrivate::handleAccept);
328 QObjectPrivate::disconnect(sender: buttonBox, signal: &QQuickDialogButtonBox::rejected, receiverPrivate: d, slot: &QQuickDialogPrivate::handleReject);
329 QObjectPrivate::disconnect(sender: buttonBox, signal: &QQuickDialogButtonBox::clicked, receiverPrivate: d, slot: &QQuickDialogPrivate::handleClick);
330 if (d->buttonBox == buttonBox)
331 d->buttonBox = nullptr;
332 }
333 if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: footer)) {
334 QObjectPrivate::connect(sender: buttonBox, signal: &QQuickDialogButtonBox::accepted, receiverPrivate: d, slot: &QQuickDialogPrivate::handleAccept);
335 QObjectPrivate::connect(sender: buttonBox, signal: &QQuickDialogButtonBox::rejected, receiverPrivate: d, slot: &QQuickDialogPrivate::handleReject);
336 QObjectPrivate::connect(sender: buttonBox, signal: &QQuickDialogButtonBox::clicked, receiverPrivate: d, slot: &QQuickDialogPrivate::handleClick);
337 d->buttonBox = buttonBox;
338 buttonBox->setStandardButtons(d->standardButtons);
339 }
340
341 d->popupItem->setFooter(footer);
342}
343
344/*!
345 \qmlproperty enumeration QtQuick.Controls::Dialog::standardButtons
346
347 This property holds a combination of standard buttons that are used by the dialog.
348
349 \snippet qtquickcontrols-dialog.qml 1
350
351 The buttons will be positioned in the appropriate order for the user's platform.
352
353 Possible flags:
354 \value Dialog.Ok An "OK" button defined with the \c AcceptRole.
355 \value Dialog.Open An "Open" button defined with the \c AcceptRole.
356 \value Dialog.Save A "Save" button defined with the \c AcceptRole.
357 \value Dialog.Cancel A "Cancel" button defined with the \c RejectRole.
358 \value Dialog.Close A "Close" button defined with the \c RejectRole.
359 \value Dialog.Discard A "Discard" or "Don't Save" button, depending on the platform, defined with the \c DestructiveRole.
360 \value Dialog.Apply An "Apply" button defined with the \c ApplyRole.
361 \value Dialog.Reset A "Reset" button defined with the \c ResetRole.
362 \value Dialog.RestoreDefaults A "Restore Defaults" button defined with the \c ResetRole.
363 \value Dialog.Help A "Help" button defined with the \c HelpRole.
364 \value Dialog.SaveAll A "Save All" button defined with the \c AcceptRole.
365 \value Dialog.Yes A "Yes" button defined with the \c YesRole.
366 \value Dialog.YesToAll A "Yes to All" button defined with the \c YesRole.
367 \value Dialog.No A "No" button defined with the \c NoRole.
368 \value Dialog.NoToAll A "No to All" button defined with the \c NoRole.
369 \value Dialog.Abort An "Abort" button defined with the \c RejectRole.
370 \value Dialog.Retry A "Retry" button defined with the \c AcceptRole.
371 \value Dialog.Ignore An "Ignore" button defined with the \c AcceptRole.
372 \value Dialog.NoButton An invalid button.
373
374 \sa DialogButtonBox
375*/
376QPlatformDialogHelper::StandardButtons QQuickDialog::standardButtons() const
377{
378 Q_D(const QQuickDialog);
379 return d->standardButtons;
380}
381
382void QQuickDialog::setStandardButtons(QPlatformDialogHelper::StandardButtons buttons)
383{
384 Q_D(QQuickDialog);
385 if (d->standardButtons == buttons)
386 return;
387
388 d->standardButtons = buttons;
389 if (d->buttonBox)
390 d->buttonBox->setStandardButtons(buttons);
391 emit standardButtonsChanged();
392}
393
394/*!
395 \since QtQuick.Controls 2.3 (Qt 5.10)
396 \qmlmethod AbstractButton QtQuick.Controls::Dialog::standardButton(StandardButton button)
397
398 Returns the specified standard \a button, or \c null if it does not exist.
399
400 \sa standardButtons
401*/
402QQuickAbstractButton *QQuickDialog::standardButton(QPlatformDialogHelper::StandardButton button) const
403{
404 Q_D(const QQuickDialog);
405 if (!d->buttonBox)
406 return nullptr;
407 return d->buttonBox->standardButton(button);
408}
409
410/*!
411 \since QtQuick.Controls 2.3 (Qt 5.10)
412 \qmlproperty int QtQuick.Controls::Dialog::result
413
414 This property holds the result code.
415
416 Standard result codes:
417 \value Dialog.Accepted The dialog was accepted.
418 \value Dialog.Rejected The dialog was rejected.
419
420 \sa accept(), reject(), done()
421*/
422int QQuickDialog::result() const
423{
424 Q_D(const QQuickDialog);
425 return d->result;
426}
427
428void QQuickDialog::setResult(int result)
429{
430 Q_D(QQuickDialog);
431 if (d->result == result)
432 return;
433
434 d->result = result;
435 emit resultChanged();
436}
437
438/*!
439 \since QtQuick.Controls 2.5 (Qt 5.12)
440 \qmlproperty real QtQuick.Controls::Dialog::implicitHeaderWidth
441 \readonly
442
443 This property holds the implicit header width.
444
445 The value is equal to \c {header && header.visible ? header.implicitWidth : 0}.
446
447 \sa implicitHeaderHeight, implicitFooterWidth
448*/
449qreal QQuickDialog::implicitHeaderWidth() const
450{
451 Q_D(const QQuickDialog);
452 return d->popupItem->implicitHeaderWidth();
453}
454
455/*!
456 \since QtQuick.Controls 2.5 (Qt 5.12)
457 \qmlproperty real QtQuick.Controls::Dialog::implicitHeaderHeight
458 \readonly
459
460 This property holds the implicit header height.
461
462 The value is equal to \c {header && header.visible ? header.implicitHeight : 0}.
463
464 \sa implicitHeaderWidth, implicitFooterHeight
465*/
466qreal QQuickDialog::implicitHeaderHeight() const
467{
468 Q_D(const QQuickDialog);
469 return d->popupItem->implicitHeaderHeight();
470}
471
472/*!
473 \since QtQuick.Controls 2.5 (Qt 5.12)
474 \qmlproperty real QtQuick.Controls::Dialog::implicitFooterWidth
475 \readonly
476
477 This property holds the implicit footer width.
478
479 The value is equal to \c {footer && footer.visible ? footer.implicitWidth : 0}.
480
481 \sa implicitFooterHeight, implicitHeaderWidth
482*/
483qreal QQuickDialog::implicitFooterWidth() const
484{
485 Q_D(const QQuickDialog);
486 return d->popupItem->implicitFooterWidth();
487}
488
489/*!
490 \since QtQuick.Controls 2.5 (Qt 5.12)
491 \qmlproperty real QtQuick.Controls::Dialog::implicitFooterHeight
492 \readonly
493
494 This property holds the implicit footer height.
495
496 The value is equal to \c {footer && footer.visible ? footer.implicitHeight : 0}.
497
498 \sa implicitFooterWidth, implicitHeaderHeight
499*/
500qreal QQuickDialog::implicitFooterHeight() const
501{
502 Q_D(const QQuickDialog);
503 return d->popupItem->implicitFooterHeight();
504}
505
506void QQuickDialog::setOpacity(qreal opacity)
507{
508 Q_D(QQuickDialog);
509 QQuickPopup::setOpacity(d->popupWindow ? qreal(!qFuzzyIsNull(d: opacity)) : opacity);
510}
511
512/*!
513 \qmlmethod void QtQuick.Controls::Dialog::accept()
514
515 Emits the \l accepted() signal and closes the dialog.
516
517 \sa reject(), done()
518*/
519void QQuickDialog::accept()
520{
521 done(result: Accepted);
522}
523
524/*!
525 \qmlmethod void QtQuick.Controls::Dialog::reject()
526
527 Emits the \l rejected() signal and closes the dialog.
528
529 \sa accept(), done()
530*/
531void QQuickDialog::reject()
532{
533 done(result: Rejected);
534}
535
536/*!
537 \since QtQuick.Controls 2.3 (Qt 5.10)
538 \qmlmethod void QtQuick.Controls::Dialog::done(int result)
539
540 \list 1
541 \li Sets the \a result.
542 \li Emits \l accepted() or \l rejected() depending on
543 whether the result is \c Dialog.Accepted or \c Dialog.Rejected,
544 respectively.
545 \li Emits \l{Popup::}{closed()}.
546 \endlist
547
548 \sa accept(), reject(), result
549*/
550void QQuickDialog::done(int result)
551{
552 setResult(result);
553
554 if (result == Accepted)
555 emit accepted();
556 else if (result == Rejected)
557 emit rejected();
558
559 close();
560}
561
562#if QT_CONFIG(accessibility)
563QAccessible::Role QQuickDialog::accessibleRole() const
564{
565 return QAccessible::Dialog;
566}
567
568void QQuickDialog::accessibilityActiveChanged(bool active)
569{
570 Q_D(QQuickDialog);
571 QQuickPopup::accessibilityActiveChanged(active);
572
573 if (active)
574 maybeSetAccessibleName(name: d->popupItem->title());
575}
576#endif
577
578QT_END_NAMESPACE
579
580#include "moc_qquickdialog_p.cpp"
581

source code of qtdeclarative/src/quicktemplates/qquickdialog.cpp