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 \l open() function. Alternatively, you can call \l setModal(true) or
234 \l setWindowModality(), and then \l show(). In both cases, once the dialog is
235 displayed, the control is immediately returned to the caller. You must connect
236 to the \l finished() signal to know when the dialog is closed and what its
237 \l {#return} {return value} is. Alternatively, you can connect to the
238 \l accepted() and \l rejected() signals.
239
240 When implementing a custom dialog, to close the dialog and return an
241 appropriate value, connect a default button, for example, an OK button, to the
242 \l accept() slot, and a Cancel button to the \l reject() slot. Alternatively,
243 you can call the \l done() slot with \c Accepted or \c Rejected.
244
245 If you show the modal dialog to perform a long-running operation, it is
246 recommended to perform the operation in a background worker thread, so that
247 it does not interfere with the GUI thread.
248
249 \warning When using \l open() or \l show(), the modal dialog should not be
250 created on the stack, so that it does not get destroyed as soon as the control
251 returns to the caller.
252
253 \note There is a way to show a modal dialog in a blocking mode by calling
254 \l exec(). In this case, the control returns to the GUI thread only when the
255 dialog is closed. However, such approach is discouraged, because it creates a
256 nested event loop, which is not fully supported by some platforms.
257
258 \section1 Modeless Dialogs
259
260 A \b{modeless} dialog is a dialog that operates
261 independently of other windows in the same application. Find and
262 replace dialogs in word-processors are often modeless to allow the
263 user to interact with both the application's main window and with
264 the dialog.
265
266 Modeless dialogs are displayed using show(), which returns control
267 to the caller immediately.
268
269 If you invoke the \l{QWidget::show()}{show()} function after hiding
270 a dialog, the dialog will be displayed in its original position. This is
271 because the window manager decides the position for windows that
272 have not been explicitly placed by the programmer. To preserve the
273 position of a dialog that has been moved by the user, save its position
274 in your \l{QWidget::closeEvent()}{closeEvent()} handler and then
275 move the dialog to that position, before showing it again.
276
277 \target default
278 \section1 Default Button
279
280 A dialog's \e default button is the button that's pressed when the
281 user presses Enter (Return). This button is used to signify that
282 the user accepts the dialog's settings and wants to close the
283 dialog. Use QPushButton::setDefault(), QPushButton::isDefault()
284 and QPushButton::autoDefault() to set and control the dialog's
285 default button.
286
287 \target escapekey
288 \section1 Escape Key
289
290 If the user presses the Esc key in a dialog, QDialog::reject()
291 will be called. This will cause the window to close:
292 The \l{QCloseEvent}{close event} cannot be \l{QEvent::ignore()}{ignored}.
293
294 \section1 Extensibility
295
296 Extensibility is the ability to show the dialog in two ways: a
297 partial dialog that shows the most commonly used options, and a
298 full dialog that shows all the options. Typically an extensible
299 dialog will initially appear as a partial dialog, but with a
300 \uicontrol More toggle button. If the user presses the
301 \uicontrol More button down, the dialog is expanded.
302
303 \target return
304 \section1 Return Value (Modal Dialogs)
305
306 Modal dialogs are often used in situations where a return value is
307 required, e.g. to indicate whether the user pressed \uicontrol OK or
308 \uicontrol Cancel. A dialog can be closed by calling the accept() or the
309 reject() slots, and exec() will return \c Accepted or \c Rejected
310 as appropriate. The exec() call returns the result of the dialog.
311 The result is also available from result() if the dialog has not
312 been destroyed.
313
314 In order to modify your dialog's close behavior, you can reimplement
315 the functions accept(), reject() or done(). The
316 \l{QWidget::closeEvent()}{closeEvent()} function should only be
317 reimplemented to preserve the dialog's position or to override the
318 standard close or reject behavior.
319
320 \target examples
321 \section1 Code Examples
322
323 A modal dialog:
324
325 \snippet dialogs/dialogs.cpp 1
326
327 A modeless dialog:
328
329 \snippet dialogs/dialogs.cpp 0
330
331 A dialog with an extension:
332
333 \snippet dialogs/dialogs.cpp extension
334
335 By setting the \l{QLayout::}{sizeConstraint} property of the dialog's
336 layout to \l{QLayout::}{SetFixedSize}, the dialog will not be resizable
337 by the user, and will automatically shrink when the extension gets hidden.
338
339 \sa QDialogButtonBox, QTabWidget, QWidget, QProgressDialog,
340 {Standard Dialogs Example}
341*/
342
343/*! \enum QDialog::DialogCode
344
345 The value returned by a modal dialog.
346
347 \value Accepted
348 \value Rejected
349*/
350
351/*!
352 \property QDialog::sizeGripEnabled
353 \brief whether the size grip is enabled
354
355 A QSizeGrip is placed in the bottom-right corner of the dialog when this
356 property is enabled. By default, the size grip is disabled.
357*/
358
359
360/*!
361 Constructs a dialog with parent \a parent.
362
363 A dialog is always a top-level widget, but if it has a parent, its
364 default location is centered on top of the parent. It will also
365 share the parent's taskbar entry.
366
367 The widget flags \a f are passed on to the QWidget constructor.
368 If, for example, you don't want a What's This button in the title bar
369 of the dialog, pass Qt::WindowTitleHint | Qt::WindowSystemMenuHint in \a f.
370
371 \sa QWidget::setWindowFlags()
372*/
373
374QDialog::QDialog(QWidget *parent, Qt::WindowFlags f)
375 : QWidget(*new QDialogPrivate, parent,
376 f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0)))
377{
378}
379
380/*!
381 \overload
382 \internal
383*/
384QDialog::QDialog(QDialogPrivate &dd, QWidget *parent, Qt::WindowFlags f)
385 : QWidget(dd, parent, f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0)))
386{
387}
388
389/*!
390 Destroys the QDialog, deleting all its children.
391*/
392
393QDialog::~QDialog()
394{
395 QT_TRY {
396 // Need to hide() here, as our (to-be) overridden hide()
397 // will not be called in ~QWidget.
398 hide();
399 } QT_CATCH(...) {
400 // we're in the destructor - just swallow the exception
401 }
402}
403
404/*!
405 \internal
406 This function is called by the push button \a pushButton when it
407 becomes the default button. If \a pushButton is \nullptr, the dialogs
408 default default button becomes the default button. This is what a
409 push button calls when it loses focus.
410*/
411#if QT_CONFIG(pushbutton)
412void QDialogPrivate::setDefault(QPushButton *pushButton)
413{
414 Q_Q(QDialog);
415 bool hasMain = false;
416 QList<QPushButton*> list = q->findChildren<QPushButton*>();
417 for (int i=0; i<list.size(); ++i) {
418 QPushButton *pb = list.at(i);
419 if (pb->window() == q) {
420 if (pb == mainDef)
421 hasMain = true;
422 if (pb != pushButton)
423 pb->setDefault(false);
424 }
425 }
426 if (!pushButton && hasMain)
427 mainDef->setDefault(true);
428 if (!hasMain)
429 mainDef = pushButton;
430}
431
432/*!
433 \internal
434 This function sets the default default push button to \a pushButton.
435 This function is called by QPushButton::setDefault().
436*/
437void QDialogPrivate::setMainDefault(QPushButton *pushButton)
438{
439 mainDef = nullptr;
440 setDefault(pushButton);
441}
442
443/*!
444 \internal
445 Hides the default button indicator. Called when non auto-default
446 push button get focus.
447 */
448void QDialogPrivate::hideDefault()
449{
450 Q_Q(QDialog);
451 QList<QPushButton*> list = q->findChildren<QPushButton*>();
452 for (int i=0; i<list.size(); ++i) {
453 list.at(i)->setDefault(false);
454 }
455}
456#endif
457
458void QDialogPrivate::resetModalitySetByOpen()
459{
460 Q_Q(QDialog);
461 if (resetModalityTo != -1 && !q->testAttribute(attribute: Qt::WA_SetWindowModality)) {
462 // open() changed the window modality and the user didn't touch it afterwards; restore it
463 q->setWindowModality(Qt::WindowModality(resetModalityTo));
464 q->setAttribute(Qt::WA_SetWindowModality, on: wasModalitySet);
465#ifdef Q_OS_MAC
466 Q_ASSERT(resetModalityTo != Qt::WindowModal);
467 q->setParent(q->parentWidget(), Qt::Dialog);
468#endif
469 }
470 resetModalityTo = -1;
471}
472
473/*!
474 In general returns the modal dialog's result code, \c Accepted or
475 \c Rejected.
476
477 \note When called on a QMessageBox instance, the returned value is a
478 value of the \l QMessageBox::StandardButton enum.
479
480 Do not call this function if the dialog was constructed with the
481 Qt::WA_DeleteOnClose attribute.
482*/
483int QDialog::result() const
484{
485 Q_D(const QDialog);
486 return d->rescode;
487}
488
489/*!
490 \fn void QDialog::setResult(int i)
491
492 Sets the modal dialog's result code to \a i.
493
494 \note We recommend that you use one of the values defined by
495 QDialog::DialogCode.
496*/
497void QDialog::setResult(int r)
498{
499 Q_D(QDialog);
500 d->rescode = r;
501}
502
503/*!
504 Shows the dialog as a \l{QDialog#Modal Dialogs}{window modal dialog},
505 returning immediately.
506
507 \sa exec(), show(), result(), setWindowModality()
508*/
509void QDialog::open()
510{
511 Q_D(QDialog);
512
513 Qt::WindowModality modality = windowModality();
514 if (modality != Qt::WindowModal) {
515 d->resetModalityTo = modality;
516 d->wasModalitySet = testAttribute(attribute: Qt::WA_SetWindowModality);
517 setWindowModality(Qt::WindowModal);
518 setAttribute(Qt::WA_SetWindowModality, on: false);
519#ifdef Q_OS_MAC
520 setParent(parentWidget(), Qt::Sheet);
521#endif
522 }
523
524 setResult(0);
525 show();
526}
527
528/*!
529 Shows the dialog as a \l{QDialog#Modal Dialogs}{modal dialog},
530 blocking until the user closes it. The function returns a \l
531 DialogCode result.
532
533 If the dialog is \l{Qt::ApplicationModal}{application modal}, users cannot
534 interact with any other window in the same application until they close
535 the dialog. If the dialog is \l{Qt::ApplicationModal}{window modal}, only
536 interaction with the parent window is blocked while the dialog is open.
537 By default, the dialog is application modal.
538
539 \note Avoid using this function; instead, use \c{open()}. Unlike exec(),
540 open() is asynchronous, and does not spin an additional event loop. This
541 prevents a series of dangerous bugs from happening (e.g. deleting the
542 dialog's parent while the dialog is open via exec()). When using open() you
543 can connect to the finished() signal of QDialog to be notified when the
544 dialog is closed.
545
546 \sa open(), show(), result(), setWindowModality()
547*/
548
549int QDialog::exec()
550{
551 Q_D(QDialog);
552
553 if (Q_UNLIKELY(d->eventLoop)) {
554 qWarning(msg: "QDialog::exec: Recursive call detected");
555 return -1;
556 }
557
558 bool deleteOnClose = testAttribute(attribute: Qt::WA_DeleteOnClose);
559 setAttribute(Qt::WA_DeleteOnClose, on: false);
560
561 d->resetModalitySetByOpen();
562
563 bool wasShowModal = testAttribute(attribute: Qt::WA_ShowModal);
564 setAttribute(Qt::WA_ShowModal, on: true);
565 setResult(0);
566
567 show();
568
569 QPointer<QDialog> guard = this;
570 if (d->nativeDialogInUse) {
571 d->platformHelper()->exec();
572 } else {
573 QEventLoop eventLoop;
574 d->eventLoop = &eventLoop;
575 (void) eventLoop.exec(flags: QEventLoop::DialogExec);
576 }
577 if (guard.isNull())
578 return QDialog::Rejected;
579 d->eventLoop = nullptr;
580
581 setAttribute(Qt::WA_ShowModal, on: wasShowModal);
582
583 int res = result();
584 if (d->nativeDialogInUse)
585 d->helperDone(static_cast<QDialog::DialogCode>(res), d->platformHelper());
586 if (deleteOnClose)
587 delete this;
588 return res;
589}
590
591/*!
592 Closes the dialog and sets its result code to \a r. The finished() signal
593 will emit \a r; if \a r is QDialog::Accepted or QDialog::Rejected, the
594 accepted() or the rejected() signals will also be emitted, respectively.
595
596 If this dialog is shown with exec(), done() also causes the local event loop
597 to finish, and exec() to return \a r.
598
599 As with QWidget::close(), done() deletes the dialog if the
600 Qt::WA_DeleteOnClose flag is set. If the dialog is the application's
601 main widget, the application terminates. If the dialog is the
602 last window closed, the QGuiApplication::lastWindowClosed() signal is
603 emitted.
604
605 \sa accept(), reject(), QApplication::activeWindow(), QCoreApplication::quit()
606*/
607
608void QDialog::done(int r)
609{
610 QPointer<QDialog> guard(this);
611
612 Q_D(QDialog);
613 d->close(resultCode: r);
614
615 if (!guard)
616 return;
617
618 int dialogCode = d->dialogCode();
619 if (dialogCode == QDialog::Accepted)
620 emit accepted();
621 else if (dialogCode == QDialog::Rejected)
622 emit rejected();
623
624 if (guard)
625 emit finished(result: r);
626}
627
628/*!
629 Hides the modal dialog and sets the result code to \c Accepted.
630
631 \sa reject(), done()
632*/
633
634void QDialog::accept()
635{
636 done(r: Accepted);
637}
638
639/*!
640 Hides the modal dialog and sets the result code to \c Rejected.
641
642 \sa accept(), done()
643*/
644
645void QDialog::reject()
646{
647 done(r: Rejected);
648}
649
650/*! \reimp */
651bool QDialog::eventFilter(QObject *o, QEvent *e)
652{
653 return QWidget::eventFilter(watched: o, event: e);
654}
655
656/*****************************************************************************
657 Event handlers
658 *****************************************************************************/
659
660#ifndef QT_NO_CONTEXTMENU
661/*! \reimp */
662void QDialog::contextMenuEvent(QContextMenuEvent *e)
663{
664#if !QT_CONFIG(whatsthis) || !QT_CONFIG(menu)
665 Q_UNUSED(e);
666#else
667 QWidget *w = childAt(p: e->pos());
668 if (!w) {
669 w = rect().contains(p: e->pos()) ? this : nullptr;
670 if (!w)
671 return;
672 }
673 while (w && w->whatsThis().size() == 0 && !w->testAttribute(attribute: Qt::WA_CustomWhatsThis))
674 w = w->isWindow() ? nullptr : w->parentWidget();
675 if (w) {
676 QPointer<QMenu> p = new QMenu(this);
677 QAction *wt = p.data()->addAction(text: tr(s: "What's This?"));
678 if (p.data()->exec(pos: e->globalPos()) == wt) {
679 QHelpEvent e(QEvent::WhatsThis, w->rect().center(),
680 w->mapToGlobal(w->rect().center()));
681 QCoreApplication::sendEvent(receiver: w, event: &e);
682 }
683 delete p.data();
684 }
685#endif
686}
687#endif // QT_NO_CONTEXTMENU
688
689/*! \reimp */
690void QDialog::keyPressEvent(QKeyEvent *e)
691{
692#ifndef QT_NO_SHORTCUT
693 // Calls reject() if Escape is pressed. Simulates a button
694 // click for the default button if Enter is pressed. Move focus
695 // for the arrow keys. Ignore the rest.
696 if (e->matches(key: QKeySequence::Cancel)) {
697 reject();
698 } else
699#endif
700 if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
701 switch (e->key()) {
702#if QT_CONFIG(pushbutton)
703 case Qt::Key_Enter:
704 case Qt::Key_Return: {
705 QList<QPushButton*> list = findChildren<QPushButton*>();
706 for (int i=0; i<list.size(); ++i) {
707 QPushButton *pb = list.at(i);
708 if (pb->isDefault() && pb->isVisible()) {
709 if (pb->isEnabled())
710 pb->click();
711 return;
712 }
713 }
714 }
715 break;
716#endif
717 default:
718 e->ignore();
719 return;
720 }
721 } else {
722 e->ignore();
723 }
724}
725
726/*! \reimp */
727void QDialog::closeEvent(QCloseEvent *e)
728{
729#if QT_CONFIG(whatsthis)
730 if (isModal() && QWhatsThis::inWhatsThisMode())
731 QWhatsThis::leaveWhatsThisMode();
732#endif
733 if (isVisible()) {
734 QPointer<QObject> that = this;
735 reject();
736 if (that && isVisible())
737 e->ignore();
738 } else {
739 e->accept();
740 }
741}
742
743/*****************************************************************************
744 Geometry management.
745 *****************************************************************************/
746
747/*! \reimp
748*/
749
750void QDialog::setVisible(bool visible)
751{
752 Q_D(QDialog);
753
754 if (testAttribute(attribute: Qt::WA_WState_ExplicitShowHide) && testAttribute(attribute: Qt::WA_WState_Hidden) != visible)
755 return;
756
757 d->setVisible(visible);
758}
759
760void QDialogPrivate::setVisible(bool visible)
761{
762 Q_Q(QDialog);
763 if (!q->testAttribute(attribute: Qt::WA_DontShowOnScreen) && canBeNativeDialog() && setNativeDialogVisible(visible))
764 return;
765
766 // We should not block windows by the invisible modal dialog
767 // if a platform-specific dialog is implemented as an in-process
768 // Qt window, because in this case it will also be blocked.
769 const bool dontBlockWindows = q->testAttribute(attribute: Qt::WA_DontShowOnScreen)
770 && styleHint(hint: QPlatformDialogHelper::DialogIsQtWindow).toBool();
771 Qt::WindowModality oldModality;
772 bool wasModalitySet;
773
774 if (dontBlockWindows) {
775 oldModality = q->windowModality();
776 wasModalitySet = q->testAttribute(attribute: Qt::WA_SetWindowModality);
777 q->setWindowModality(Qt::NonModal);
778 }
779
780 if (visible) {
781 QWidgetPrivate::setVisible(visible);
782
783 // Window activation might be prevented. We can't test isActiveWindow here,
784 // as the window will be activated asynchronously by the window manager.
785 if (!q->testAttribute(attribute: Qt::WA_ShowWithoutActivating)) {
786 QWidget *fw = q->window()->focusWidget();
787 if (!fw)
788 fw = q;
789
790 /*
791 The following block is to handle a special case, and does not
792 really follow proper logic in concern of autoDefault and TAB
793 order. However, it's here to ease usage for the users. If a
794 dialog has a default QPushButton, and first widget in the TAB
795 order also is a QPushButton, then we give focus to the main
796 default QPushButton. This simplifies code for the developers,
797 and actually catches most cases... If not, then they simply
798 have to use [widget*]->setFocus() themselves...
799 */
800#if QT_CONFIG(pushbutton)
801 if (mainDef && fw->focusPolicy() == Qt::NoFocus) {
802 QWidget *first = fw;
803 while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus)
804 ;
805 if (first != mainDef && qobject_cast<QPushButton*>(object: first))
806 mainDef->setFocus();
807 }
808 if (!mainDef && q->isWindow()) {
809 QWidget *w = fw;
810 while ((w = w->nextInFocusChain()) != fw) {
811 QPushButton *pb = qobject_cast<QPushButton *>(object: w);
812 if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) {
813 pb->setDefault(true);
814 break;
815 }
816 }
817 }
818#endif
819 if (fw && !fw->hasFocus()) {
820 QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason);
821 QCoreApplication::sendEvent(receiver: fw, event: &e);
822 }
823 }
824
825#if QT_CONFIG(accessibility)
826 QAccessibleEvent event(q, QAccessible::DialogStart);
827 QAccessible::updateAccessibility(event: &event);
828#endif
829
830 } else {
831
832#if QT_CONFIG(accessibility)
833 if (q->isVisible()) {
834 QAccessibleEvent event(q, QAccessible::DialogEnd);
835 QAccessible::updateAccessibility(event: &event);
836 }
837#endif
838
839 // Reimplemented to exit a modal event loop when the dialog is hidden.
840 QWidgetPrivate::setVisible(visible);
841 if (eventLoop)
842 eventLoop->exit();
843 }
844
845 if (dontBlockWindows) {
846 q->setWindowModality(oldModality);
847 q->setAttribute(Qt::WA_SetWindowModality, on: wasModalitySet);
848 }
849
850#if QT_CONFIG(pushbutton)
851 const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
852 if (mainDef && q->isActiveWindow()
853 && theme->themeHint(hint: QPlatformTheme::DialogSnapToDefaultButton).toBool())
854 QCursor::setPos(mainDef->mapToGlobal(mainDef->rect().center()));
855#endif
856}
857
858/*!\reimp */
859void QDialog::showEvent(QShowEvent *event)
860{
861 if (!event->spontaneous() && !testAttribute(attribute: Qt::WA_Moved)) {
862 Qt::WindowStates state = windowState();
863 adjustPosition(parentWidget());
864 setAttribute(Qt::WA_Moved, on: false); // not really an explicit position
865 if (state != windowState())
866 setWindowState(state);
867 }
868}
869
870/*! \internal */
871void QDialog::adjustPosition(QWidget* w)
872{
873 Q_D(QDialog);
874
875 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
876 if (theme->themeHint(hint: QPlatformTheme::WindowAutoPlacement).toBool())
877 return;
878 QPoint p(0, 0);
879 int extraw = 0, extrah = 0;
880 const QWindow *parentWindow = nullptr;
881 if (w) {
882 w = w->window();
883 } else {
884 parentWindow = d->transientParentWindow();
885 }
886 QRect desk;
887 QScreen *scrn = nullptr;
888 if (w)
889 scrn = w->screen();
890 else if (parentWindow)
891 scrn = parentWindow->screen();
892 else if (QGuiApplication::primaryScreen()->virtualSiblings().size() > 1)
893 scrn = QGuiApplication::screenAt(point: QCursor::pos());
894 else
895 scrn = screen();
896 if (scrn)
897 desk = scrn->availableGeometry();
898
899 QWidgetList list = QApplication::topLevelWidgets();
900 for (int i = 0; (extraw == 0 || extrah == 0) && i < list.size(); ++i) {
901 QWidget * current = list.at(i);
902 if (current->isVisible()) {
903 int framew = current->geometry().x() - current->x();
904 int frameh = current->geometry().y() - current->y();
905
906 extraw = qMax(a: extraw, b: framew);
907 extrah = qMax(a: extrah, b: frameh);
908 }
909 }
910
911 // sanity check for decoration frames. With embedding, we
912 // might get extraordinary values
913 if (extraw == 0 || extrah == 0 || extraw >= 10 || extrah >= 40) {
914 extrah = 40;
915 extraw = 10;
916 }
917
918
919 if (w) {
920 // Use pos() if the widget is embedded into a native window
921 QPoint pp;
922 if (w->windowHandle() && qvariant_cast<WId>(v: w->windowHandle()->property(name: "_q_embedded_native_parent_handle")))
923 pp = w->pos();
924 else
925 pp = w->mapToGlobal(QPoint(0,0));
926 p = QPoint(pp.x() + w->width()/2,
927 pp.y() + w->height()/ 2);
928 } else if (parentWindow) {
929 // QTBUG-63406: Widget-based dialog in QML, which has no Widget parent
930 // but a transient parent window.
931 QPoint pp = parentWindow->mapToGlobal(pos: QPoint(0, 0));
932 p = QPoint(pp.x() + parentWindow->width() / 2, pp.y() + parentWindow->height() / 2);
933 } else {
934 // p = middle of the desktop
935 p = QPoint(desk.x() + desk.width()/2, desk.y() + desk.height()/2);
936 }
937
938 // p = origin of this
939 p = QPoint(p.x()-width()/2 - extraw,
940 p.y()-height()/2 - extrah);
941
942
943 if (p.x() + extraw + width() > desk.x() + desk.width())
944 p.setX(desk.x() + desk.width() - width() - extraw);
945 if (p.x() < desk.x())
946 p.setX(desk.x());
947
948 if (p.y() + extrah + height() > desk.y() + desk.height())
949 p.setY(desk.y() + desk.height() - height() - extrah);
950 if (p.y() < desk.y())
951 p.setY(desk.y());
952
953 // QTBUG-52735: Manually set the correct target screen since scaling in a
954 // subsequent call to QWindow::resize() may otherwise use the wrong factor
955 // if the screen changed notification is still in an event queue.
956 if (scrn) {
957 if (QWindow *window = windowHandle())
958 window->setScreen(scrn);
959 }
960
961 move(p);
962}
963
964/*! \reimp */
965QSize QDialog::sizeHint() const
966{
967 Q_D(const QDialog);
968 if (d->extension) {
969 if (d->orientation == Qt::Horizontal)
970 return QSize(QWidget::sizeHint().width(),
971 qMax(a: QWidget::sizeHint().height(),b: d->extension->sizeHint().height()));
972 else
973 return QSize(qMax(a: QWidget::sizeHint().width(), b: d->extension->sizeHint().width()),
974 QWidget::sizeHint().height());
975 }
976 return QWidget::sizeHint();
977}
978
979
980/*! \reimp */
981QSize QDialog::minimumSizeHint() const
982{
983 Q_D(const QDialog);
984 if (d->extension) {
985 if (d->orientation == Qt::Horizontal)
986 return QSize(QWidget::minimumSizeHint().width(),
987 qMax(a: QWidget::minimumSizeHint().height(), b: d->extension->minimumSizeHint().height()));
988 else
989 return QSize(qMax(a: QWidget::minimumSizeHint().width(), b: d->extension->minimumSizeHint().width()),
990 QWidget::minimumSizeHint().height());
991 }
992
993 return QWidget::minimumSizeHint();
994}
995
996/*!
997 \property QDialog::modal
998 \brief whether show() should pop up the dialog as modal or modeless
999
1000 By default, this property is \c false and show() pops up the dialog
1001 as modeless. Setting this property to true is equivalent to setting
1002 QWidget::windowModality to Qt::ApplicationModal.
1003
1004 exec() ignores the value of this property and always pops up the
1005 dialog as modal.
1006
1007 \sa QWidget::windowModality, show(), exec()
1008*/
1009
1010void QDialog::setModal(bool modal)
1011{
1012 setAttribute(Qt::WA_ShowModal, on: modal);
1013}
1014
1015
1016bool QDialog::isSizeGripEnabled() const
1017{
1018#if QT_CONFIG(sizegrip)
1019 Q_D(const QDialog);
1020 return !!d->resizer;
1021#else
1022 return false;
1023#endif
1024}
1025
1026
1027void QDialog::setSizeGripEnabled(bool enabled)
1028{
1029#if !QT_CONFIG(sizegrip)
1030 Q_UNUSED(enabled);
1031#else
1032 Q_D(QDialog);
1033#if QT_CONFIG(sizegrip)
1034 d->sizeGripEnabled = enabled;
1035 if (enabled && d->doShowExtension)
1036 return;
1037#endif
1038 if (!enabled != !d->resizer) {
1039 if (enabled) {
1040 d->resizer = new QSizeGrip(this);
1041 // adjustSize() processes all events, which is suboptimal
1042 d->resizer->resize(d->resizer->sizeHint());
1043 if (isRightToLeft())
1044 d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
1045 else
1046 d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
1047 d->resizer->raise();
1048 d->resizer->show();
1049 } else {
1050 delete d->resizer;
1051 d->resizer = nullptr;
1052 }
1053 }
1054#endif // QT_CONFIG(sizegrip)
1055}
1056
1057
1058
1059/*! \reimp */
1060void QDialog::resizeEvent(QResizeEvent *)
1061{
1062#if QT_CONFIG(sizegrip)
1063 Q_D(QDialog);
1064 if (d->resizer) {
1065 if (isRightToLeft())
1066 d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
1067 else
1068 d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
1069 d->resizer->raise();
1070 }
1071#endif
1072}
1073
1074/*! \fn void QDialog::finished(int result)
1075
1076 This signal is emitted when the dialog's \a result code has been
1077 set, either by the user or by calling done(), accept(), or
1078 reject().
1079
1080 Note that this signal is \e not emitted when hiding the dialog
1081 with hide() or setVisible(false). This includes deleting the
1082 dialog while it is visible.
1083
1084 \sa accepted(), rejected()
1085*/
1086
1087/*! \fn void QDialog::accepted()
1088
1089 This signal is emitted when the dialog has been accepted either by
1090 the user or by calling accept() or done() with the
1091 QDialog::Accepted argument.
1092
1093 Note that this signal is \e not emitted when hiding the dialog
1094 with hide() or setVisible(false). This includes deleting the
1095 dialog while it is visible.
1096
1097 \sa finished(), rejected()
1098*/
1099
1100/*! \fn void QDialog::rejected()
1101
1102 This signal is emitted when the dialog has been rejected either by
1103 the user or by calling reject() or done() with the
1104 QDialog::Rejected argument.
1105
1106 Note that this signal is \e not emitted when hiding the dialog
1107 with hide() or setVisible(false). This includes deleting the
1108 dialog while it is visible.
1109
1110 \sa finished(), accepted()
1111*/
1112
1113QT_END_NAMESPACE
1114#include "moc_qdialog.cpp"
1115

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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