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
qdialog.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
5#include "kernel/qwidget_p.h"
6#include <QtWidgets/qtwidgetsglobal.h>
7#include <utility>
8#if QT_CONFIG(colordialog)
9#include "qcolordialog.h"
10#endif
11#if QT_CONFIG(fontdialog)
12#include "qfontdialog.h"
13#endif
14#if QT_CONFIG(filedialog)
15#include "qfiledialog.h"
16#endif
17
18#include "qevent.h"
19#include "qapplication.h"
20#include "qlayout.h"
21#if QT_CONFIG(sizegrip)
22#include "qsizegrip.h"
23#endif
24#if QT_CONFIG(whatsthis)
25#include "qwhatsthis.h"
26#endif
27#if QT_CONFIG(menu)
28#include "qmenu.h"
29#endif
30#include "qcursor.h"
31#if QT_CONFIG(messagebox)
32#include "qmessagebox.h"
33#endif
34#if QT_CONFIG(errormessage)
35#include "qerrormessage.h"
36#endif
37#include <qpa/qplatformtheme.h>
38#include "private/qdialog_p.h"
39#include "private/qguiapplication_p.h"
40#if QT_CONFIG(accessibility)
41#include "qaccessible.h"
42#endif
43
45
46static inline int themeDialogType(const QDialog *dialog)
47{
48#if QT_CONFIG(filedialog)
49 if (qobject_cast<const QFileDialog *>(dialog))
50 return QPlatformTheme::FileDialog;
51#endif
52#if QT_CONFIG(colordialog)
53 if (qobject_cast<const QColorDialog *>(dialog))
54 return QPlatformTheme::ColorDialog;
55#endif
56#if QT_CONFIG(fontdialog)
57 if (qobject_cast<const QFontDialog *>(dialog))
58 return QPlatformTheme::FontDialog;
59#endif
60#if QT_CONFIG(messagebox)
61 if (qobject_cast<const QMessageBox *>(dialog))
62 return QPlatformTheme::MessageDialog;
63#endif
64#if QT_CONFIG(errormessage)
65 if (qobject_cast<const QErrorMessage *>(dialog))
66 return QPlatformTheme::MessageDialog;
67#endif
68#if !QT_CONFIG(filedialog) && !QT_CONFIG(colordialog) && !QT_CONFIG(fontdialog) &&
69 !QT_CONFIG(messagebox) && !QT_CONFIG(errormessage)
70 Q_UNUSED(dialog);
71#endif
72 return -1;
73}
74
75QDialogPrivate::~QDialogPrivate()
76{
77 delete m_platformHelper;
78}
79
80QPlatformDialogHelper *QDialogPrivate::platformHelper() const
81{
82 // Delayed creation of the platform, ensuring that
83 // that qobject_cast<> on the dialog works in the plugin.
84 if (!m_platformHelperCreated && canBeNativeDialog()) {
85 m_platformHelperCreated = true;
86 QDialogPrivate *ncThis = const_cast<QDialogPrivate *>(this);
87 QDialog *dialog = ncThis->q_func();
88 const int type = themeDialogType(dialog);
89 if (type >= 0) {
90 m_platformHelper = QGuiApplicationPrivate::platformTheme()
91 ->createPlatformDialogHelper(static_cast<QPlatformTheme::DialogType>(type));
92 if (m_platformHelper) {
93 QObject::connect(m_platformHelper, SIGNAL(accept()), dialog, SLOT(accept()));
94 QObject::connect(m_platformHelper, SIGNAL(reject()), dialog, SLOT(reject()));
95 ncThis->initHelper(m_platformHelper);
96 }
97 }
98 }
99 return m_platformHelper;
100}
101
102bool QDialogPrivate::canBeNativeDialog() const
103{
104 if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs))
105 return false;
106
107 QDialogPrivate *ncThis = const_cast<QDialogPrivate *>(this);
108 QDialog *dialog = ncThis->q_func();
109 const int type = themeDialogType(dialog);
110 if (type >= 0)
111 return QGuiApplicationPrivate::platformTheme()
112 ->usePlatformNativeDialog(static_cast<QPlatformTheme::DialogType>(type));
113 return false;
114}
115
116/*!
117 \internal
118
119 Properly closes dialog and sets the \a resultCode.
120 */
121void QDialogPrivate::close(int resultCode)
122{
123 Q_Q(QDialog);
124
125 q->setResult(resultCode);
126
127 if (!data.is_closing) {
128 // Until Qt 6.3 we didn't close dialogs, so they didn't receive a QCloseEvent.
129 // It is likely that subclasses implement closeEvent and handle them as rejection
130 // (like QMessageBox and QProgressDialog do), so eat those events.
131 struct CloseEventEater : QObject
132 {
133 using QObject::QObject;
134 protected:
135 bool eventFilter(QObject *o, QEvent *e) override
136 {
137 if (e->type() == QEvent::Close)
138 return true;
139 return QObject::eventFilter(o, e);
140 }
141 } closeEventEater;
142 q->installEventFilter(&closeEventEater);
143 QWidgetPrivate::close();
144 } else {
145 // If the close was initiated outside of QDialog we will end up
146 // here via QDialog::closeEvent calling reject(), in which case
147 // we need to hide the dialog to ensure QDialog::closeEvent does
148 // not ignore the close event. FIXME: Why is QDialog doing this?
149 q->hide();
150 }
151
152 resetModalitySetByOpen();
153}
154
155void QDialogPrivate::recreate()
156{
157 // Calling destroy while having the eventLoop pointer in place would cause the
158 // event loop to exit - however, with recreate, we do not actually want this behavior
159 // but want to keep the QDialog alive, just with a different RHI configuration
160 QPointer<QEventLoop> existingEventLoop = std::exchange(eventLoop, nullptr);
161 QWidgetPrivate::recreate();
162 eventLoop = existingEventLoop;
163}
164
165QWindow *QDialogPrivate::transientParentWindow() const
166{
167 Q_Q(const QDialog);
168 if (const QWidget *parent = q->nativeParentWidget())
169 return parent->windowHandle();
170 else if (q->windowHandle())
171 return q->windowHandle()->transientParent();
172 return nullptr;
173}
174
175bool QDialogPrivate::setNativeDialogVisible(bool visible)
176{
177 if (QPlatformDialogHelper *helper = platformHelper()) {
178 if (visible) {
179 Q_Q(QDialog);
180 helperPrepareShow(helper);
181 nativeDialogInUse = helper->show(q->windowFlags(), q->windowModality(), transientParentWindow());
182 } else if (nativeDialogInUse) {
183 helper->hide();
184 }
185 }
186 return nativeDialogInUse;
187}
188
189QVariant QDialogPrivate::styleHint(QPlatformDialogHelper::StyleHint hint) const
190{
191 if (const QPlatformDialogHelper *helper = platformHelper())
192 return helper->styleHint(hint);
193 return QPlatformDialogHelper::defaultStyleHint(hint);
194}
195
196/*!
197 \class QDialog
198 \brief The QDialog class is the base class of dialog windows.
199
200 \ingroup dialog-classes
201 \ingroup abstractwidgets
202 \inmodule QtWidgets
203
204 A dialog window is a top-level window mostly used for short-term
205 tasks and brief communications with the user. QDialogs may be
206 modal or modeless. QDialogs can
207 provide a \l{#return}{return value}, and they can have \l{#default}{default buttons}. QDialogs can also have a QSizeGrip in their
208 lower-right corner, using setSizeGripEnabled().
209
210 Note that QDialog (and any other widget that has type \c Qt::Dialog) uses
211 the parent widget slightly differently from other classes in Qt. A dialog is
212 always a top-level widget, but if it has a parent, its default location is
213 centered on top of the parent's top-level widget (if it is not top-level
214 itself). It will also share the parent's taskbar entry.
215
216 Use the overload of the QWidget::setParent() function to change
217 the ownership of a QDialog widget. This function allows you to
218 explicitly set the window flags of the reparented widget; using
219 the overloaded function will clear the window flags specifying the
220 window-system properties for the widget (in particular it will
221 reset the Qt::Dialog flag).
222
223 \note The parent relationship of the dialog does \e{not} imply
224 that the dialog will always be stacked on top of the parent
225 window. To ensure that the dialog is always on top, make the
226 dialog modal. This also applies for child windows of the dialog
227 itself. To ensure that child windows of the dialog stay on top
228 of the dialog, make the child windows modal as well.
229
230 \section1 Modal Dialogs
231
232 A \b{modal} dialog is a dialog that blocks input to other
233 visible windows in the same application. Dialogs that are used to
234 request a file name from the user or that are used to set
235 application preferences are usually modal. Dialogs can be
236 \l{Qt::ApplicationModal}{application modal} (the default) or
237 \l{Qt::WindowModal}{window modal}.
238
239 When an application modal dialog is opened, the user must finish
240 interacting with the dialog and close it before they can access
241 any other window in the application. Window modal dialogs only
242 block access to the window associated with the dialog, allowing
243 the user to continue to use other windows in an application.
244
245 The most common way to display a modal dialog is to call its
246 \l open() function. Alternatively, you can call \l{setModal()}{setModal(true)} or
247 \l setWindowModality(), and then \l show(). In both cases, once the dialog is
248 displayed, the control is immediately returned to the caller. You must connect
249 to the \l finished() signal to know when the dialog is closed and what its
250 \l {#return} {return value} is. Alternatively, you can connect to the
251 \l accepted() and \l rejected() signals.
252
253 When implementing a custom dialog, to close the dialog and return an
254 appropriate value, connect a default button, for example, an OK button, to the
255 \l accept() slot, and a Cancel button to the \l reject() slot. Alternatively,
256 you can call the \l done() slot with \c Accepted or \c Rejected.
257
258 If you show the modal dialog to perform a long-running operation, it is
259 recommended to perform the operation in a background worker thread, so that
260 it does not interfere with the GUI thread.
261
262 \warning When using \l open() or \l show(), the modal dialog should not be
263 created on the stack, so that it does not get destroyed as soon as the control
264 returns to the caller.
265
266 \note There is a way to show a modal dialog in a blocking mode by calling
267 \l exec(). In this case, the control returns to the GUI thread only when the
268 dialog is closed. However, such approach is discouraged, because it creates a
269 nested event loop, which is not fully supported by some platforms.
270
271 \section1 Modeless Dialogs
272
273 A \b{modeless} dialog is a dialog that operates
274 independently of other windows in the same application. Find and
275 replace dialogs in word-processors are often modeless to allow the
276 user to interact with both the application's main window and with
277 the dialog.
278
279 Modeless dialogs are displayed using show(), which returns control
280 to the caller immediately.
281
282 If you invoke the \l{QWidget::show()}{show()} function after hiding
283 a dialog, the dialog will be displayed in its original position. This is
284 because the window manager decides the position for windows that
285 have not been explicitly placed by the programmer. To preserve the
286 position of a dialog that has been moved by the user, save its position
287 in your \l{QWidget::closeEvent()}{closeEvent()} handler and then
288 move the dialog to that position, before showing it again.
289
290 \target default
291 \section1 Default Button
292
293 A dialog's \e default button is the button that's pressed when the
294 user presses Enter (Return). This button is used to signify that
295 the user accepts the dialog's settings and wants to close the
296 dialog. Use QPushButton::setDefault(), QPushButton::isDefault()
297 and QPushButton::autoDefault() to set and control the dialog's
298 default button.
299
300 \target escapekey
301 \section1 Escape Key
302
303 If the user presses the Esc key in a dialog, QDialog::reject()
304 will be called. This will cause the window to close:
305 The \l{QCloseEvent}{close event} cannot be \l{QEvent::ignore()}{ignored}.
306
307 \section1 Extensibility
308
309 Extensibility is the ability to show the dialog in two ways: a
310 partial dialog that shows the most commonly used options, and a
311 full dialog that shows all the options. Typically an extensible
312 dialog will initially appear as a partial dialog, but with a
313 \uicontrol More toggle button. If the user presses the
314 \uicontrol More button down, the dialog is expanded.
315
316 \target return
317 \section1 Return Value (Modal Dialogs)
318
319 Modal dialogs are often used in situations where a return value is
320 required, e.g. to indicate whether the user pressed \uicontrol OK or
321 \uicontrol Cancel. A dialog can be closed by calling the accept() or the
322 reject() slots, and exec() will return \c Accepted or \c Rejected
323 as appropriate. The exec() call returns the result of the dialog.
324 The result is also available from result() if the dialog has not
325 been destroyed.
326
327 In order to modify your dialog's close behavior, you can reimplement
328 the functions accept(), reject() or done(). The
329 \l{QWidget::closeEvent()}{closeEvent()} function should only be
330 reimplemented to preserve the dialog's position or to override the
331 standard close or reject behavior.
332
333 \target examples
334 \section1 Code Examples
335
336 A modal dialog:
337
338 \snippet dialogs/dialogs.cpp 1
339
340 A modeless dialog:
341
342 \snippet dialogs/dialogs.cpp 0
343
344 A dialog with an extension:
345
346 \snippet dialogs/dialogs.cpp extension
347
348 By setting the \l{QLayout::}{sizeConstraint} property of the dialog's
349 layout to \l{QLayout::}{SetFixedSize}, the dialog will not be resizable
350 by the user, and will automatically shrink when the extension gets hidden.
351
352 \sa QDialogButtonBox, QTabWidget, QWidget, QProgressDialog,
353 {Standard Dialogs Example}
354*/
355
356/*! \enum QDialog::DialogCode
357
358 The value returned by a modal dialog.
359
360 \value Accepted
361 \value Rejected
362*/
363
364/*!
365 \property QDialog::sizeGripEnabled
366 \brief whether the size grip is enabled
367
368 A QSizeGrip is placed in the bottom-right corner of the dialog when this
369 property is enabled. By default, the size grip is disabled.
370*/
371
372
373/*!
374 Constructs a dialog with parent \a parent.
375
376 A dialog is always a top-level widget, but if it has a parent, its
377 default location is centered on top of the parent. It will also
378 share the parent's taskbar entry.
379
380 The widget flags \a f are passed on to the QWidget constructor.
381 If, for example, you don't want a What's This button in the title bar
382 of the dialog, pass Qt::WindowTitleHint | Qt::WindowSystemMenuHint in \a f.
383
384 \sa QWidget::setWindowFlags()
385*/
386
387QDialog::QDialog(QWidget *parent, Qt::WindowFlags f)
388 : QWidget(*new QDialogPrivate, parent,
389 f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0)))
390{
391}
392
393/*!
394 \overload
395 \internal
396*/
397QDialog::QDialog(QDialogPrivate &dd, QWidget *parent, Qt::WindowFlags f)
398 : QWidget(dd, parent, f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0)))
399{
400}
401
402/*!
403 Destroys the QDialog, deleting all its children.
404*/
405
406QDialog::~QDialog()
407{
408 QT_TRY {
409 // Need to hide() here, as our (to-be) overridden hide()
410 // will not be called in ~QWidget.
411 hide();
412 } QT_CATCH(...) {
413 // we're in the destructor - just swallow the exception
414 }
415}
416
417/*!
418 \internal
419 This function is called by the push button \a pushButton when it
420 becomes the default button. If \a pushButton is \nullptr, the dialogs
421 default default button becomes the default button. This is what a
422 push button calls when it loses focus.
423*/
424#if QT_CONFIG(pushbutton)
425void QDialogPrivate::setDefault(QPushButton *pushButton)
426{
427 Q_Q(QDialog);
428 bool hasMain = false;
429 QList<QPushButton*> list = q->findChildren<QPushButton*>();
430 for (int i=0; i<list.size(); ++i) {
431 QPushButton *pb = list.at(i);
432 if (pb->window() == q) {
433 if (pb == mainDef)
434 hasMain = true;
435 if (pb != pushButton)
436 pb->setDefault(false);
437 }
438 }
439 if (!pushButton && hasMain)
440 mainDef->setDefault(true);
441 if (!hasMain)
442 mainDef = pushButton;
443}
444
445/*!
446 \internal
447 This function sets the default default push button to \a pushButton.
448 This function is called by QPushButton::setDefault().
449*/
450void QDialogPrivate::setMainDefault(QPushButton *pushButton)
451{
452 mainDef = nullptr;
453 setDefault(pushButton);
454}
455
456/*!
457 \internal
458 Hides the default button indicator. Called when non auto-default
459 push button get focus.
460 */
461void QDialogPrivate::hideDefault()
462{
463 Q_Q(QDialog);
464 QList<QPushButton*> list = q->findChildren<QPushButton*>();
465 for (int i=0; i<list.size(); ++i) {
466 list.at(i)->setDefault(false);
467 }
468}
469#endif
470
471void QDialogPrivate::resetModalitySetByOpen()
472{
473 Q_Q(QDialog);
474 if (resetModalityTo != -1 && !q->testAttribute(Qt::WA_SetWindowModality)) {
475 // open() changed the window modality and the user didn't touch it afterwards; restore it
476 q->setWindowModality(Qt::WindowModality(resetModalityTo));
477 q->setAttribute(Qt::WA_SetWindowModality, wasModalitySet);
478#ifdef Q_OS_MACOS
479 Q_ASSERT(resetModalityTo != Qt::WindowModal);
480 q->setParent(q->parentWidget(), Qt::Dialog);
481#endif
482 }
483 resetModalityTo = -1;
484}
485
486/*!
487 In general returns the modal dialog's result code, \c Accepted or
488 \c Rejected.
489
490 \note When called on a QMessageBox instance, the returned value is a
491 value of the \l QMessageBox::StandardButton enum.
492
493 Do not call this function if the dialog was constructed with the
494 Qt::WA_DeleteOnClose attribute.
495*/
496int QDialog::result() const
497{
498 Q_D(const QDialog);
499 return d->rescode;
500}
501
502/*!
503 \fn void QDialog::setResult(int i)
504
505 Sets the modal dialog's result code to \a i.
506
507 \note We recommend that you use one of the values defined by
508 QDialog::DialogCode.
509*/
510void QDialog::setResult(int r)
511{
512 Q_D(QDialog);
513 d->rescode = r;
514}
515
516/*!
517 Shows the dialog as a \l{QDialog#Modal Dialogs}{window modal dialog},
518 returning immediately.
519
520 \sa exec(), show(), result(), setWindowModality()
521*/
522void QDialog::open()
523{
524 Q_D(QDialog);
525
526 Qt::WindowModality modality = windowModality();
527 if (modality != Qt::WindowModal) {
528 d->resetModalityTo = modality;
529 d->wasModalitySet = testAttribute(Qt::WA_SetWindowModality);
530 setWindowModality(Qt::WindowModal);
531 setAttribute(Qt::WA_SetWindowModality, false);
532#ifdef Q_OS_MACOS
533 setParent(parentWidget(), Qt::Sheet);
534#endif
535 }
536
537 setResult(0);
538 show();
539}
540
541/*!
542 Shows the dialog as a \l{QDialog#Modal Dialogs}{modal dialog},
543 blocking until the user closes it. The function returns a \l
544 DialogCode result.
545
546 If the dialog is \l{Qt::ApplicationModal}{application modal}, users cannot
547 interact with any other window in the same application until they close
548 the dialog. If the dialog is \l{Qt::ApplicationModal}{window modal}, only
549 interaction with the parent window is blocked while the dialog is open.
550 By default, the dialog is application modal.
551
552 \note Avoid using this function; instead, use \c{open()}. Unlike exec(),
553 open() is asynchronous, and does not spin an additional event loop. This
554 prevents a series of dangerous bugs from happening (e.g. deleting the
555 dialog's parent while the dialog is open via exec()). When using open() you
556 can connect to the finished() signal of QDialog to be notified when the
557 dialog is closed.
558
559 \sa open(), show(), result(), setWindowModality()
560*/
561
562int QDialog::exec()
563{
564 Q_D(QDialog);
565
566 if (Q_UNLIKELY(d->eventLoop)) {
567 qWarning("QDialog::exec: Recursive call detected");
568 return -1;
569 }
570
571 bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
572 setAttribute(Qt::WA_DeleteOnClose, false);
573
574 d->resetModalitySetByOpen();
575
576 bool wasShowModal = testAttribute(Qt::WA_ShowModal);
577 setAttribute(Qt::WA_ShowModal, true);
578 setResult(0);
579
580 show();
581
582 QPointer<QDialog> guard = this;
583 if (d->nativeDialogInUse) {
584 d->platformHelper()->exec();
585 } else {
586 QEventLoop eventLoop;
587 d->eventLoop = &eventLoop;
588 (void) eventLoop.exec(QEventLoop::DialogExec);
589 }
590 if (guard.isNull())
591 return QDialog::Rejected;
592 d->eventLoop = nullptr;
593
594 setAttribute(Qt::WA_ShowModal, wasShowModal);
595
596 int res = result();
597 if (d->nativeDialogInUse)
598 d->helperDone(static_cast<QDialog::DialogCode>(res), d->platformHelper());
599 if (deleteOnClose)
600 delete this;
601 return res;
602}
603
604/*!
605 Closes the dialog and sets its result code to \a r. The finished() signal
606 will emit \a r; if \a r is QDialog::Accepted or QDialog::Rejected, the
607 accepted() or the rejected() signals will also be emitted, respectively.
608
609 If this dialog is shown with exec(), done() also causes the local event loop
610 to finish, and exec() to return \a r.
611
612 As with QWidget::close(), done() deletes the dialog if the
613 Qt::WA_DeleteOnClose flag is set. If the dialog is the application's
614 main widget, the application terminates. If the dialog is the
615 last window closed, the QGuiApplication::lastWindowClosed() signal is
616 emitted.
617
618 \sa accept(), reject(), QApplication::activeWindow(), QCoreApplication::quit()
619*/
620
621void QDialog::done(int r)
622{
623 QPointer<QDialog> guard(this);
624
625 Q_D(QDialog);
626 d->close(r);
627
628 if (!guard)
629 return;
630
631 int dialogCode = d->dialogCode();
632 if (dialogCode == QDialog::Accepted)
633 emit accepted();
634 else if (dialogCode == QDialog::Rejected)
635 emit rejected();
636
637 if (guard)
638 emit finished(r);
639}
640
641/*!
642 Hides the modal dialog and sets the result code to \c Accepted.
643
644 \sa reject(), done()
645*/
646
647void QDialog::accept()
648{
649 done(Accepted);
650}
651
652/*!
653 Hides the modal dialog and sets the result code to \c Rejected.
654
655 \sa accept(), done()
656*/
657
658void QDialog::reject()
659{
660 done(Rejected);
661}
662
663/*! \reimp */
664bool QDialog::eventFilter(QObject *o, QEvent *e)
665{
666 return QWidget::eventFilter(o, e);
667}
668
669/*****************************************************************************
670 Event handlers
671 *****************************************************************************/
672
673#ifndef QT_NO_CONTEXTMENU
674/*! \reimp */
675void QDialog::contextMenuEvent(QContextMenuEvent *e)
676{
677#if !QT_CONFIG(whatsthis) || !QT_CONFIG(menu)
678 Q_UNUSED(e);
679#else
680 QWidget *w = childAt(e->pos());
681 if (!w) {
682 w = rect().contains(e->pos()) ? this : nullptr;
683 if (!w)
684 return;
685 }
686 while (w && w->whatsThis().size() == 0 && !w->testAttribute(Qt::WA_CustomWhatsThis))
687 w = w->isWindow() ? nullptr : w->parentWidget();
688 if (w) {
689 QPointer<QMenu> p = new QMenu(this);
690 QAction *wt = p.data()->addAction(tr("What's This?"));
691 if (p.data()->exec(e->globalPos()) == wt) {
692 QHelpEvent e(QEvent::WhatsThis, w->rect().center(),
693 w->mapToGlobal(w->rect().center()));
694 QCoreApplication::sendEvent(w, &e);
695 }
696 delete p.data();
697 }
698#endif
699}
700#endif // QT_NO_CONTEXTMENU
701
702/*! \reimp */
703void QDialog::keyPressEvent(QKeyEvent *e)
704{
705#ifndef QT_NO_SHORTCUT
706 // Calls reject() if Escape is pressed. Simulates a button
707 // click for the default button if Enter is pressed. Move focus
708 // for the arrow keys. Ignore the rest.
709 if (e->matches(QKeySequence::Cancel)) {
710 reject();
711 } else
712#endif
713 if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
714 switch (e->key()) {
715#if QT_CONFIG(pushbutton)
716 case Qt::Key_Enter:
717 case Qt::Key_Return: {
718 QList<QPushButton*> list = findChildren<QPushButton*>();
719 for (int i=0; i<list.size(); ++i) {
720 QPushButton *pb = list.at(i);
721 if (pb->isDefault() && pb->isVisible()) {
722 if (pb->isEnabled())
723 pb->click();
724 return;
725 }
726 }
727 }
728 break;
729#endif
730 default:
731 e->ignore();
732 return;
733 }
734 } else {
735 e->ignore();
736 }
737}
738
739/*! \reimp */
740void QDialog::closeEvent(QCloseEvent *e)
741{
742#if QT_CONFIG(whatsthis)
743 if (isModal() && QWhatsThis::inWhatsThisMode())
744 QWhatsThis::leaveWhatsThisMode();
745#endif
746 if (isVisible()) {
747 QPointer<QObject> that = this;
748 reject();
749 if (that && isVisible())
750 e->ignore();
751 } else {
752 e->accept();
753 }
754}
755
756/*****************************************************************************
757 Geometry management.
758 *****************************************************************************/
759
760/*! \reimp
761*/
762
763void QDialog::setVisible(bool visible)
764{
765 QWidget::setVisible(visible);
766}
767
768void QDialogPrivate::setVisible(bool visible)
769{
770 Q_Q(QDialog);
771 if (!q->testAttribute(Qt::WA_DontShowOnScreen) && canBeNativeDialog() && setNativeDialogVisible(visible))
772 return;
773
774 // We should not block windows by the invisible modal dialog
775 // if a platform-specific dialog is implemented as an in-process
776 // Qt window, because in this case it will also be blocked.
777 const bool dontBlockWindows = q->testAttribute(Qt::WA_DontShowOnScreen)
778 && styleHint(QPlatformDialogHelper::DialogIsQtWindow).toBool();
779 Qt::WindowModality oldModality;
780 bool wasModalitySet;
781
782 if (dontBlockWindows) {
783 oldModality = q->windowModality();
784 wasModalitySet = q->testAttribute(Qt::WA_SetWindowModality);
785 q->setWindowModality(Qt::NonModal);
786 }
787
788 if (visible) {
789 QWidgetPrivate::setVisible(visible);
790
791 // Window activation might be prevented. We can't test isActiveWindow here,
792 // as the window will be activated asynchronously by the window manager.
793 if (!q->testAttribute(Qt::WA_ShowWithoutActivating)) {
794 QWidget *fw = q->window()->focusWidget();
795 if (!fw)
796 fw = q;
797
798 /*
799 The following block is to handle a special case, and does not
800 really follow proper logic in concern of autoDefault and TAB
801 order. However, it's here to ease usage for the users. If a
802 dialog has a default QPushButton, and first widget in the TAB
803 order also is a QPushButton, then we give focus to the main
804 default QPushButton. This simplifies code for the developers,
805 and actually catches most cases... If not, then they simply
806 have to use [widget*]->setFocus() themselves...
807 */
808#if QT_CONFIG(pushbutton)
809 if (mainDef && fw->focusPolicy() == Qt::NoFocus) {
810 QWidget *first = fw;
811 while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus)
812 ;
813 if (first != mainDef && qobject_cast<QPushButton*>(first))
814 mainDef->setFocus();
815 }
816 if (!mainDef && q->isWindow()) {
817 QWidget *w = fw;
818 while ((w = w->nextInFocusChain()) != fw) {
819 QPushButton *pb = qobject_cast<QPushButton *>(w);
820 if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) {
821 pb->setDefault(true);
822 break;
823 }
824 }
825 }
826#endif
827 if (fw && !fw->hasFocus()) {
828 QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason);
829 QCoreApplication::sendEvent(fw, &e);
830 }
831 }
832
833#if QT_CONFIG(accessibility)
834 QAccessibleEvent event(q, QAccessible::DialogStart);
835 QAccessible::updateAccessibility(&event);
836#endif
837
838 } else {
839
840#if QT_CONFIG(accessibility)
841 if (q->isVisible()) {
842 QAccessibleEvent event(q, QAccessible::DialogEnd);
843 QAccessible::updateAccessibility(&event);
844 }
845#endif
846
847 // Reimplemented to exit a modal event loop when the dialog is hidden.
848 QWidgetPrivate::setVisible(visible);
849 if (eventLoop)
850 eventLoop->exit();
851 }
852
853 if (dontBlockWindows) {
854 q->setWindowModality(oldModality);
855 q->setAttribute(Qt::WA_SetWindowModality, wasModalitySet);
856 }
857
858#if QT_CONFIG(pushbutton)
859 const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
860 if (mainDef && q->isActiveWindow()
861 && theme->themeHint(QPlatformTheme::DialogSnapToDefaultButton).toBool())
862 QCursor::setPos(mainDef->mapToGlobal(mainDef->rect().center()));
863#endif
864}
865
866/*!\reimp */
867void QDialog::showEvent(QShowEvent *event)
868{
869 if (!event->spontaneous() && !testAttribute(Qt::WA_Moved)) {
870 Qt::WindowStates state = windowState();
871 adjustPosition(parentWidget());
872 setAttribute(Qt::WA_Moved, false); // not really an explicit position
873 if (state != windowState())
874 setWindowState(state);
875 }
876}
877
878/*! \internal */
879void QDialog::adjustPosition(QWidget* w)
880{
881 Q_D(QDialog);
882
883 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
884 if (theme->themeHint(QPlatformTheme::WindowAutoPlacement).toBool())
885 return;
886 QPoint p(0, 0);
887 int extraw = 0, extrah = 0;
888 const QWindow *parentWindow = nullptr;
889 if (w) {
890 w = w->window();
891 } else {
892 parentWindow = d->transientParentWindow();
893 }
894 QRect desk;
895 QScreen *scrn = nullptr;
896 if (w)
897 scrn = w->screen();
898 else if (parentWindow)
899 scrn = parentWindow->screen();
900 else if (QGuiApplication::primaryScreen()->virtualSiblings().size() > 1)
901 scrn = QGuiApplication::screenAt(QCursor::pos());
902 else
903 scrn = screen();
904 if (scrn)
905 desk = scrn->availableGeometry();
906
907 QWidgetList list = QApplication::topLevelWidgets();
908 for (int i = 0; (extraw == 0 || extrah == 0) && i < list.size(); ++i) {
909 QWidget * current = list.at(i);
910 if (current->isVisible()) {
911 int framew = current->geometry().x() - current->x();
912 int frameh = current->geometry().y() - current->y();
913
914 extraw = qMax(extraw, framew);
915 extrah = qMax(extrah, frameh);
916 }
917 }
918
919 // sanity check for decoration frames. With embedding, we
920 // might get extraordinary values
921 if (extraw == 0 || extrah == 0 || extraw >= 10 || extrah >= 40) {
922 extrah = 40;
923 extraw = 10;
924 }
925
926
927 if (w) {
928 // Use pos() if the widget is embedded into a native window
929 QPoint pp;
930 if (w->windowHandle() && qvariant_cast<WId>(w->windowHandle()->property("_q_embedded_native_parent_handle")))
931 pp = w->pos();
932 else
933 pp = w->mapToGlobal(QPoint(0,0));
934 p = QPoint(pp.x() + w->width()/2,
935 pp.y() + w->height()/ 2);
936 } else if (parentWindow) {
937 // QTBUG-63406: Widget-based dialog in QML, which has no Widget parent
938 // but a transient parent window.
939 QPoint pp = parentWindow->mapToGlobal(QPoint(0, 0));
940 p = QPoint(pp.x() + parentWindow->width() / 2, pp.y() + parentWindow->height() / 2);
941 } else {
942 // p = middle of the desktop
943 p = QPoint(desk.x() + desk.width()/2, desk.y() + desk.height()/2);
944 }
945
946 // p = origin of this
947 p = QPoint(p.x()-width()/2 - extraw,
948 p.y()-height()/2 - extrah);
949
950
951 if (p.x() + extraw + width() > desk.x() + desk.width())
952 p.setX(desk.x() + desk.width() - width() - extraw);
953 if (p.x() < desk.x())
954 p.setX(desk.x());
955
956 if (p.y() + extrah + height() > desk.y() + desk.height())
957 p.setY(desk.y() + desk.height() - height() - extrah);
958 if (p.y() < desk.y())
959 p.setY(desk.y());
960
961 // QTBUG-52735: Manually set the correct target screen since scaling in a
962 // subsequent call to QWindow::resize() may otherwise use the wrong factor
963 // if the screen changed notification is still in an event queue.
964 if (scrn) {
965 if (QWindow *window = windowHandle())
966 window->setScreen(scrn);
967 }
968
969 move(p);
970}
971
972/*! \reimp */
973QSize QDialog::sizeHint() const
974{
975 Q_D(const QDialog);
976 if (d->extension) {
977 if (d->orientation == Qt::Horizontal)
978 return QSize(QWidget::sizeHint().width(),
979 qMax(QWidget::sizeHint().height(),d->extension->sizeHint().height()));
980 else
981 return QSize(qMax(QWidget::sizeHint().width(), d->extension->sizeHint().width()),
982 QWidget::sizeHint().height());
983 }
984 return QWidget::sizeHint();
985}
986
987
988/*! \reimp */
989QSize QDialog::minimumSizeHint() const
990{
991 Q_D(const QDialog);
992 if (d->extension) {
993 if (d->orientation == Qt::Horizontal)
994 return QSize(QWidget::minimumSizeHint().width(),
995 qMax(QWidget::minimumSizeHint().height(), d->extension->minimumSizeHint().height()));
996 else
997 return QSize(qMax(QWidget::minimumSizeHint().width(), d->extension->minimumSizeHint().width()),
998 QWidget::minimumSizeHint().height());
999 }
1000
1001 return QWidget::minimumSizeHint();
1002}
1003
1004/*!
1005 \property QDialog::modal
1006 \brief whether show() should pop up the dialog as modal or modeless
1007
1008 By default, this property is \c false and show() pops up the dialog
1009 as modeless. Setting this property to true is equivalent to setting
1010 QWidget::windowModality to Qt::ApplicationModal.
1011
1012 exec() ignores the value of this property and always pops up the
1013 dialog as modal.
1014
1015 \sa QWidget::windowModality, show(), exec()
1016*/
1017
1018void QDialog::setModal(bool modal)
1019{
1020 setAttribute(Qt::WA_ShowModal, modal);
1021}
1022
1023
1024bool QDialog::isSizeGripEnabled() const
1025{
1026#if QT_CONFIG(sizegrip)
1027 Q_D(const QDialog);
1028 return !!d->resizer;
1029#else
1030 return false;
1031#endif
1032}
1033
1034
1035void QDialog::setSizeGripEnabled(bool enabled)
1036{
1037#if !QT_CONFIG(sizegrip)
1038 Q_UNUSED(enabled);
1039#else
1040 Q_D(QDialog);
1041#if QT_CONFIG(sizegrip)
1042 d->sizeGripEnabled = enabled;
1043 if (enabled && d->doShowExtension)
1044 return;
1045#endif
1046 if (!enabled != !d->resizer) {
1047 if (enabled) {
1048 d->resizer = new QSizeGrip(this);
1049 // adjustSize() processes all events, which is suboptimal
1050 d->resizer->resize(d->resizer->sizeHint());
1051 if (isRightToLeft())
1052 d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
1053 else
1054 d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
1055 d->resizer->raise();
1056 d->resizer->show();
1057 } else {
1058 delete d->resizer;
1059 d->resizer = nullptr;
1060 }
1061 }
1062#endif // QT_CONFIG(sizegrip)
1063}
1064
1065
1066
1067/*! \reimp */
1068void QDialog::resizeEvent(QResizeEvent *)
1069{
1070#if QT_CONFIG(sizegrip)
1071 Q_D(QDialog);
1072 if (d->resizer) {
1073 if (isRightToLeft())
1074 d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
1075 else
1076 d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
1077 d->resizer->raise();
1078 }
1079#endif
1080}
1081
1082/*! \fn void QDialog::finished(int result)
1083
1084 This signal is emitted when the dialog's \a result code has been
1085 set, either by the user or by calling done(), accept(), or
1086 reject().
1087
1088 Note that this signal is \e not emitted when hiding the dialog
1089 with hide() or setVisible(false). This includes deleting the
1090 dialog while it is visible.
1091
1092 \sa accepted(), rejected()
1093*/
1094
1095/*! \fn void QDialog::accepted()
1096
1097 This signal is emitted when the dialog has been accepted either by
1098 the user or by calling accept() or done() with the
1099 QDialog::Accepted argument.
1100
1101 Note that this signal is \e not emitted when hiding the dialog
1102 with hide() or setVisible(false). This includes deleting the
1103 dialog while it is visible.
1104
1105 \sa finished(), rejected()
1106*/
1107
1108/*! \fn void QDialog::rejected()
1109
1110 This signal is emitted when the dialog has been rejected either by
1111 the user or by calling reject() or done() with the
1112 QDialog::Rejected argument.
1113
1114 Note that this signal is \e not emitted when hiding the dialog
1115 with hide() or setVisible(false). This includes deleting the
1116 dialog while it is visible.
1117
1118 \sa finished(), accepted()
1119*/
1120
1121QT_END_NAMESPACE
1122#include "moc_qdialog.cpp"
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE int themeDialogType(const QDialog *dialog)
Definition qdialog.cpp:46