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

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