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 | 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 | |
366 | QDialog::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 | */ |
376 | QDialog::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 | |
385 | QDialog::~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) |
404 | void 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 | */ |
429 | void 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 | */ |
440 | void 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 | |
450 | void 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 | */ |
475 | int 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 | */ |
489 | void 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 | */ |
501 | void 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 | |
541 | int 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 | |
600 | void 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 | |
626 | void 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 | |
637 | void QDialog::reject() |
638 | { |
639 | done(r: Rejected); |
640 | } |
641 | |
642 | /*! \reimp */ |
643 | bool 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 */ |
654 | void 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 */ |
682 | void 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 */ |
719 | void 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 | |
742 | void 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 | |
752 | void 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 */ |
851 | void 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 */ |
863 | void 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 */ |
957 | QSize 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 */ |
973 | QSize 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 | |
1002 | void QDialog::setModal(bool modal) |
1003 | { |
1004 | setAttribute(Qt::WA_ShowModal, on: modal); |
1005 | } |
1006 | |
1007 | |
1008 | bool 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 | |
1019 | void 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 */ |
1052 | void 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 | |
1105 | QT_END_NAMESPACE |
1106 | #include "moc_qdialog.cpp" |
1107 |
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 Advanced QML with KDAB
Find out more