diff options
Diffstat (limited to 'src/widgets/dialogs/qdialog.cpp')
-rw-r--r-- | src/widgets/dialogs/qdialog.cpp | 451 |
1 files changed, 151 insertions, 300 deletions
diff --git a/src/widgets/dialogs/qdialog.cpp b/src/widgets/dialogs/qdialog.cpp index 906022a185..27466d03d6 100644 --- a/src/widgets/dialogs/qdialog.cpp +++ b/src/widgets/dialogs/qdialog.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtWidgets/qtwidgetsglobal.h> #if QT_CONFIG(colordialog) @@ -49,8 +13,6 @@ #endif #include "qevent.h" -#include "qdesktopwidget.h" -#include <private/qdesktopwidget_p.h> #include "qapplication.h" #include "qlayout.h" #if QT_CONFIG(sizegrip) @@ -72,7 +34,7 @@ #include <qpa/qplatformtheme.h> #include "private/qdialog_p.h" #include "private/qguiapplication_p.h" -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) #include "qaccessible.h" #endif @@ -136,6 +98,9 @@ QPlatformDialogHelper *QDialogPrivate::platformHelper() const bool QDialogPrivate::canBeNativeDialog() const { + if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)) + return false; + QDialogPrivate *ncThis = const_cast<QDialogPrivate *>(this); QDialog *dialog = ncThis->q_func(); const int type = themeDialogType(dialog); @@ -148,36 +113,40 @@ bool QDialogPrivate::canBeNativeDialog() const /*! \internal - Properly hides dialog and sets the \a resultCode. + Properly closes dialog and sets the \a resultCode. */ -void QDialogPrivate::hide(int resultCode) +void QDialogPrivate::close(int resultCode) { Q_Q(QDialog); q->setResult(resultCode); - q->hide(); - - close_helper(QWidgetPrivate::CloseNoEvent); - resetModalitySetByOpen(); -} -/*! - \internal - - Emits finished() signal with \a resultCode. If the \a dialogCode - is equal to 0 emits rejected(), if the \a dialogCode is equal to - 1 emits accepted(). - */ -void QDialogPrivate::finalize(int resultCode, int dialogCode) -{ - Q_Q(QDialog); - - if (dialogCode == QDialog::Accepted) - emit q->accepted(); - else if (dialogCode == QDialog::Rejected) - emit q->rejected(); + if (!data.is_closing) { + // Until Qt 6.3 we didn't close dialogs, so they didn't receive a QCloseEvent. + // It is likely that subclasses implement closeEvent and handle them as rejection + // (like QMessageBox and QProgressDialog do), so eat those events. + struct CloseEventEater : QObject + { + using QObject::QObject; + protected: + bool eventFilter(QObject *o, QEvent *e) override + { + if (e->type() == QEvent::Close) + return true; + return QObject::eventFilter(o, e); + } + } closeEventEater; + q->installEventFilter(&closeEventEater); + QWidgetPrivate::close(); + } else { + // If the close was initiated outside of QDialog we will end up + // here via QDialog::closeEvent calling reject(), in which case + // we need to hide the dialog to ensure QDialog::closeEvent does + // not ignore the close event. FIXME: Why is QDialog doing this? + q->hide(); + } - emit q->finished(resultCode); + resetModalitySetByOpen(); } QWindow *QDialogPrivate::transientParentWindow() const @@ -211,14 +180,6 @@ QVariant QDialogPrivate::styleHint(QPlatformDialogHelper::StyleHint hint) const return QPlatformDialogHelper::defaultStyleHint(hint); } -void QDialogPrivate::deletePlatformHelper() -{ - delete m_platformHelper; - m_platformHelper = nullptr; - m_platformHelperCreated = false; - nativeDialogInUse = false; -} - /*! \class QDialog \brief The QDialog class is the base class of dialog windows. @@ -319,7 +280,8 @@ void QDialogPrivate::deletePlatformHelper() \section1 Escape Key If the user presses the Esc key in a dialog, QDialog::reject() - will be called. This will cause the window to close: The \l{QCloseEvent}{close event} cannot be \l{QEvent::ignore()}{ignored}. + will be called. This will cause the window to close: + The \l{QCloseEvent}{close event} cannot be \l{QEvent::ignore()}{ignored}. \section1 Extensibility @@ -327,9 +289,8 @@ void QDialogPrivate::deletePlatformHelper() partial dialog that shows the most commonly used options, and a full dialog that shows all the options. Typically an extensible dialog will initially appear as a partial dialog, but with a - \uicontrol More toggle button. If the user presses the \uicontrol More button down, - the dialog is expanded. The \l{Extension Example} shows how to achieve - extensible dialogs using Qt. + \uicontrol More toggle button. If the user presses the + \uicontrol More button down, the dialog is expanded. \target return \section1 Return Value (Modal Dialogs) @@ -359,8 +320,15 @@ void QDialogPrivate::deletePlatformHelper() \snippet dialogs/dialogs.cpp 0 + A dialog with an extension: + + \snippet dialogs/dialogs.cpp extension + + By setting the \l{QLayout::}{sizeConstraint} property of the dialog's + layout to \l{QLayout::}{SetFixedSize}, the dialog will not be resizable + by the user, and will automatically shrink when the extension gets hidden. + \sa QDialogButtonBox, QTabWidget, QWidget, QProgressDialog, - {fowler}{GUI Design Handbook: Dialogs, Standard}, {Extension Example}, {Standard Dialogs Example} */ @@ -625,7 +593,7 @@ int QDialog::exec() As with QWidget::close(), done() deletes the dialog if the Qt::WA_DeleteOnClose flag is set. If the dialog is the application's main widget, the application terminates. If the dialog is the - last window closed, the QApplication::lastWindowClosed() signal is + last window closed, the QGuiApplication::lastWindowClosed() signal is emitted. \sa accept(), reject(), QApplication::activeWindow(), QCoreApplication::quit() @@ -633,9 +601,22 @@ int QDialog::exec() void QDialog::done(int r) { + QPointer<QDialog> guard(this); + Q_D(QDialog); - d->hide(r); - d->finalize(r, r); + d->close(r); + + if (!guard) + return; + + int dialogCode = d->dialogCode(); + if (dialogCode == QDialog::Accepted) + emit accepted(); + else if (dialogCode == QDialog::Rejected) + emit rejected(); + + if (guard) + emit finished(r); } /*! @@ -763,104 +744,108 @@ void QDialog::closeEvent(QCloseEvent *e) void QDialog::setVisible(bool visible) { Q_D(QDialog); - if (!testAttribute(Qt::WA_DontShowOnScreen) && d->canBeNativeDialog() && d->setNativeDialogVisible(visible)) + + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden) != visible) + return; + + d->setVisible(visible); +} + +void QDialogPrivate::setVisible(bool visible) +{ + Q_Q(QDialog); + if (!q->testAttribute(Qt::WA_DontShowOnScreen) && canBeNativeDialog() && setNativeDialogVisible(visible)) return; // We should not block windows by the invisible modal dialog // if a platform-specific dialog is implemented as an in-process // Qt window, because in this case it will also be blocked. - const bool dontBlockWindows = testAttribute(Qt::WA_DontShowOnScreen) - && d->styleHint(QPlatformDialogHelper::DialogIsQtWindow).toBool(); + const bool dontBlockWindows = q->testAttribute(Qt::WA_DontShowOnScreen) + && styleHint(QPlatformDialogHelper::DialogIsQtWindow).toBool(); Qt::WindowModality oldModality; bool wasModalitySet; if (dontBlockWindows) { - oldModality = windowModality(); - wasModalitySet = testAttribute(Qt::WA_SetWindowModality); - setWindowModality(Qt::NonModal); + oldModality = q->windowModality(); + wasModalitySet = q->testAttribute(Qt::WA_SetWindowModality); + q->setWindowModality(Qt::NonModal); } if (visible) { - if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) - return; - - QWidget::setVisible(visible); -#if QT_DEPRECATED_SINCE(5, 13) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - showExtension(d->doShowExtension); -QT_WARNING_POP -#endif - QWidget *fw = window()->focusWidget(); - if (!fw) - fw = this; - - /* - The following block is to handle a special case, and does not - really follow propper logic in concern of autoDefault and TAB - order. However, it's here to ease usage for the users. If a - dialog has a default QPushButton, and first widget in the TAB - order also is a QPushButton, then we give focus to the main - default QPushButton. This simplifies code for the developers, - and actually catches most cases... If not, then they simply - have to use [widget*]->setFocus() themselves... - */ + q->QWidget::setVisible(visible); + + // Window activation might be prevented. We can't test isActiveWindow here, + // as the window will be activated asynchronously by the window manager. + if (!q->testAttribute(Qt::WA_ShowWithoutActivating)) { + QWidget *fw = q->window()->focusWidget(); + if (!fw) + fw = q; + + /* + The following block is to handle a special case, and does not + really follow proper logic in concern of autoDefault and TAB + order. However, it's here to ease usage for the users. If a + dialog has a default QPushButton, and first widget in the TAB + order also is a QPushButton, then we give focus to the main + default QPushButton. This simplifies code for the developers, + and actually catches most cases... If not, then they simply + have to use [widget*]->setFocus() themselves... + */ #if QT_CONFIG(pushbutton) - if (d->mainDef && fw->focusPolicy() == Qt::NoFocus) { - QWidget *first = fw; - while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus) - ; - if (first != d->mainDef && qobject_cast<QPushButton*>(first)) - d->mainDef->setFocus(); - } - if (!d->mainDef && isWindow()) { - QWidget *w = fw; - while ((w = w->nextInFocusChain()) != fw) { - QPushButton *pb = qobject_cast<QPushButton *>(w); - if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) { - pb->setDefault(true); - break; + if (mainDef && fw->focusPolicy() == Qt::NoFocus) { + QWidget *first = fw; + while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus) + ; + if (first != mainDef && qobject_cast<QPushButton*>(first)) + mainDef->setFocus(); + } + if (!mainDef && q->isWindow()) { + QWidget *w = fw; + while ((w = w->nextInFocusChain()) != fw) { + QPushButton *pb = qobject_cast<QPushButton *>(w); + if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) { + pb->setDefault(true); + break; + } } } - } #endif - if (fw && !fw->hasFocus()) { - QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason); - QCoreApplication::sendEvent(fw, &e); + if (fw && !fw->hasFocus()) { + QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason); + QCoreApplication::sendEvent(fw, &e); + } } -#ifndef QT_NO_ACCESSIBILITY - QAccessibleEvent event(this, QAccessible::DialogStart); +#if QT_CONFIG(accessibility) + QAccessibleEvent event(q, QAccessible::DialogStart); QAccessible::updateAccessibility(&event); #endif } else { - if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) - return; -#ifndef QT_NO_ACCESSIBILITY - if (isVisible()) { - QAccessibleEvent event(this, QAccessible::DialogEnd); +#if QT_CONFIG(accessibility) + if (q->isVisible()) { + QAccessibleEvent event(q, QAccessible::DialogEnd); QAccessible::updateAccessibility(&event); } #endif // Reimplemented to exit a modal event loop when the dialog is hidden. - QWidget::setVisible(visible); - if (d->eventLoop) - d->eventLoop->exit(); + q->QWidget::setVisible(visible); + if (eventLoop) + eventLoop->exit(); } if (dontBlockWindows) { - setWindowModality(oldModality); - setAttribute(Qt::WA_SetWindowModality, wasModalitySet); + q->setWindowModality(oldModality); + q->setAttribute(Qt::WA_SetWindowModality, wasModalitySet); } #if QT_CONFIG(pushbutton) const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); - if (d->mainDef && isActiveWindow() + if (mainDef && q->isActiveWindow() && theme->themeHint(QPlatformTheme::DialogSnapToDefaultButton).toBool()) - QCursor::setPos(d->mainDef->mapToGlobal(d->mainDef->rect().center())); + QCursor::setPos(mainDef->mapToGlobal(mainDef->rect().center())); #endif } @@ -879,23 +864,31 @@ void QDialog::showEvent(QShowEvent *event) /*! \internal */ void QDialog::adjustPosition(QWidget* w) { + Q_D(QDialog); if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) if (theme->themeHint(QPlatformTheme::WindowAutoPlacement).toBool()) return; QPoint p(0, 0); - int extraw = 0, extrah = 0, scrn = 0; - if (w) - w = w->window(); - QRect desk; + int extraw = 0, extrah = 0; + const QWindow *parentWindow = nullptr; if (w) { - scrn = QDesktopWidgetPrivate::screenNumber(w); - } else if (QDesktopWidgetPrivate::isVirtualDesktop()) { - scrn = QDesktopWidgetPrivate::screenNumber(QCursor::pos()); + w = w->window(); } else { - scrn = QDesktopWidgetPrivate::screenNumber(this); + parentWindow = d->transientParentWindow(); } - desk = QDesktopWidgetPrivate::availableGeometry(scrn); + QRect desk; + QScreen *scrn = nullptr; + if (w) + scrn = w->screen(); + else if (parentWindow) + scrn = parentWindow->screen(); + else if (QGuiApplication::primaryScreen()->virtualSiblings().size() > 1) + scrn = QGuiApplication::screenAt(QCursor::pos()); + else + scrn = screen(); + if (scrn) + desk = scrn->availableGeometry(); QWidgetList list = QApplication::topLevelWidgets(); for (int i = 0; (extraw == 0 || extrah == 0) && i < list.size(); ++i) { @@ -926,6 +919,11 @@ void QDialog::adjustPosition(QWidget* w) pp = w->mapToGlobal(QPoint(0,0)); p = QPoint(pp.x() + w->width()/2, pp.y() + w->height()/ 2); + } else if (parentWindow) { + // QTBUG-63406: Widget-based dialog in QML, which has no Widget parent + // but a transient parent window. + QPoint pp = parentWindow->mapToGlobal(QPoint(0, 0)); + p = QPoint(pp.x() + parentWindow->width() / 2, pp.y() + parentWindow->height() / 2); } else { // p = middle of the desktop p = QPoint(desk.x() + desk.width()/2, desk.y() + desk.height()/2); @@ -949,161 +947,14 @@ void QDialog::adjustPosition(QWidget* w) // QTBUG-52735: Manually set the correct target screen since scaling in a // subsequent call to QWindow::resize() may otherwise use the wrong factor // if the screen changed notification is still in an event queue. - if (scrn >= 0) { + if (scrn) { if (QWindow *window = windowHandle()) - window->setScreen(QGuiApplication::screens().at(scrn)); + window->setScreen(scrn); } move(p); } -#if QT_DEPRECATED_SINCE(5, 13) -/*! - \obsolete - - If \a orientation is Qt::Horizontal, the extension will be displayed - to the right of the dialog's main area. If \a orientation is - Qt::Vertical, the extension will be displayed below the dialog's main - area. - - Instead of using this functionality, we recommend that you simply call - show() or hide() on the part of the dialog that you want to use as an - extension. See the \l{Extension Example} for details. - - \sa setExtension() -*/ -void QDialog::setOrientation(Qt::Orientation orientation) -{ - Q_D(QDialog); - d->orientation = orientation; -} - -/*! - \obsolete - - Returns the dialog's extension orientation. - - Instead of using this functionality, we recommend that you simply call - show() or hide() on the part of the dialog that you want to use as an - extension. See the \l{Extension Example} for details. - - \sa extension() -*/ -Qt::Orientation QDialog::orientation() const -{ - Q_D(const QDialog); - return d->orientation; -} - -/*! - \obsolete - - Sets the widget, \a extension, to be the dialog's extension, - deleting any previous extension. The dialog takes ownership of the - extension. Note that if \nullptr is passed, any existing extension will be - deleted. This function must only be called while the dialog is hidden. - - Instead of using this functionality, we recommend that you simply call - show() or hide() on the part of the dialog that you want to use as an - extension. See the \l{Extension Example} for details. - - \sa showExtension(), setOrientation() -*/ -void QDialog::setExtension(QWidget* extension) -{ - Q_D(QDialog); - delete d->extension; - d->extension = extension; - - if (!extension) - return; - - if (extension->parentWidget() != this) - extension->setParent(this); - extension->hide(); -} - -/*! - \obsolete - - Returns the dialog's extension or \nullptr if no extension has been - defined. - - Instead of using this functionality, we recommend that you simply call - show() or hide() on the part of the dialog that you want to use as an - extension. See the \l{Extension Example} for details. - - \sa showExtension(), setOrientation() -*/ -QWidget* QDialog::extension() const -{ - Q_D(const QDialog); - return d->extension; -} - - -/*! - \obsolete - - If \a showIt is true, the dialog's extension is shown; otherwise the - extension is hidden. - - Instead of using this functionality, we recommend that you simply call - show() or hide() on the part of the dialog that you want to use as an - extension. See the \l{Extension Example} for details. - - \sa show(), setExtension(), setOrientation() -*/ -void QDialog::showExtension(bool showIt) -{ - Q_D(QDialog); - d->doShowExtension = showIt; - if (!d->extension) - return; - if (!testAttribute(Qt::WA_WState_Visible)) - return; - if (d->extension->isVisible() == showIt) - return; - - if (showIt) { - d->size = size(); - d->min = minimumSize(); - d->max = maximumSize(); - if (layout()) - layout()->setEnabled(false); - QSize s(d->extension->sizeHint() - .expandedTo(d->extension->minimumSize()) - .boundedTo(d->extension->maximumSize())); - if (d->orientation == Qt::Horizontal) { - int h = qMax(height(), s.height()); - d->extension->setGeometry(width(), 0, s.width(), h); - setFixedSize(width() + s.width(), h); - } else { - int w = qMax(width(), s.width()); - d->extension->setGeometry(0, height(), w, s.height()); - setFixedSize(w, height() + s.height()); - } - d->extension->show(); -#if QT_CONFIG(sizegrip) - const bool sizeGripEnabled = isSizeGripEnabled(); - setSizeGripEnabled(false); - d->sizeGripEnabled = sizeGripEnabled; -#endif - } else { - d->extension->hide(); - // workaround for CDE window manager that won't shrink with (-1,-1) - setMinimumSize(d->min.expandedTo(QSize(1, 1))); - setMaximumSize(d->max); - resize(d->size); - if (layout()) - layout()->setEnabled(true); -#if QT_CONFIG(sizegrip) - setSizeGripEnabled(d->sizeGripEnabled); -#endif - } -} -#endif - /*! \reimp */ QSize QDialog::sizeHint() const { |