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

source code of qtbase/src/widgets/dialogs/qdialog.cpp