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 | |
41 | QT_BEGIN_NAMESPACE |
42 | |
43 | static 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 | |
72 | QDialogPrivate::~QDialogPrivate() |
73 | { |
74 | delete m_platformHelper; |
75 | } |
76 | |
77 | QPlatformDialogHelper *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 | |
99 | bool 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 | */ |
118 | void 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 | |
152 | QWindow *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 | |
162 | bool 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 | |
176 | QVariant 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 | |
374 | QDialog::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 | */ |
384 | QDialog::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 | |
393 | QDialog::~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) |
412 | void 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 | */ |
437 | void 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 | */ |
448 | void 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 | |
458 | void 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 | */ |
483 | int 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 | */ |
497 | void 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 | */ |
509 | void 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 | |
549 | int 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 | |
608 | void 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 | |
634 | void 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 | |
645 | void QDialog::reject() |
646 | { |
647 | done(r: Rejected); |
648 | } |
649 | |
650 | /*! \reimp */ |
651 | bool 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 */ |
662 | void 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 */ |
690 | void 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 */ |
727 | void 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 | |
750 | void 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 | |
760 | void 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 */ |
859 | void 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 */ |
871 | void 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 */ |
965 | QSize 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 */ |
981 | QSize 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 | |
1010 | void QDialog::setModal(bool modal) |
1011 | { |
1012 | setAttribute(Qt::WA_ShowModal, on: modal); |
1013 | } |
1014 | |
1015 | |
1016 | bool 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 | |
1027 | void 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 */ |
1060 | void 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 | |
1113 | QT_END_NAMESPACE |
1114 | #include "moc_qdialog.cpp" |
1115 |
Definitions
- themeDialogType
- ~QDialogPrivate
- platformHelper
- canBeNativeDialog
- close
- transientParentWindow
- setNativeDialogVisible
- styleHint
- QDialog
- QDialog
- ~QDialog
- setDefault
- setMainDefault
- hideDefault
- resetModalitySetByOpen
- result
- setResult
- open
- exec
- done
- accept
- reject
- eventFilter
- contextMenuEvent
- keyPressEvent
- closeEvent
- setVisible
- setVisible
- showEvent
- adjustPosition
- sizeHint
- minimumSizeHint
- setModal
- isSizeGripEnabled
- setSizeGripEnabled
Learn to use CMake with our Intro Training
Find out more