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