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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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