diff options
Diffstat (limited to 'src/widgets/kernel/qapplication.cpp')
-rw-r--r-- | src/widgets/kernel/qapplication.cpp | 1799 |
1 files changed, 678 insertions, 1121 deletions
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 129569a466..5e1d792319 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1,48 +1,11 @@ -/**************************************************************************** -** -** 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 "qplatformdefs.h" #include "qabstracteventdispatcher.h" #include "qapplication.h" #include "qclipboard.h" #include "qcursor.h" -#include "qdesktopwidget.h" #include "qdir.h" #include "qevent.h" #include "qfile.h" @@ -58,7 +21,9 @@ #include "qstyle.h" #include "qstyleoption.h" #include "qstylefactory.h" +#if QT_CONFIG(tooltip) #include "qtooltip.h" +#endif #include "qtranslator.h" #include "qvariant.h" #include "qwidget.h" @@ -77,45 +42,51 @@ #include <QtGui/qstylehints.h> #include <QtGui/qinputmethod.h> #include <QtGui/private/qwindow_p.h> -#include <QtGui/qtouchdevice.h> +#include <QtGui/qpointingdevice.h> +#include <QtGui/private/qpointingdevice_p.h> #include <qpa/qplatformtheme.h> #if QT_CONFIG(whatsthis) #include <QtWidgets/QWhatsThis> #endif +#if QT_CONFIG(accessibility) +#include <QtGui/qaccessible_base.h> +#include "private/qaccessiblewidgetfactory_p.h" +#endif #include "private/qkeymapper_p.h" -#include "private/qaccessiblewidgetfactory_p.h" #include <qthread.h> #include <private/qthread_p.h> +#include <QtGui/private/qevent_p.h> +#include <QtGui/private/qeventpoint_p.h> #include <private/qfont_p.h> +#if QT_CONFIG(action) +#include <private/qaction_p.h> +#endif #include <stdlib.h> #include "qapplication_p.h" -#include "private/qevent_p.h" #include "qwidget_p.h" #include "qgesture.h" #include "private/qgesturemanager_p.h" #include <qpa/qplatformfontdatabase.h> -#ifdef Q_OS_WIN -#include <QtCore/qt_windows.h> // for qt_win_display_dc() -#endif - #include "qdatetime.h" #include <qpa/qplatformwindow.h> #include <qtwidgets_tracepoints_p.h> +#ifdef Q_OS_MACOS +#include <QtCore/private/qcore_mac_p.h> +#endif + #include <algorithm> #include <iterator> -//#define ALIEN_DEBUG - static void initResources() { Q_INIT_RESOURCE(qstyle); @@ -127,6 +98,17 @@ static void initResources() QT_BEGIN_NAMESPACE +Q_STATIC_LOGGING_CATEGORY(lcWidgetPopup, "qt.widgets.popup"); + +using namespace Qt::StringLiterals; + +Q_TRACE_PREFIX(qtwidgets, + "#include <qcoreevent.h>" +); +Q_TRACE_METADATA(qtwidgets, "ENUM { AUTO, RANGE User ... MaxUser } QEvent::Type;"); +Q_TRACE_POINT(qtwidgets, QApplication_notify_entry, QObject *receiver, QEvent *event, QEvent::Type type); +Q_TRACE_POINT(qtwidgets, QApplication_notify_exit, bool consumed, bool filtered); + // Helper macro for static functions to check on the existence of the application class. #define CHECK_QAPP_INSTANCE(...) \ if (Q_LIKELY(QCoreApplication::instance())) { \ @@ -142,8 +124,8 @@ QApplicationPrivate *QApplicationPrivate::self = nullptr; bool QApplicationPrivate::autoSipEnabled = true; -QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, int flags) - : QApplicationPrivateBase(argc, argv, flags) +QApplicationPrivate::QApplicationPrivate(int &argc, char **argv) + : QGuiApplicationPrivate(argc, argv) { application_type = QApplicationPrivate::Gui; @@ -218,8 +200,7 @@ void QApplicationPrivate::createEventDispatcher() \li It provides localization of strings that are visible to the user via translate(). - \li It provides some magical objects like the desktop() and the - clipboard(). + \li It provides some magical objects like the clipboard(). \li It knows about the application's windows. You can ask which widget is at a certain position using widgetAt(), get a list of @@ -266,7 +247,6 @@ void QApplicationPrivate::createEventDispatcher() postEvent(), sendPostedEvents(), removePostedEvents(), - hasPendingEvents(), notify(). \row @@ -284,7 +264,6 @@ void QApplicationPrivate::createEventDispatcher() \li Widgets \li allWidgets(), topLevelWidgets(), - desktop(), activePopupWidget(), activeModalWidget(), clipboard(), @@ -308,22 +287,6 @@ void QApplicationPrivate::createEventDispatcher() \sa QCoreApplication, QAbstractEventDispatcher, QEventLoop, QSettings */ -#if QT_DEPRECATED_SINCE(5, 8) -// ### fixme: Qt 6: Remove ColorSpec and accessors. -/*! - \enum QApplication::ColorSpec - \obsolete - - \value NormalColor the default color allocation policy - \value CustomColor the same as NormalColor for X11; allocates colors - to a palette on demand under Windows - \value ManyColor the right choice for applications that use thousands of - colors - - See setColorSpec() for full details. -*/ -#endif - /*! \fn QWidget *QApplication::topLevelAt(const QPoint &point) @@ -349,7 +312,6 @@ QWidget *QApplication::topLevelAt(const QPoint &pos) */ void qt_init_tooltip_palette(); -void qt_cleanup(); QStyle *QApplicationPrivate::app_style = nullptr; // default application style #ifndef QT_NO_STYLE_STYLESHEET @@ -369,12 +331,11 @@ QPointer<QWidget> QApplicationPrivate::wheel_widget; #endif bool qt_in_tab_key_event = false; int qt_antialiasing_threshold = -1; -QSize QApplicationPrivate::app_strut = QSize(0,0); // no default application strut int QApplicationPrivate::enabledAnimations = QPlatformTheme::GeneralUiEffect; bool QApplicationPrivate::widgetCount = false; #ifdef QT_KEYPAD_NAVIGATION Qt::NavigationMode QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadTabOrder; -QWidget *QApplicationPrivate::oldEditFocus = 0; +QWidget *QApplicationPrivate::oldEditFocus = nullptr; #endif inline bool QApplicationPrivate::isAlien(QWidget *widget) @@ -393,9 +354,7 @@ Q_GLOBAL_STATIC(FontHash, app_fonts) // Exported accessor for use outside of this file FontHash *qt_app_fonts_hash() { return app_fonts(); } -QWidgetList *QApplicationPrivate::popupWidgets = nullptr; // has keyboard input focus - -QDesktopWidget *qt_desktopWidget = nullptr; // root window widgets +QWidget *qt_desktopWidget = nullptr; // root window widgets /*! \internal @@ -406,7 +365,7 @@ void QApplicationPrivate::process_cmdline() styleOverride = QString::fromLocal8Bit(qgetenv("QT_STYLE_OVERRIDE")); // process platform-indep command line - if (!qt_is_gui_used || !argc) + if (qt_is_tty_app || !argc) return; int i, j; @@ -426,10 +385,10 @@ void QApplicationPrivate::process_cmdline() // obsolete argument #ifndef QT_NO_STYLE_STYLESHEET } else if (strcmp(arg, "-stylesheet") == 0 && i < argc -1) { - styleSheet = QLatin1String("file:///"); + styleSheet = "file:///"_L1; styleSheet.append(QString::fromLocal8Bit(argv[++i])); } else if (strncmp(arg, "-stylesheet=", 12) == 0) { - styleSheet = QLatin1String("file:///"); + styleSheet = "file:///"_L1; styleSheet.append(QString::fromLocal8Bit(arg + 12)); #endif } else if (qstrcmp(arg, "-widgetcount") == 0) { @@ -439,7 +398,7 @@ void QApplicationPrivate::process_cmdline() } } - if(j < argc) { + if (j < argc) { argv[j] = nullptr; argc = j; } @@ -493,9 +452,9 @@ void QApplicationPrivate::process_cmdline() #ifdef Q_QDOC QApplication::QApplication(int &argc, char **argv) #else -QApplication::QApplication(int &argc, char **argv, int _internal) +QApplication::QApplication(int &argc, char **argv, int) #endif - : QGuiApplication(*new QApplicationPrivate(argc, argv, _internal)) + : QGuiApplication(*new QApplicationPrivate(argc, argv)) { Q_D(QApplication); d->init(); @@ -514,7 +473,7 @@ void QApplicationPrivate::init() initResources(); - qt_is_gui_used = (application_type != QApplicationPrivate::Tty); + qt_is_tty_app = (application_type == QApplicationPrivate::Tty); process_cmdline(); // Must be called before initialize() @@ -526,7 +485,7 @@ void QApplicationPrivate::init() initialize(); eventDispatcher->startingUp(); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) // factory for accessible interfaces for widgets shipped with Qt QAccessible::installFactory(&qAccessibleFactory); #endif @@ -535,21 +494,18 @@ void QApplicationPrivate::init() void qt_init_tooltip_palette() { -#ifndef QT_NO_TOOLTIP - if (const QPalette *toolTipPalette = QGuiApplicationPrivate::platformTheme()->palette(QPlatformTheme::ToolTipPalette)) - QToolTip::setPalette(*toolTipPalette); +#if QT_CONFIG(tooltip) + if (const QPalette *toolTipPalette = QGuiApplicationPrivate::platformTheme()->palette(QPlatformTheme::ToolTipPalette)) { + QPalette toolTipPal = *toolTipPalette; + toolTipPal.setResolveMask(0); + QToolTip::setPalette(toolTipPal); + } #endif } -#if QT_CONFIG(statemachine) -void qRegisterGuiStateMachine(); -void qUnregisterGuiStateMachine(); -#endif extern void qRegisterWidgetsVariant(); /*! - \fn void QApplicationPrivate::initialize() - Initializes the QApplication object, called from the constructors. */ void QApplicationPrivate::initialize() @@ -572,22 +528,20 @@ void QApplicationPrivate::initialize() } else { qWarning("QApplication: invalid style override '%s' passed, ignoring it.\n" "\tAvailable styles: %s", qPrintable(styleOverride), - qPrintable(QStyleFactory::keys().join(QLatin1String(", ")))); + qPrintable(QStyleFactory::keys().join(", "_L1))); + // Clear styleOverride so it is not picked by Qt Quick Controls (QTBUG-100563) + styleOverride.clear(); } } // Trigger default style if none was set already Q_UNUSED(QApplication::style()); } -#if QT_CONFIG(statemachine) - // trigger registering of QStateMachine's GUI types - qRegisterGuiStateMachine(); -#endif if (qEnvironmentVariableIntValue("QT_USE_NATIVE_WINDOWS") > 0) QCoreApplication::setAttribute(Qt::AA_NativeWindows); - if (qt_is_gui_used) + if (!qt_is_tty_app) initializeMultitouch(); if (QGuiApplication::desktopSettingsAware()) @@ -673,8 +627,8 @@ void QApplicationPrivate::initializeWidgetFontHash() QWidget *QApplication::activePopupWidget() { - return QApplicationPrivate::popupWidgets && !QApplicationPrivate::popupWidgets->isEmpty() ? - QApplicationPrivate::popupWidgets->constLast() : nullptr; + auto *win = qobject_cast<QWidgetWindow *>(QGuiApplicationPrivate::activePopupWindow()); + return win ? win->widget() : nullptr; } @@ -747,58 +701,24 @@ QApplication::~QApplication() QApplicationPrivate::app_style = nullptr; #if QT_CONFIG(draganddrop) - if (qt_is_gui_used) + if (!qt_is_tty_app) delete QDragManager::self(); #endif d->cleanupMultitouch(); - qt_cleanup(); + QPixmapCache::clear(); + QColormap::cleanup(); + + QApplicationPrivate::active_window = nullptr; //### this should not be necessary if (QApplicationPrivate::widgetCount) qDebug("Widgets left: %i Max widgets: %i \n", QWidgetPrivate::instanceCounter, QWidgetPrivate::maxInstances); QApplicationPrivate::obey_desktop_settings = true; - QApplicationPrivate::app_strut = QSize(0, 0); QApplicationPrivate::enabledAnimations = QPlatformTheme::GeneralUiEffect; QApplicationPrivate::widgetCount = false; - -#if QT_CONFIG(statemachine) - // trigger unregistering of QStateMachine's GUI types - qUnregisterGuiStateMachine(); -#endif -} - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) -// #fixme: Remove. -static HDC displayDC = 0; // display device context - -Q_WIDGETS_EXPORT HDC qt_win_display_dc() // get display DC -{ - Q_ASSERT(qApp && qApp->thread() == QThread::currentThread()); - if (!displayDC) - displayDC = GetDC(0); - return displayDC; -} -#endif -#endif - -void qt_cleanup() -{ - QPixmapCache::clear(); - QColormap::cleanup(); - - QApplicationPrivate::active_window = nullptr; //### this should not be necessary -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - if (displayDC) { - ReleaseDC(0, displayDC); - displayDC = 0; - } -#endif -#endif } /*! @@ -862,33 +782,54 @@ QWidget *QApplication::widgetAt(const QPoint &p) */ bool QApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) { - if ((event->type() == QEvent::UpdateRequest - || event->type() == QEvent::LayoutRequest - || event->type() == QEvent::Resize - || event->type() == QEvent::Move - || event->type() == QEvent::LanguageChange)) { - for (QPostEventList::const_iterator it = postedEvents->constBegin(); it != postedEvents->constEnd(); ++it) { - const QPostEvent &cur = *it; - if (cur.receiver != receiver || cur.event == nullptr || cur.event->type() != event->type()) - continue; - if (cur.event->type() == QEvent::LayoutRequest - || cur.event->type() == QEvent::UpdateRequest) { - ; - } else if (cur.event->type() == QEvent::Resize) { - ((QResizeEvent *)(cur.event))->s = ((QResizeEvent *)event)->s; - } else if (cur.event->type() == QEvent::Move) { - ((QMoveEvent *)(cur.event))->p = ((QMoveEvent *)event)->p; - } else if (cur.event->type() == QEvent::LanguageChange) { - ; - } else { - continue; - } - delete event; - return true; + // Only compress the following events: + const QEvent::Type type = event->type(); + switch (type) { + case QEvent::UpdateRequest: + case QEvent::UpdateLater: + case QEvent::LayoutRequest: + case QEvent::Resize: + case QEvent::Move: + case QEvent::LanguageChange: + break; + default: + return QGuiApplication::compressEvent(event, receiver, postedEvents); + } + + for (const auto &postedEvent : std::as_const(*postedEvents)) { + + // Continue, unless a valid event of the same type exists for the same receiver + if (postedEvent.receiver != receiver + || !postedEvent.event + || postedEvent.event->type() != type) { + continue; } - return false; + + // Handle type specific compression + switch (type) { + case QEvent::Resize: + static_cast<QResizeEvent *>(postedEvent.event)->m_size = + static_cast<const QResizeEvent *>(event)->size(); + break; + case QEvent::Move: + static_cast<QMoveEvent *>(postedEvent.event)->m_pos = + static_cast<const QMoveEvent *>(event)->pos(); + break; + case QEvent::UpdateLater: + static_cast<QUpdateLaterEvent *>(postedEvent.event)->m_region += + static_cast<const QUpdateLaterEvent *>(event)->region(); + break; + case QEvent::UpdateRequest: + case QEvent::LanguageChange: + case QEvent::LayoutRequest: + break; + default: + continue; + } + delete event; + return true; } - return QGuiApplication::compressEvent(event, receiver, postedEvents); + return false; } /*! @@ -985,10 +926,7 @@ QStyle *QApplication::style() // Take ownership of the style defaultStyle->setParent(qApp); - if (testAttribute(Qt::AA_SetPalette)) - defaultStyle->polish(*QGuiApplicationPrivate::app_pal); - else - QApplicationPrivate::initializeWidgetPalettesFromTheme(); + QGuiApplicationPrivate::updatePalette(); #ifndef QT_NO_STYLE_STYLESHEET if (!QApplicationPrivate::styleSheet.isEmpty()) { @@ -1059,13 +997,10 @@ void QApplication::setStyle(QStyle *style) QApplicationPrivate::app_style = style; QApplicationPrivate::app_style->setParent(qApp); // take ownership - // take care of possible palette requirements of certain gui - // styles. Do it before polishing the application since the style - // might call QApplication::setPalette() itself - if (testAttribute(Qt::AA_SetPalette)) - QApplicationPrivate::app_style->polish(*QGuiApplicationPrivate::app_pal); - else - QApplicationPrivate::initializeWidgetPalettesFromTheme(); + // Take care of possible palette requirements of certain + // styles. Do it before polishing the application since the + // style might call QApplication::setPalette() itself. + QGuiApplicationPrivate::updatePalette(); // The default widget font hash is based on the platform theme, // not the style, but the widget fonts could in theory have been @@ -1123,7 +1058,7 @@ void QApplication::setStyle(QStyle *style) Requests a QStyle object for \a style from the QStyleFactory. The string must be one of the QStyleFactory::keys(), typically one of - "windows", "windowsvista", "fusion", or "macintosh". Style + "windows", "windowsvista", "fusion", or "macos". Style names are case insensitive. Returns \nullptr if an unknown \a style is passed, otherwise the QStyle object @@ -1143,109 +1078,6 @@ QStyle* QApplication::setStyle(const QString& style) return s; } -#if QT_DEPRECATED_SINCE(5, 8) -/*! - Returns the color specification. - \obsolete - - \sa QApplication::setColorSpec() -*/ - -int QApplication::colorSpec() -{ - return QApplication::NormalColor; -} - -/*! - Sets the color specification for the application to \a spec. - \obsolete - - This call has no effect. - - The color specification controls how the application allocates colors when - run on a display with a limited amount of colors, e.g. 8 bit / 256 color - displays. - - The color specification must be set before you create the QApplication - object. - - The options are: - \list - \li QApplication::NormalColor. This is the default color allocation - strategy. Use this option if your application uses buttons, menus, - texts and pixmaps with few colors. With this option, the - application uses system global colors. This works fine for most - applications under X11, but on the Windows platform, it may cause - dithering of non-standard colors. - \li QApplication::CustomColor. Use this option if your application - needs a small number of custom colors. On X11, this option is the - same as NormalColor. On Windows, Qt creates a Windows palette, and - allocates colors to it on demand. - \li QApplication::ManyColor. Use this option if your application is - very color hungry, e.g., it requires thousands of colors. \br - Under X11 the effect is: - \list - \li For 256-color displays which have at best a 256 color true - color visual, the default visual is used, and colors are - allocated from a color cube. The color cube is the 6x6x6 - (216 color) "Web palette" (the red, green, and blue - components always have one of the following values: 0x00, - 0x33, 0x66, 0x99, 0xCC, or 0xFF), but the number of colors - can be changed by the \e -ncols option. The user can force - the application to use the true color visual with the - \l{QApplication::QApplication()}{-visual} option. - \li For 256-color displays which have a true color visual with - more than 256 colors, use that visual. Silicon Graphics X - servers this feature, for example. They provide an 8 bit - visual by default but can deliver true color when asked. - \endlist - On Windows, Qt creates a Windows palette, and fills it with a color - cube. - \endlist - - Be aware that the CustomColor and ManyColor choices may lead to colormap - flashing: The foreground application gets (most) of the available colors, - while the background windows will look less attractive. - - Example: - - \snippet code/src_gui_kernel_qapplication.cpp 2 - - \sa colorSpec() -*/ - -void QApplication::setColorSpec(int spec) -{ - Q_UNUSED(spec) -} -#endif - -/*! - \property QApplication::globalStrut - \brief the minimum size that any GUI element that the user can interact - with should have - \deprecated - - For example, no button should be resized to be smaller than the global - strut size. The strut size should be considered when reimplementing GUI - controls that may be used on touch-screens or similar I/O devices. - - Example: - - \snippet code/src_gui_kernel_qapplication.cpp 3 - - By default, this property contains a QSize object with zero width and height. -*/ -QSize QApplication::globalStrut() -{ - return QApplicationPrivate::app_strut; -} - -void QApplication::setGlobalStrut(const QSize& strut) -{ - QApplicationPrivate::app_strut = strut; -} - // Widget specific palettes QApplicationPrivate::PaletteHash QApplicationPrivate::widgetPalettes; @@ -1262,6 +1094,19 @@ QPalette QApplicationPrivate::basePalette() const if (const QPalette *themePalette = platformTheme() ? platformTheme()->palette() : nullptr) palette = themePalette->resolve(palette); + // This palette now is Qt-generated, so reset the resolve mask. This allows + // QStyle::polish implementations to respect palettes that are user provided, + // by checking if the palette has a brush set for a color that the style might + // otherwise overwrite. + palette.setResolveMask(0); + + // Finish off by letting the application style polish the palette. This will + // not result in the polished palette becoming a user-set palette, as the + // resulting base palette is only used as a fallback, with the resolve mask + // set to 0. + if (app_style) + app_style->polish(palette); + return palette; } @@ -1335,17 +1180,19 @@ QPalette QApplication::palette(const char *className) */ void QApplication::setPalette(const QPalette &palette, const char* className) { - QPalette polishedPalette = palette; - - if (QApplicationPrivate::app_style) - QApplicationPrivate::app_style->polish(polishedPalette); - if (className) { + QPalette polishedPalette = palette; + if (QApplicationPrivate::app_style) { + auto originalResolveMask = palette.resolveMask(); + QApplicationPrivate::app_style->polish(polishedPalette); + polishedPalette.setResolveMask(originalResolveMask); + } + QApplicationPrivate::widgetPalettes.insert(className, polishedPalette); if (qApp) qApp->d_func()->handlePaletteChanged(className); } else { - QGuiApplication::setPalette(polishedPalette); + QGuiApplication::setPalette(palette); } } @@ -1356,23 +1203,20 @@ void QApplicationPrivate::handlePaletteChanged(const char *className) // Setting the global application palette is documented to // reset any previously set class specific widget palettes. - bool sendPaletteChangeToAllWidgets = false; - if (!className && !widgetPalettes.isEmpty()) { - sendPaletteChangeToAllWidgets = true; + if (!className && !widgetPalettes.isEmpty()) widgetPalettes.clear(); - } QGuiApplicationPrivate::handlePaletteChanged(className); QEvent event(QEvent::ApplicationPaletteChange); const QWidgetList widgets = QApplication::allWidgets(); for (auto widget : widgets) { - if (sendPaletteChangeToAllWidgets || (!className && widget->isWindow()) || (className && widget->inherits(className))) + if (!widget->isWindow() && widget->inherits(className)) QCoreApplication::sendEvent(widget, &event); } #if QT_CONFIG(graphicsview) - for (auto scene : qAsConst(scene_list)) + for (auto scene : std::as_const(scene_list)) QCoreApplication::sendEvent(scene, &event); #endif @@ -1429,9 +1273,11 @@ QFont QApplication::font() /*! \overload - Returns the default font for the \a widget. + Returns the default font for the \a widget. If a default font was not + registered for the \a{widget}'s class, it returns the default font of + its nearest registered superclass. - \sa fontMetrics(), QWidget::setFont() + \sa fontMetrics(), setFont(), QWidget::setFont() */ QFont QApplication::font(const QWidget *widget) @@ -1449,14 +1295,16 @@ QFont QApplication::font(const QWidget *widget) return hash->value(QByteArrayLiteral("QMiniFont")); } #endif - FontHashConstIt it = hash->constFind(widget->metaObject()->className()); + // Return the font for the nearest registered superclass + const QMetaObject *metaObj = widget->metaObject(); + FontHashConstIt it = hash->constFind(metaObj->className()); const FontHashConstIt cend = hash->constEnd(); + while (it == cend && metaObj != &QWidget::staticMetaObject) { + metaObj = metaObj->superClass(); + it = hash->constFind(metaObj->className()); + } if (it != cend) return it.value(); - for (it = hash->constBegin(); it != cend; ++it) { - if (widget->inherits(it.key())) - return it.value(); - } } return font(); } @@ -1501,26 +1349,20 @@ QFont QApplication::font(const char *className) void QApplication::setFont(const QFont &font, const char *className) { - bool all = false; FontHash *hash = app_fonts(); if (!className) { QGuiApplication::setFont(font); - if (hash && hash->size()) { - all = true; + if (hash && hash->size()) hash->clear(); - } } else if (hash) { hash->insert(className, font); } if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) { - // Send ApplicationFontChange to qApp itself, and to the widgets. QEvent e(QEvent::ApplicationFontChange); - QCoreApplication::sendEvent(QApplication::instance(), &e); - QWidgetList wids = QApplication::allWidgets(); for (QWidgetList::ConstIterator it = wids.constBegin(), cend = wids.constEnd(); it != cend; ++it) { QWidget *w = *it; - if (all || (!className && w->isWindow()) || w->inherits(className)) // matching class + if (!w->isWindow() && w->inherits(className)) // matching class sendEvent(w, &e); } @@ -1558,6 +1400,12 @@ void QApplicationPrivate::setSystemFont(const QFont &font) */ QString QApplicationPrivate::desktopStyleKey() { +#if defined(QT_BUILD_INTERNAL) + // Allow auto-tests to override the desktop style + if (qEnvironmentVariableIsSet("QT_DESKTOP_STYLE_KEY")) + return QString::fromLocal8Bit(qgetenv("QT_DESKTOP_STYLE_KEY")); +#endif + // The platform theme might return a style that is not available, find // first valid one. if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { @@ -1571,24 +1419,6 @@ QString QApplicationPrivate::desktopStyleKey() return QString(); } -#if QT_VERSION < 0x060000 // remove these forwarders in Qt 6 -/*! - \property QApplication::windowIcon - \brief the default window icon - - \sa QWidget::setWindowIcon(), {Setting the Application Icon} -*/ -QIcon QApplication::windowIcon() -{ - return QGuiApplication::windowIcon(); -} - -void QApplication::setWindowIcon(const QIcon &icon) -{ - QGuiApplication::setWindowIcon(icon); -} -#endif - void QApplicationPrivate::notifyWindowIconChanged() { QEvent ev(QEvent::ApplicationWindowIconChange); @@ -1688,7 +1518,7 @@ void QApplicationPrivate::setFocusWidget(QWidget *focus, Qt::FocusReason reason) QWidget *prev = focus_widget; focus_widget = focus; - if(focus_widget) + if (focus_widget) focus_widget->d_func()->setFocus_sys(); if (reason != Qt::NoFocusReason) { @@ -1696,7 +1526,7 @@ void QApplicationPrivate::setFocusWidget(QWidget *focus, Qt::FocusReason reason) //send events if (prev) { #ifdef QT_KEYPAD_NAVIGATION - if (QApplication::keypadNavigationEnabled()) { + if (QApplicationPrivate::keyboardNavigationEnabled()) { if (prev->hasEditFocus() && reason != Qt::PopupFocusReason) prev->setEditFocus(false); } @@ -1707,7 +1537,7 @@ void QApplicationPrivate::setFocusWidget(QWidget *focus, Qt::FocusReason reason) if (that) QCoreApplication::sendEvent(that->style(), &out); } - if(focus && QApplicationPrivate::focus_widget == focus) { + if (focus && QApplicationPrivate::focus_widget == focus) { QFocusEvent in(QEvent::FocusIn, reason); QPointer<QWidget> that = focus; QCoreApplication::sendEvent(focus, &in); @@ -1734,7 +1564,9 @@ QWidget *QApplication::activeWindow() return QApplicationPrivate::active_window; } +#if QT_DEPRECATED_SINCE(6,0) /*! + \deprecated Use the QFontMetricsF constructor instead. Returns display (screen) font metrics for the application font. \sa font(), setFont(), QWidget::fontMetrics(), QPainter::fontMetrics() @@ -1742,8 +1574,9 @@ QWidget *QApplication::activeWindow() QFontMetrics QApplication::fontMetrics() { - return desktop()->fontMetrics(); + return QApplicationPrivate::desktop()->fontMetrics(); } +#endif bool QApplicationPrivate::tryCloseAllWidgetWindows(QWindowList *processedWindows) { @@ -1774,30 +1607,21 @@ retry: return true; } -bool QApplicationPrivate::tryCloseAllWindows() -{ - QWindowList processedWindows; - return QApplicationPrivate::tryCloseAllWidgetWindows(&processedWindows) - && QGuiApplicationPrivate::tryCloseRemainingWindows(processedWindows); -} - /*! Closes all top-level windows. This function is particularly useful for applications with many top-level - windows. It could, for example, be connected to a \uicontrol{Exit} entry in the - \uicontrol{File} menu: - - \snippet mainwindows/mdi/mainwindow.cpp 0 + windows. The windows are closed in random order, until one window does not accept - the close event. The application quits when the last window was - successfully closed; this can be turned off by setting - \l quitOnLastWindowClosed to false. + the close event. The application quits when the last window was successfully + closed, unless \l quitOnLastWindowClosed is set to false. To trigger application + termination from e.g. a menu, use QCoreApplication::quit() instead of this + function. \sa quitOnLastWindowClosed, lastWindowClosed(), QWidget::close(), - QWidget::closeEvent(), lastWindowClosed(), QCoreApplication::quit(), topLevelWidgets(), - QWidget::isWindow() + QWidget::closeEvent(), lastWindowClosed(), QCoreApplication::quit(), + topLevelWidgets(), QWidget::isWindow() */ void QApplication::closeAllWindows() { @@ -1842,9 +1666,16 @@ void QApplication::aboutQt() bool QApplication::event(QEvent *e) { Q_D(QApplication); - if (e->type() == QEvent::Quit) { + switch (e->type()) { + case QEvent::Quit: + // FIXME: This logic first tries to close all windows, and then + // checks whether it was successful, but the conditions used in + // closeAllWindows() differ from the verification logic below. + // We should build on the logic in tryCloseAllWidgetWindows(). closeAllWindows(); for (auto *w : topLevelWidgets()) { + if (w->data->is_closing) + continue; if (w->isVisible() && !(w->windowType() == Qt::Desktop) && !(w->windowType() == Qt::Popup) && (!(w->windowType() == Qt::Dialog) || !w->parentWidget()) && !w->testAttribute(Qt::WA_DontShowOnScreen)) { e->ignore(); @@ -1856,7 +1687,7 @@ bool QApplication::event(QEvent *e) // closeAllWindows(). FIXME: Unify all this close magic through closeAllWindows. return QCoreApplication::event(e); #ifndef Q_OS_WIN - } else if (e->type() == QEvent::LocaleChange) { + case QEvent::LocaleChange: { // on Windows the event propagation is taken care by the // WM_SETTINGCHANGE event handler. const QWidgetList list = topLevelWidgets(); @@ -1866,8 +1697,10 @@ bool QApplication::event(QEvent *e) w->d_func()->setLocale_helper(QLocale(), true); } } + break; + } #endif - } else if (e->type() == QEvent::Timer) { + case QEvent::Timer: { QTimerEvent *te = static_cast<QTimerEvent*>(e); Q_ASSERT(te != nullptr); if (te->timerId() == d->toolTipWakeUp.timerId()) { @@ -1896,19 +1729,28 @@ bool QApplication::event(QEvent *e) } else if (te->timerId() == d->toolTipFallAsleep.timerId()) { d->toolTipFallAsleep.stop(); } + break; + } #if QT_CONFIG(whatsthis) - } else if (e->type() == QEvent::EnterWhatsThisMode) { + case QEvent::EnterWhatsThisMode: QWhatsThis::enterWhatsThisMode(); return true; #endif - } - - if(e->type() == QEvent::LanguageChange) { + case QEvent::LanguageChange: + case QEvent::ApplicationFontChange: + case QEvent::ApplicationPaletteChange: { + // QGuiApplication::event does not account for the cases where + // there is a top level widget without a window handle. So they + // need to have the event posted here const QWidgetList list = topLevelWidgets(); for (auto *w : list) { - if (!(w->windowType() == Qt::Desktop)) - postEvent(w, new QEvent(QEvent::LanguageChange)); + if (!w->windowHandle() && (w->windowType() != Qt::Desktop)) + postEvent(w, new QEvent(e->type())); } + break; + } + default: + break; } return QGuiApplication::event(e); @@ -1940,6 +1782,7 @@ void QApplicationPrivate::notifyLayoutDirectionChange() /*! \fn void QApplication::setActiveWindow(QWidget* active) + \deprecated [6.5] Use QWidget::activateWindow() instead. Sets the active window to the \a active widget in response to a system event. The function is called from the platform specific event handlers. @@ -1956,8 +1799,15 @@ void QApplicationPrivate::notifyLayoutDirectionChange() \sa activeWindow(), QWidget::activateWindow() */ +#if QT_DEPRECATED_SINCE(6,5) void QApplication::setActiveWindow(QWidget* act) { + QApplicationPrivate::setActiveWindow(act); +} +#endif + +void QApplicationPrivate::setActiveWindow(QWidget* act) +{ QWidget* window = act?act->window():nullptr; if (QApplicationPrivate::active_window == window) @@ -1974,8 +1824,8 @@ void QApplication::setActiveWindow(QWidget* act) QWidgetList toBeDeactivated; if (QApplicationPrivate::active_window) { - if (style()->styleHint(QStyle::SH_Widget_ShareActivation, nullptr, QApplicationPrivate::active_window)) { - const QWidgetList list = topLevelWidgets(); + if (QApplication::style()->styleHint(QStyle::SH_Widget_ShareActivation, nullptr, QApplicationPrivate::active_window)) { + const QWidgetList list = QApplication::topLevelWidgets(); for (auto *w : list) { if (w->isVisible() && w->isActiveWindow()) toBeDeactivated.append(w); @@ -1996,8 +1846,8 @@ void QApplication::setActiveWindow(QWidget* act) QApplicationPrivate::active_window = window; if (QApplicationPrivate::active_window) { - if (style()->styleHint(QStyle::SH_Widget_ShareActivation, nullptr, QApplicationPrivate::active_window)) { - const QWidgetList list = topLevelWidgets(); + if (QApplication::style()->styleHint(QStyle::SH_Widget_ShareActivation, nullptr, QApplicationPrivate::active_window)) { + const QWidgetList list = QApplication::topLevelWidgets(); for (auto *w : list) { if (w->isVisible() && w->isActiveWindow()) toBeActivated.append(w); @@ -2015,17 +1865,17 @@ void QApplication::setActiveWindow(QWidget* act) for (int i = 0; i < toBeActivated.size(); ++i) { QWidget *w = toBeActivated.at(i); - sendSpontaneousEvent(w, &windowActivate); - sendSpontaneousEvent(w, &activationChange); + QApplication::sendSpontaneousEvent(w, &windowActivate); + QApplication::sendSpontaneousEvent(w, &activationChange); } for(int i = 0; i < toBeDeactivated.size(); ++i) { QWidget *w = toBeDeactivated.at(i); - sendSpontaneousEvent(w, &windowDeactivate); - sendSpontaneousEvent(w, &activationChange); + QApplication::sendSpontaneousEvent(w, &windowDeactivate); + QApplication::sendSpontaneousEvent(w, &activationChange); } - if (QApplicationPrivate::popupWidgets == nullptr) { // !inPopupMode() + if (!inPopupMode()) { // then focus events if (!QApplicationPrivate::active_window && QApplicationPrivate::focus_widget) { QApplicationPrivate::setFocusWidget(nullptr, Qt::ActiveWindowFocusReason); @@ -2038,12 +1888,13 @@ void QApplication::setActiveWindow(QWidget* act) if (w) { w->setFocus(Qt::ActiveWindowFocusReason); } else { - // If the focus widget is not in the activate_window, clear the focus w = QApplicationPrivate::focus_widget; - if (!w && QApplicationPrivate::active_window->focusPolicy() != Qt::NoFocus) - QApplicationPrivate::setFocusWidget(QApplicationPrivate::active_window, Qt::ActiveWindowFocusReason); - else if (!QApplicationPrivate::active_window->isAncestorOf(w)) + if (!w && QApplicationPrivate::active_window->focusPolicy() != Qt::NoFocus) { + QApplicationPrivate::active_window->setFocus(Qt::ActiveWindowFocusReason); + } else if (!QApplicationPrivate::active_window->isAncestorOf(w)) { + // If the focus widget is not in the activate_window, clear the focus QApplicationPrivate::setFocusWidget(nullptr, Qt::ActiveWindowFocusReason); + } } } } @@ -2076,19 +1927,37 @@ QWidget *qt_tlw_for_window(QWindow *wnd) void QApplicationPrivate::notifyActiveWindowChange(QWindow *previous) { - Q_UNUSED(previous); - QWindow *wnd = QGuiApplicationPrivate::focus_window; - if (inPopupMode()) // some delayed focus event to ignore +#ifndef Q_OS_MACOS + // Some delayed focus event to ignore, unless we are on cocoa where + // popups can be opened via right-click on inactive applications + if (inPopupMode()) return; - QWidget *tlw = qt_tlw_for_window(wnd); - QApplication::setActiveWindow(tlw); +#endif + QWindow *focusWindow = QGuiApplicationPrivate::focus_window; + QWidget *focusWidget = qt_tlw_for_window(focusWindow); + QApplicationPrivate::setActiveWindow(focusWidget); // QTBUG-37126, Active X controls may set the focus on native child widgets. - if (wnd && tlw && wnd != tlw->windowHandle()) { - if (QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(wnd)) + if (focusWindow && focusWidget && focusWindow != focusWidget->windowHandle()) { + if (QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(focusWindow)) if (QWidget *widget = widgetWindow->widget()) if (widget->inherits("QAxHostWidget")) widget->setFocus(Qt::ActiveWindowFocusReason); } + + // QApplication::setActiveWindow() will deliver window activation events for + // QWidgetWindows. But for other subclasses of QWindow (like QQuickWindow), we + // need to send them explicitly, like we do from the base class implementation. + if (previous && !qobject_cast<QWidgetWindow *>(previous)) { + QEvent de(QEvent::WindowDeactivate); + QCoreApplication::sendEvent(previous, &de); + } + + if (focusWindow && !qobject_cast<QWidgetWindow *>(focusWindow)) { + QEvent ae(QEvent::WindowActivate); + QCoreApplication::sendEvent(focusWindow, &ae); + } + + // don't call base class to avoid double delivery of WindowActivate/Deactivate events } /*!internal @@ -2106,7 +1975,7 @@ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool f = toplevel; QWidget *w = f; - QWidget *test = f->d_func()->focus_next; + QWidget *test = f->nextInFocusChain(); bool seenWindow = false; bool focusWidgetAfterWindow = false; while (test && test != f) { @@ -2118,10 +1987,15 @@ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool // \a next). This is to ensure that we can tab in and out of compound widgets // without getting stuck in a tab-loop between parent and child. QWidget *focusProxy = test->d_func()->deepestFocusProxy(); - - if ((test->focusPolicy() & focus_flag) == focus_flag - && !(next && focusProxy && focusProxy->isAncestorOf(test)) - && !(!next && focusProxy && test->isAncestorOf(focusProxy)) + auto effectiveFocusPolicy = [](QWidget *widget) { + return widget->isEnabled() ? widget->focusPolicy() : Qt::NoFocus; + }; + const bool canTakeFocus = (effectiveFocusPolicy(focusProxy ? focusProxy : test) + & focus_flag) == focus_flag; + const bool composites = focusProxy ? (next ? focusProxy->isAncestorOf(test) + : test->isAncestorOf(focusProxy)) + : false; + if (canTakeFocus && !composites && test->isVisibleTo(toplevel) && test->isEnabled() && !(w->windowType() == Qt::SubWindow && !w->isAncestorOf(test)) && (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test)) @@ -2132,7 +2006,7 @@ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool if (next) break; } - test = test->d_func()->focus_next; + test = test->nextInFocusChain(); } if (wrappingOccurred != nullptr) @@ -2157,24 +2031,9 @@ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool */ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, const QPointF &globalPosF) { -#if 0 - if (leave) { - QEvent e(QEvent::Leave); - QCoreApplication::sendEvent(leave, & e); - } - if (enter) { - const QPoint windowPos = enter->window()->mapFromGlobal(globalPos); - QEnterEvent e(enter->mapFromGlobal(globalPos), windowPos, globalPos); - QCoreApplication::sendEvent(enter, & e); - } - return; -#endif - if ((!enter && !leave) || (enter == leave)) return; -#ifdef ALIEN_DEBUG - qDebug() << "QApplicationPrivate::dispatchEnterLeave, ENTER:" << enter << "LEAVE:" << leave; -#endif + QWidgetList leaveList; QWidgetList enterList; @@ -2230,7 +2089,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con if (w->testAttribute(Qt::WA_Hover) && (!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) { Q_ASSERT(instance()); - QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1), w->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos), + QHoverEvent he(QEvent::HoverLeave, QPointF(-1, -1), globalPosF, w->mapFromGlobal(globalPosF), QGuiApplication::keyboardModifiers()); qApp->d_func()->notify_helper(w, &he); } @@ -2238,20 +2097,21 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con } if (!enterList.isEmpty()) { // Guard against QGuiApplicationPrivate::lastCursorPosition initialized to qInf(), qInf(). - const QPoint globalPos = qIsInf(globalPosF.x()) - ? QPoint(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX) - : globalPosF.toPoint(); - const QPoint windowPos = qAsConst(enterList).back()->window()->mapFromGlobal(globalPos); + const QPointF globalPos = qIsInf(globalPosF.x()) + ? QPointF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX) + : globalPosF; + const QPointF windowPos = std::as_const(enterList).back()->window()->mapFromGlobal(globalPos); for (auto it = enterList.crbegin(), end = enterList.crend(); it != end; ++it) { auto *w = *it; if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, nullptr)) { const QPointF localPos = w->mapFromGlobal(globalPos); - QEnterEvent enterEvent(localPos, windowPos, globalPosF); + QEnterEvent enterEvent(localPos, windowPos, globalPos); QCoreApplication::sendEvent(w, &enterEvent); if (w->testAttribute(Qt::WA_Hover) && (!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) { - QHoverEvent he(QEvent::HoverEnter, localPos, QPoint(-1, -1), + QHoverEvent he(QEvent::HoverEnter, windowPos, QPointF(-1, -1), globalPos, QGuiApplication::keyboardModifiers()); + QMutableEventPoint::setPosition(he.point(0), localPos); qApp->d_func()->notify_helper(w, &he); } } @@ -2285,7 +2145,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con if (!parentOfLeavingCursor->window()->graphicsProxyWidget()) #endif { - if (enter == QApplication::desktop()) { + if (enter == QApplicationPrivate::desktop()) { qt_qpa_set_cursor(enter, true); } else { qt_qpa_set_cursor(parentOfLeavingCursor, true); @@ -2328,107 +2188,16 @@ bool QApplicationPrivate::isBlockedByModal(QWidget *widget) return window && self->isWindowBlocked(window); } -bool QApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blockingWindow) const +Qt::WindowModality QApplicationPrivate::defaultModality() const { - QWindow *unused = nullptr; - if (Q_UNLIKELY(!window)) { - qWarning().nospace() << "window == 0 passed."; - return false; - } - if (!blockingWindow) - blockingWindow = &unused; + return Qt::ApplicationModal; +} - if (modalWindowList.isEmpty()) { - *blockingWindow = nullptr; - return false; - } +bool QApplicationPrivate::windowNeverBlocked(QWindow *window) const +{ QWidget *popupWidget = QApplication::activePopupWidget(); QWindow *popupWindow = popupWidget ? popupWidget->windowHandle() : nullptr; - if (popupWindow == window || (!popupWindow && QWindowPrivate::get(window)->isPopup())) { - *blockingWindow = nullptr; - return false; - } - - for (int i = 0; i < modalWindowList.count(); ++i) { - QWindow *modalWindow = modalWindowList.at(i); - - // A window is not blocked by another modal window if the two are - // the same, or if the window is a child of the modal window. - if (window == modalWindow || modalWindow->isAncestorOf(window, QWindow::IncludeTransients)) { - *blockingWindow = nullptr; - return false; - } - - Qt::WindowModality windowModality = modalWindow->modality(); - QWidgetWindow *modalWidgetWindow = qobject_cast<QWidgetWindow *>(modalWindow); - if (windowModality == Qt::NonModal) { - // determine the modality type if it hasn't been set on the - // modalWindow's widget, this normally happens when waiting for a - // native dialog. use WindowModal if we are the child of a group - // leader; otherwise use ApplicationModal. - QWidget *m = modalWidgetWindow ? modalWidgetWindow->widget() : nullptr; - while (m && !m->testAttribute(Qt::WA_GroupLeader)) { - m = m->parentWidget(); - if (m) - m = m->window(); - } - windowModality = (m && m->testAttribute(Qt::WA_GroupLeader)) - ? Qt::WindowModal - : Qt::ApplicationModal; - } - - switch (windowModality) { - case Qt::ApplicationModal: - { - QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(window); - QWidget *groupLeaderForWidget = widgetWindow ? widgetWindow->widget() : nullptr; - while (groupLeaderForWidget && !groupLeaderForWidget->testAttribute(Qt::WA_GroupLeader)) - groupLeaderForWidget = groupLeaderForWidget->parentWidget(); - - if (groupLeaderForWidget) { - // if \a widget has WA_GroupLeader, it can only be blocked by ApplicationModal children - QWidget *m = modalWidgetWindow ? modalWidgetWindow->widget() : nullptr; - while (m && m != groupLeaderForWidget && !m->testAttribute(Qt::WA_GroupLeader)) - m = m->parentWidget(); - if (m == groupLeaderForWidget) { - *blockingWindow = m->windowHandle(); - return true; - } - } else if (modalWindow != window) { - *blockingWindow = modalWindow; - return true; - } - break; - } - case Qt::WindowModal: - { - QWindow *w = window; - do { - QWindow *m = modalWindow; - do { - if (m == w) { - *blockingWindow = m; - return true; - } - QWindow *p = m->parent(); - if (!p) - p = m->transientParent(); - m = p; - } while (m); - QWindow *p = w->parent(); - if (!p) - p = w->transientParent(); - w = p; - } while (w); - break; - } - default: - Q_ASSERT_X(false, "QApplication", "internal error, a modal window cannot be modeless"); - break; - } - } - *blockingWindow = nullptr; - return false; + return popupWindow == window || (!popupWindow && QWindowPrivate::get(window)->isPopup()); } /*!\internal @@ -2459,12 +2228,6 @@ bool qt_try_modal(QWidget *widget, QEvent::Type type) bool block_event = false; switch (type) { -#if 0 - case QEvent::Focus: - if (!static_cast<QWSFocusEvent*>(event)->simpleData.get_focus) - break; - // drop through -#endif case QEvent::MouseButtonPress: // disallow mouse/key events case QEvent::MouseButtonRelease: case QEvent::MouseMove: @@ -2514,9 +2277,6 @@ QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPoint if (mouseGrabber && mouseGrabber != candidate) { receiver = mouseGrabber; *pos = receiver->mapFromGlobal(candidate->mapToGlobal(windowPos)); -#ifdef ALIEN_DEBUG - qDebug() << " ** receiver adjusted to:" << receiver << "pos:" << pos; -#endif } return receiver; @@ -2546,7 +2306,7 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, const bool graphicsWidget = nativeWidget->testAttribute(Qt::WA_DontShowOnScreen); - bool widgetUnderMouse = QRectF(receiver->rect()).contains(event->localPos()); + bool widgetUnderMouse = QRectF(receiver->rect()).contains(event->position()); // Clear the obsolete leaveAfterRelease value, if mouse button has been released but // leaveAfterRelease has not been updated. @@ -2572,21 +2332,14 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, || (isAlien(lastMouseReceiver) && !alienWidget)) { if (activePopupWidget) { if (!QWidget::mouseGrabber()) - dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver, event->screenPos()); + dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver, event->globalPosition()); } else { - dispatchEnterLeave(receiver, lastMouseReceiver, event->screenPos()); + dispatchEnterLeave(receiver, lastMouseReceiver, event->globalPosition()); } } } -#ifdef ALIEN_DEBUG - qDebug() << "QApplicationPrivate::sendMouseEvent: receiver:" << receiver - << "pos:" << event->pos() << "alien" << alienWidget << "button down" - << *buttonDown << "last" << lastMouseReceiver << "leave after release" - << leaveAfterRelease; -#endif - // We need this quard in case someone opens a modal dialog / popup. If that's the case // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver. const bool wasLeaveAfterRelease = leaveAfterRelease != nullptr; @@ -2611,8 +2364,8 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, if (nativeGuard) enter = alienGuard ? alienWidget : nativeWidget; else // The receiver is typically deleted on mouse release with drag'n'drop. - enter = QApplication::widgetAt(event->globalPos()); - dispatchEnterLeave(enter, leaveAfterRelease, event->screenPos()); + enter = QApplication::widgetAt(event->globalPosition().toPoint()); + dispatchEnterLeave(enter, leaveAfterRelease, event->globalPosition()); leaveAfterRelease = nullptr; lastMouseReceiver = enter; } else if (!wasLeaveAfterRelease) { @@ -2620,7 +2373,7 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, if (!QWidget::mouseGrabber()) lastMouseReceiver = alienGuard ? alienWidget : (nativeGuard ? nativeWidget : nullptr); } else { - lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPos()); + lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPosition().toPoint()); } } @@ -2631,7 +2384,7 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, This function should only be called when the widget changes visibility, i.e. when the \a widget is shown, hidden or deleted. This function does nothing if the widget is a top-level or native, i.e. not an alien widget. In that - case enter/leave events are genereated by the underlying windowing system. + case enter/leave events are generated by the underlying windowing system. */ extern QPointer<QWidget> qt_last_mouse_receiver; extern Q_WIDGETS_EXPORT QWidget *qt_button_down; @@ -2645,7 +2398,7 @@ void QApplicationPrivate::sendSyntheticEnterLeave(QWidget *widget) return; // Widget was not under the cursor when it was hidden/deleted. if (widgetInShow && widget->parentWidget()->data->in_show) - return; // Ingore recursive show. + return; // Ignore recursive show. QWidget *mouseGrabber = QWidget::mouseGrabber(); if (mouseGrabber && mouseGrabber != widget) @@ -2685,22 +2438,19 @@ void QApplicationPrivate::sendSyntheticEnterLeave(QWidget *widget) } /*! - Returns the desktop widget (also called the root window). + \internal - The desktop may be composed of multiple screens, so it would be incorrect, - for example, to attempt to \e center some widget in the desktop's geometry. - QDesktopWidget has various functions for obtaining useful geometries upon - the desktop, such as QDesktopWidget::screenGeometry() and - QDesktopWidget::availableGeometry(). + Returns the desktop widget (also called the root window). - On X11, it is also possible to draw on the desktop. + The widget represents the entire virtual desktop, and its geometry will + be the union of all screens. */ -QDesktopWidget *QApplication::desktop() +QWidget *QApplicationPrivate::desktop() { CHECK_QAPP_INSTANCE(nullptr) if (!qt_desktopWidget || // not created yet !(qt_desktopWidget->windowType() == Qt::Desktop)) { // reparented away - qt_desktopWidget = new QDesktopWidget(); + qt_desktopWidget = new QWidget(nullptr, Qt::Desktop); } return qt_desktopWidget; } @@ -2730,7 +2480,7 @@ void QApplication::setStartDragTime(int ms) The default value is 500 ms. - \sa startDragDistance(), {Drag and Drop} + \sa startDragDistance(), {Drag and Drop in Qt}{Drag and Drop} */ int QApplication::startDragTime() @@ -2751,6 +2501,7 @@ void QApplication::setStartDragDistance(int l) /*! \property QApplication::startDragDistance + \brief the minimum distance required for a drag and drop operation to start. If you support drag and drop in your application, and want to start a drag and drop operation after the user has moved the cursor a certain distance @@ -2768,7 +2519,7 @@ void QApplication::setStartDragDistance(int l) The default value (if the platform doesn't provide a different default) is 10 pixels. - \sa startDragTime(), QPoint::manhattanLength(), {Drag and Drop} + \sa startDragTime(), QPoint::manhattanLength(), {Drag and Drop in Qt}{Drag and Drop} */ int QApplication::startDragDistance() @@ -2790,8 +2541,9 @@ int QApplication::startDragDistance() exec(), because modal widgets call exec() to start a local event loop. To make your application perform idle processing, i.e., executing a special - function whenever there are no pending events, use a QTimer with 0 timeout. - More advanced idle processing schemes can be achieved using processEvents(). + function whenever there are no pending events, use a QChronoTimer with 0ns + timeout. More advanced idle processing schemes can be achieved using + processEvents(). We recommend that you connect clean-up code to the \l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in your @@ -2810,34 +2562,6 @@ int QApplication::exec() return QGuiApplication::exec(); } -bool QApplicationPrivate::shouldQuit() -{ - /* if there is no non-withdrawn primary window left (except - the ones without QuitOnClose), we emit the lastWindowClosed - signal */ - QWidgetList list = QApplication::topLevelWidgets(); - QWindowList processedWindows; - for (int i = 0; i < list.size(); ++i) { - QWidget *w = list.at(i); - if (QWindow *window = w->windowHandle()) { // Menus, popup widgets may not have a QWindow - processedWindows.push_back(window); - if (w->isVisible() && !w->parentWidget() && w->testAttribute(Qt::WA_QuitOnClose)) - return false; - } - } - return QGuiApplicationPrivate::shouldQuitInternal(processedWindows); -} - -static inline void closeAllPopups() -{ - // Close all popups: In case some popup refuses to close, - // we give up after 1024 attempts (to avoid an infinite loop). - int maxiter = 1024; - QWidget *popup; - while ((popup = QApplication::activePopupWidget()) && maxiter--) - popup->close(); -} - /*! \reimp */ bool QApplication::notify(QObject *receiver, QEvent *e) @@ -2856,64 +2580,14 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QCoreApplicationPrivate::checkReceiverThread(receiver); #endif - if (receiver->isWindowType()) { - if (QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(receiver), e)) - return true; // Platform plugin ate the event + const bool isWindowType = receiver->isWindowType(); + const bool isWidgetType = receiver->isWidgetType(); + if (isWindowType + && QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(receiver), e)) { + return true; // Platform plugin ate the event } - if(e->spontaneous()) { - // Capture the current mouse and keyboard states. Doing so here is - // required in order to support Qt Test synthesized events. Real mouse - // and keyboard state updates from the platform plugin are managed by - // QGuiApplicationPrivate::process(Mouse|Wheel|Key|Touch|Tablet)Event(); - // ### FIXME: Qt Test should not call qapp->notify(), but rather route - // the events through the proper QPA interface. This is required to - // properly generate all other events such as enter/leave etc. - switch (e->type()) { - case QEvent::MouseButtonPress: - { - QMouseEvent *me = static_cast<QMouseEvent*>(e); - QApplicationPrivate::modifier_buttons = me->modifiers(); - QApplicationPrivate::mouse_buttons |= me->button(); - break; - } - case QEvent::MouseButtonDblClick: - { - QMouseEvent *me = static_cast<QMouseEvent*>(e); - QApplicationPrivate::modifier_buttons = me->modifiers(); - QApplicationPrivate::mouse_buttons |= me->button(); - break; - } - case QEvent::MouseButtonRelease: - { - QMouseEvent *me = static_cast<QMouseEvent*>(e); - QApplicationPrivate::modifier_buttons = me->modifiers(); - QApplicationPrivate::mouse_buttons &= ~me->button(); - break; - } - case QEvent::KeyPress: - case QEvent::KeyRelease: - case QEvent::MouseMove: -#if QT_CONFIG(wheelevent) - case QEvent::Wheel: -#endif - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: -#if QT_CONFIG(tabletevent) - case QEvent::TabletMove: - case QEvent::TabletPress: - case QEvent::TabletRelease: -#endif - { - QInputEvent *ie = static_cast<QInputEvent*>(e); - QApplicationPrivate::modifier_buttons = ie->modifiers(); - break; - } - default: - break; - } - } + QGuiApplicationPrivate::captureGlobalModifierState(e); #ifndef QT_NO_GESTURES // walk through parents and check for gestures @@ -2941,7 +2615,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) break; default: if (d->gestureManager->thread() == QThread::currentThread()) { - if (receiver->isWidgetType()) { + if (isWidgetType) { if (d->gestureManager->filterEvent(static_cast<QWidget *>(receiver), e)) return true; } else { @@ -2959,9 +2633,11 @@ bool QApplication::notify(QObject *receiver, QEvent *e) switch (e->type()) { case QEvent::ApplicationDeactivate: + case QEvent::OrientationChange: // Close all popups (triggers when switching applications // by pressing ALT-TAB on Windows, which is not receive as key event. - closeAllPopups(); + // triggers when the screen rotates.) + d->closeAllPopups(); break; case QEvent::Wheel: // User input and window activation makes tooltips sleep case QEvent::ActivationChange: @@ -2976,108 +2652,89 @@ bool QApplication::notify(QObject *receiver, QEvent *e) Q_FALLTHROUGH(); case QEvent::Leave: d->toolTipWakeUp.stop(); + break; default: break; } switch (e->type()) { - case QEvent::KeyPress: { - QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e); - const int key = keyEvent->key(); - // When a key press is received which is not spontaneous then it needs to - // be manually sent as a shortcut override event to ensure that any - // matching shortcut is triggered first. This enables emulation/playback - // of recorded events to still have the same effect. - if (!e->spontaneous() && receiver->isWidgetType()) { - if (qt_sendShortcutOverrideEvent(qobject_cast<QWidget *>(receiver), keyEvent->timestamp(), - key, keyEvent->modifiers(), keyEvent->text(), - keyEvent->isAutoRepeat(), keyEvent->count())) - return true; - } - qt_in_tab_key_event = (key == Qt::Key_Backtab - || key == Qt::Key_Tab - || key == Qt::Key_Left - || key == Qt::Key_Up - || key == Qt::Key_Right - || key == Qt::Key_Down); + case QEvent::KeyPress: { + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e); + const int key = keyEvent->key(); + // When a key press is received which is not spontaneous then it needs to + // be manually sent as a shortcut override event to ensure that any + // matching shortcut is triggered first. This enables emulation/playback + // of recorded events to still have the same effect. + if (!e->spontaneous() && isWidgetType + && qt_sendShortcutOverrideEvent(static_cast<QWidget *>(receiver), keyEvent->timestamp(), + key, keyEvent->modifiers(), keyEvent->text(), + keyEvent->isAutoRepeat(), keyEvent->count())) { + return true; } - default: - break; + qt_in_tab_key_event = (key == Qt::Key_Backtab + || key == Qt::Key_Tab + || key == Qt::Key_Left + || key == Qt::Key_Up + || key == Qt::Key_Right + || key == Qt::Key_Down); + break; + } + default: + break; } bool res = false; - if (!receiver->isWidgetType()) { - res = d->notify_helper(receiver, e); - } else switch (e->type()) { - case QEvent::ShortcutOverride: - case QEvent::KeyPress: - case QEvent::KeyRelease: - { - bool isWidget = receiver->isWidgetType(); -#if QT_CONFIG(graphicsview) - const bool isGraphicsWidget = !isWidget && qobject_cast<QGraphicsWidget *>(receiver); -#endif + if (isWidgetType) { + QWidget * w = static_cast<QWidget *>(receiver); + switch (e->type()) { + case QEvent::ShortcutOverride: + case QEvent::KeyPress: + case QEvent::KeyRelease: { QKeyEvent* key = static_cast<QKeyEvent*>(e); bool def = key->isAccepted(); + /* + QLineEdit will emit a signal on Key_Return, but + ignore the event, and sometimes the connected + slot deletes the QLineEdit (common in itemview + delegates), so we have to check if the widget + was destroyed even if the event was ignored (to + prevent a crash) + + Note that we don't have to reset pr while + propagating (because the original receiver will + be destroyed if one of its ancestors is) + */ QPointer<QObject> pr = receiver; - while (receiver) { + while (w) { if (def) key->accept(); else key->ignore(); - QWidget *w = isWidget ? static_cast<QWidget *>(receiver) : nullptr; -#if QT_CONFIG(graphicsview) - QGraphicsWidget *gw = isGraphicsWidget ? static_cast<QGraphicsWidget *>(receiver) : nullptr; -#endif - res = d->notify_helper(receiver, e); - - if ((res && key->isAccepted()) - /* - QLineEdit will emit a signal on Key_Return, but - ignore the event, and sometimes the connected - slot deletes the QLineEdit (common in itemview - delegates), so we have to check if the widget - was destroyed even if the event was ignored (to - prevent a crash) - - note that we don't have to reset pw while - propagating (because the original receiver will - be destroyed if one of its ancestors is) - */ - || !pr - || (isWidget && (w->isWindow() || !w->parentWidget())) -#if QT_CONFIG(graphicsview) - || (isGraphicsWidget && (gw->isWindow() || !gw->parentWidget())) -#endif - ) { + res = d->notify_helper(w, e); + + if (res && key->isAccepted()) + break; + if (!pr || w->isWindow()) break; - } -#if QT_CONFIG(graphicsview) - receiver = w ? (QObject *)w->parentWidget() : (QObject *)gw->parentWidget(); -#else - receiver = w->parentWidget(); -#endif + w = w->parentWidget(); } qt_in_tab_key_event = false; + break; } - break; - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - { - QWidget* w = static_cast<QWidget *>(receiver); - + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: { QMouseEvent* mouse = static_cast<QMouseEvent*>(e); - QPoint relpos = mouse->pos(); + QPoint relpos = mouse->position().toPoint(); if (e->spontaneous()) { if (e->type() != QEvent::MouseMove) QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos); - // ### Qt 5 These dynamic tool tips should be an OPT-IN feature. Some platforms - // like OS X (probably others too), can optimize their views by not + // ### Qt 7 These dynamic tool tips should be an OPT-IN feature. Some platforms + // like macOS (probably others too), can optimize their views by not // dispatching mouse move events. We have attributes to control hover, // and mouse tracking, but as long as we are deciding to implement this // feature without choice of opting-in or out, you ALWAYS have to have @@ -3087,7 +2744,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) && w->rect().contains(relpos)) { // Outside due to mouse grab? d->toolTipWidget = w; d->toolTipPos = relpos; - d->toolTipGlobalPos = mouse->globalPos(); + d->toolTipGlobalPos = mouse->globalPosition().toPoint(); QStyle *s = d->toolTipWidget->style(); int wakeDelay = s->styleHint(QStyle::SH_ToolTip_WakeUpDelay, nullptr, d->toolTipWidget, nullptr); d->toolTipWakeUp.start(d->toolTipFallAsleep.isActive() ? 20 : wakeDelay, this); @@ -3098,11 +2755,12 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QPointer<QWidget> pw = w; while (w) { - QMouseEvent me(mouse->type(), relpos, mouse->windowPos(), mouse->globalPos(), - mouse->button(), mouse->buttons(), mouse->modifiers(), mouse->source()); - me.spont = mouse->spontaneous(); + QMouseEvent me(mouse->type(), relpos, mouse->scenePosition(), mouse->globalPosition().toPoint(), + mouse->button(), mouse->buttons(), mouse->modifiers(), mouse->source(), + mouse->pointingDevice()); + me.m_spont = mouse->spontaneous(); me.setTimestamp(mouse->timestamp()); - QGuiApplicationPrivate::setMouseEventFlags(&me, mouse->flags()); + QMutableSinglePointEvent::from(me).setDoubleClick(QMutableSinglePointEvent::from(mouse)->isDoubleClick()); // throw away any mouse-tracking-only mouse events if (!w->hasMouseTracking() && mouse->type() == QEvent::MouseMove && mouse->buttons() == 0) { @@ -3112,7 +2770,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } else { w->setAttribute(Qt::WA_NoMouseReplay, false); res = d->notify_helper(w, w == receiver ? mouse : &me); - e->spont = false; + e->m_spont = false; } eventAccepted = (w == receiver ? mouse : &me)->isAccepted(); if (res && eventAccepted) @@ -3130,12 +2788,13 @@ bool QApplication::notify(QObject *receiver, QEvent *e) break; w = static_cast<QWidget *>(receiver); - relpos = mouse->pos(); - QPoint diff = relpos - w->mapFromGlobal(d->hoverGlobalPos); + relpos = mouse->position().toPoint(); + QPoint diff = relpos - w->mapFromGlobal(mouse->globalPosition().toPoint()); while (w) { if (w->testAttribute(Qt::WA_Hover) && (!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) { - QHoverEvent he(QEvent::HoverMove, relpos, relpos - diff, mouse->modifiers()); + QHoverEvent he(QEvent::HoverMove, mouse->scenePosition(), mouse->globalPosition(), relpos - diff, mouse->modifiers()); + QMutableEventPoint::setPosition(he.point(0), relpos); d->notify_helper(w, &he); } if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) @@ -3145,14 +2804,11 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } } - d->hoverGlobalPos = mouse->globalPos(); + d->hoverGlobalPos = mouse->globalPosition().toPoint(); + break; } - break; #if QT_CONFIG(wheelevent) - case QEvent::Wheel: - { - QWidget* w = static_cast<QWidget *>(receiver); - + case QEvent::Wheel: { // QTBUG-40656, QTBUG-42731: ignore wheel events when a popup (QComboBox) is open. if (const QWidget *popup = QApplication::activePopupWidget()) { if (w->window() != popup) @@ -3160,8 +2816,18 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } QWheelEvent* wheel = static_cast<QWheelEvent*>(e); - const bool spontaneous = wheel->spontaneous(); + if (!wheel->spontaneous()) { + /* + Synthesized events shouldn't propagate, e.g. QScrollArea passes events from the + viewport on to the scrollbars, which might ignore the event if there is no more + space to scroll. If we would propagate, the event would come back to the viewport. + */ + res = d->notify_helper(w, wheel); + break; + } + const Qt::ScrollPhase phase = wheel->phase(); + QPoint relpos = wheel->position().toPoint(); // Ideally, we should lock on a widget when it starts receiving wheel // events. This avoids other widgets to start receiving those events @@ -3178,192 +2844,160 @@ bool QApplication::notify(QObject *receiver, QEvent *e) // // We assume that, when supported, the phase cycle follows the pattern: // - // ScrollBegin (ScrollUpdate* ScrollEnd)+ + // ScrollBegin (ScrollUpdate* ScrollMomentum* ScrollEnd)+ // // This means that we can have scrolling sequences (starting with ScrollBegin) // or partial sequences (after a ScrollEnd and starting with ScrollUpdate). - // If wheel_widget is null because it was deleted, we also take the same - // code path as an initial sequence. - if (phase == Qt::NoScrollPhase || phase == Qt::ScrollBegin || !QApplicationPrivate::wheel_widget) { - - // A system-generated ScrollBegin event starts a new user scrolling - // sequence, so we reset wheel_widget in case no one accepts the event - // or if we didn't get (or missed) a ScrollEnd previously. - if (spontaneous && phase == Qt::ScrollBegin) - QApplicationPrivate::wheel_widget = nullptr; - QPoint relpos = wheel->position().toPoint(); + // a widget has already grabbed the wheel for a sequence + if (QApplicationPrivate::wheel_widget) { + Q_ASSERT(phase != Qt::NoScrollPhase); + w = QApplicationPrivate::wheel_widget; + relpos = w->mapFromGlobal(wheel->globalPosition().toPoint()); + } + /* + Start or finish a scrolling sequence by grabbing/releasing the wheel via + wheel_widget. The sequence might be partial (ie. not start with ScrollBegin), + e.g. if the previous wheel_widget was destroyed mid-sequence. + */ + switch (phase) { + case Qt::ScrollEnd: + QApplicationPrivate::wheel_widget = nullptr; + break; + case Qt::ScrollBegin: + QApplicationPrivate::wheel_widget = w; + Q_FALLTHROUGH(); + case Qt::ScrollUpdate: + case Qt::ScrollMomentum: + if (!QApplicationPrivate::wheel_widget) + QApplicationPrivate::wheel_widget = w; + Q_FALLTHROUGH(); + case Qt::NoScrollPhase: + QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos); + break; + // no default: - we want warnings if we don't handle all phases explicitly + } - if (spontaneous && (phase == Qt::NoScrollPhase || phase == Qt::ScrollUpdate)) - QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos); + QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(), + wheel->modifiers(), phase, wheel->inverted(), wheel->source(), wheel->pointingDevice()); -#if QT_DEPRECATED_SINCE(5, 14) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), wheel->angleDelta(), wheel->delta(), wheel->orientation(), wheel->buttons(), - wheel->modifiers(), phase, wheel->source(), wheel->inverted()); -QT_WARNING_POP -#else - QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(), - wheel->modifiers(), phase, wheel->inverted(), wheel->source()); -#endif - we.setTimestamp(wheel->timestamp()); - bool eventAccepted; - do { - we.spont = spontaneous && w == receiver; - we.ignore(); - res = d->notify_helper(w, &we); - eventAccepted = we.isAccepted(); - if (res && eventAccepted) { - // A new scrolling sequence or partial sequence starts and w has accepted - // the event. Therefore, we can set wheel_widget, but only if it's not - // the end of a sequence. - if (spontaneous && (phase == Qt::ScrollBegin || phase == Qt::ScrollUpdate)) - QApplicationPrivate::wheel_widget = w; - break; - } - if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) - break; + we.setTimestamp(wheel->timestamp()); + bool eventAccepted; + do { + // events are delivered as accepted and ignored by the default event handler + // since we always send the same QWheelEvent object, we need to reset the accepted state + we.setAccepted(true); + we.m_spont = wheel->spontaneous() && w == receiver; + res = d->notify_helper(w, &we); + eventAccepted = we.isAccepted(); + if (res && eventAccepted) + break; + if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) + break; - we.p += w->pos(); - w = w->parentWidget(); - } while (w); - wheel->setAccepted(eventAccepted); - } else if (!spontaneous) { - // wheel_widget may forward the wheel event to a delegate widget, - // either directly or indirectly (e.g. QAbstractScrollArea will - // forward to its QScrollBars through viewportEvent()). In that - // case, the event will not be spontaneous but synthesized, so - // we can send it straight to the receiver. - d->notify_helper(w, wheel); - } else { - // The phase is either ScrollUpdate or ScrollEnd, and wheel_widget - // is set. Since it accepted the wheel event previously, we continue - // sending those events until we get a ScrollEnd, which signifies - // the end of the natural scrolling sequence. - const QPoint &relpos = QApplicationPrivate::wheel_widget->mapFromGlobal(wheel->globalPosition().toPoint()); -#if QT_DEPRECATED_SINCE(5, 0) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), wheel->angleDelta(), wheel->delta(), wheel->orientation(), wheel->buttons(), - wheel->modifiers(), wheel->phase(), wheel->source()); -QT_WARNING_POP -#else - QWheelEvent we(relpos, wheel->globalPosition(), wheel->pixelDelta(), wheel->angleDelta(), wheel->buttons(), - wheel->modifiers(), wheel->phase(), wheel->inverted(), wheel->source()); -#endif - we.setTimestamp(wheel->timestamp()); - we.spont = true; - we.ignore(); - d->notify_helper(QApplicationPrivate::wheel_widget, &we); - wheel->setAccepted(we.isAccepted()); - if (phase == Qt::ScrollEnd) - QApplicationPrivate::wheel_widget = nullptr; - } + QMutableEventPoint::setPosition(we.point(0), we.position() + w->pos()); + w = w->parentWidget(); + } while (w); + wheel->setAccepted(eventAccepted); + break; } - break; #endif #ifndef QT_NO_CONTEXTMENU - case QEvent::ContextMenu: - { - QWidget* w = static_cast<QWidget *>(receiver); + case QEvent::ContextMenu: { QContextMenuEvent *context = static_cast<QContextMenuEvent*>(e); QPoint relpos = context->pos(); bool eventAccepted = context->isAccepted(); while (w) { QContextMenuEvent ce(context->reason(), relpos, context->globalPos(), context->modifiers()); - ce.spont = e->spontaneous(); + ce.m_spont = e->spontaneous(); res = d->notify_helper(w, w == receiver ? context : &ce); eventAccepted = ((w == receiver) ? context : &ce)->isAccepted(); - e->spont = false; + e->m_spont = false; - if ((res && eventAccepted) - || w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) + if (res && eventAccepted) + break; + if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) break; relpos += w->pos(); w = w->parentWidget(); } context->setAccepted(eventAccepted); + break; } - break; #endif // QT_NO_CONTEXTMENU #if QT_CONFIG(tabletevent) - case QEvent::TabletMove: - case QEvent::TabletPress: - case QEvent::TabletRelease: - { - QWidget *w = static_cast<QWidget *>(receiver); + case QEvent::TabletMove: + case QEvent::TabletPress: + case QEvent::TabletRelease: { QTabletEvent *tablet = static_cast<QTabletEvent*>(e); - QPointF relpos = tablet->posF(); + QPointF relpos = tablet->position(); bool eventAccepted = tablet->isAccepted(); while (w) { - QTabletEvent te(tablet->type(), relpos, tablet->globalPosF(), - tablet->device(), tablet->pointerType(), + QTabletEvent te(tablet->type(), tablet->pointingDevice(), relpos, tablet->globalPosition(), tablet->pressure(), tablet->xTilt(), tablet->yTilt(), tablet->tangentialPressure(), tablet->rotation(), tablet->z(), - tablet->modifiers(), tablet->uniqueId(), tablet->button(), tablet->buttons()); - te.spont = e->spontaneous(); + tablet->modifiers(), tablet->button(), tablet->buttons()); + te.m_spont = e->spontaneous(); + te.setTimestamp(tablet->timestamp()); te.setAccepted(false); res = d->notify_helper(w, w == receiver ? tablet : &te); eventAccepted = ((w == receiver) ? tablet : &te)->isAccepted(); - e->spont = false; - if ((res && eventAccepted) - || w->isWindow() - || w->testAttribute(Qt::WA_NoMousePropagation)) + e->m_spont = false; + if (res && eventAccepted) + break; + if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) break; relpos += w->pos(); w = w->parentWidget(); } tablet->setAccepted(eventAccepted); + break; } - break; #endif // QT_CONFIG(tabletevent) -#if !defined(QT_NO_TOOLTIP) || QT_CONFIG(whatsthis) - case QEvent::ToolTip: - case QEvent::WhatsThis: - case QEvent::QueryWhatsThis: - { - QWidget* w = static_cast<QWidget *>(receiver); +#if QT_CONFIG(tooltip) || QT_CONFIG(whatsthis) + case QEvent::ToolTip: + case QEvent::WhatsThis: + case QEvent::QueryWhatsThis: { QHelpEvent *help = static_cast<QHelpEvent*>(e); QPoint relpos = help->pos(); bool eventAccepted = help->isAccepted(); while (w) { QHelpEvent he(help->type(), relpos, help->globalPos()); - he.spont = e->spontaneous(); + he.m_spont = e->spontaneous(); res = d->notify_helper(w, w == receiver ? help : &he); - e->spont = false; + e->m_spont = false; eventAccepted = (w == receiver ? help : &he)->isAccepted(); - if ((res && eventAccepted) || w->isWindow()) + if (res && eventAccepted) + break; + if (w->isWindow()) break; relpos += w->pos(); w = w->parentWidget(); } help->setAccepted(eventAccepted); + break; } - break; #endif #if QT_CONFIG(statustip) || QT_CONFIG(whatsthis) - case QEvent::StatusTip: - case QEvent::WhatsThisClicked: - { - QWidget *w = static_cast<QWidget *>(receiver); + case QEvent::StatusTip: + case QEvent::WhatsThisClicked: while (w) { res = d->notify_helper(w, e); - if ((res && e->isAccepted()) || w->isWindow()) + if (res && e->isAccepted()) + break; + if (w->isWindow()) break; w = w->parentWidget(); } - } - break; + break; #endif #if QT_CONFIG(draganddrop) - case QEvent::DragEnter: { - QWidget* w = static_cast<QWidget *>(receiver); + case QEvent::DragEnter: { QDragEnterEvent *dragEvent = static_cast<QDragEnterEvent *>(e); #if QT_CONFIG(graphicsview) // QGraphicsProxyWidget handles its own propagation, @@ -3384,15 +3018,14 @@ QT_WARNING_POP } if (w->isWindow()) break; - dragEvent->p = w->mapToParent(dragEvent->p.toPoint()); + dragEvent->m_pos = w->mapToParent(dragEvent->m_pos); w = w->parentWidget(); } + break; } - break; - case QEvent::DragMove: - case QEvent::Drop: - case QEvent::DragLeave: { - QWidget* w = static_cast<QWidget *>(receiver); + case QEvent::DragMove: + case QEvent::Drop: + case QEvent::DragLeave: { #if QT_CONFIG(graphicsview) // QGraphicsProxyWidget handles its own propagation, // and we must not change QDragManagers currentTarget. @@ -3403,14 +3036,21 @@ QT_WARNING_POP w = qobject_cast<QWidget *>(QDragManager::self()->currentTarget()); if (!w) { + // The widget that received DragEnter didn't accept the event, so we have no + // current drag target in the QDragManager. But DragLeave still needs to be + // dispatched so that enter/leave events are in balance (and so that UnderMouse + // gets cleared). + if (e->type() == QEvent::DragLeave) + w = static_cast<QWidget *>(receiver); + else break; } if (e->type() == QEvent::DragMove || e->type() == QEvent::Drop) { QDropEvent *dragEvent = static_cast<QDropEvent *>(e); - QWidget *origReciver = static_cast<QWidget *>(receiver); - while (origReciver && w != origReciver) { - dragEvent->p = origReciver->mapToParent(dragEvent->p.toPoint()); - origReciver = origReciver->parentWidget(); + QWidget *origReceiver = static_cast<QWidget *>(receiver); + while (origReceiver && w != origReceiver) { + dragEvent->m_pos = origReceiver->mapToParent(dragEvent->m_pos); + origReceiver = origReceiver->parentWidget(); } } res = d->notify_helper(w, e); @@ -3420,111 +3060,101 @@ QT_WARNING_POP #endif ) QDragManager::self()->setCurrentTarget(nullptr, e->type() == QEvent::Drop); + break; } - break; -#endif - case QEvent::TouchBegin: - // Note: TouchUpdate and TouchEnd events are never propagated - { - QWidget *widget = static_cast<QWidget *>(receiver); - QTouchEvent *touchEvent = static_cast<QTouchEvent *>(e); - bool eventAccepted = touchEvent->isAccepted(); - bool acceptTouchEvents = widget->testAttribute(Qt::WA_AcceptTouchEvents); - - if (acceptTouchEvents && e->spontaneous()) { - const QPoint localPos = touchEvent->touchPoints()[0].pos().toPoint(); - QApplicationPrivate::giveFocusAccordingToFocusPolicy(widget, e, localPos); - } +#endif // QT_CONFIG(draganddrop) + case QEvent::TouchBegin: { + // Note: TouchUpdate and TouchEnd events are never propagated + QMutableTouchEvent *touchEvent = QMutableTouchEvent::from(static_cast<QTouchEvent *>(e)); + bool eventAccepted = touchEvent->isAccepted(); + bool acceptTouchEvents = w->testAttribute(Qt::WA_AcceptTouchEvents); + + if (acceptTouchEvents && e->spontaneous() + && touchEvent->device()->type() != QInputDevice::DeviceType::TouchPad) { + const QPoint localPos = touchEvent->points()[0].position().toPoint(); + QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, localPos); + } #ifndef QT_NO_GESTURES - QPointer<QWidget> gesturePendingWidget; + QPointer<QWidget> gesturePendingWidget; #endif - while (widget) { - // first, try to deliver the touch event - acceptTouchEvents = widget->testAttribute(Qt::WA_AcceptTouchEvents); - touchEvent->setTarget(widget); - touchEvent->setAccepted(acceptTouchEvents); - QPointer<QWidget> p = widget; - res = acceptTouchEvents && d->notify_helper(widget, touchEvent); - eventAccepted = touchEvent->isAccepted(); - if (p.isNull()) { - // widget was deleted - widget = nullptr; - } else { - widget->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent, res && eventAccepted); - } - touchEvent->spont = false; - if (res && eventAccepted) { - // the first widget to accept the TouchBegin gets an implicit grab. - d->activateImplicitTouchGrab(widget, touchEvent); - break; - } + while (w) { + // first, try to deliver the touch event + acceptTouchEvents = w->testAttribute(Qt::WA_AcceptTouchEvents); + touchEvent->setTarget(w); + touchEvent->setAccepted(acceptTouchEvents); + QPointer<QWidget> p = w; + res = acceptTouchEvents && d->notify_helper(w, touchEvent); + eventAccepted = touchEvent->isAccepted(); + if (p.isNull()) { + // widget was deleted + w = nullptr; + } else { + w->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent, res && eventAccepted); + } + touchEvent->m_spont = false; + if (res && eventAccepted) { + // the first widget to accept the TouchBegin gets an implicit grab. + d->activateImplicitTouchGrab(w, touchEvent); + break; + } #ifndef QT_NO_GESTURES - if (gesturePendingWidget.isNull() && widget && QGestureManager::gesturePending(widget)) - gesturePendingWidget = widget; + if (gesturePendingWidget.isNull() && w && QGestureManager::gesturePending(w)) + gesturePendingWidget = w; #endif - if (p.isNull() || widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) - break; + if (!w || w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) + break; - QPoint offset = widget->pos(); - widget = widget->parentWidget(); - touchEvent->setTarget(widget); - for (int i = 0; i < touchEvent->_touchPoints.size(); ++i) { - QTouchEvent::TouchPoint &pt = touchEvent->_touchPoints[i]; - pt.d->pos = pt.pos() + offset; - pt.d->startPos = pt.startPos() + offset; - pt.d->lastPos = pt.lastPos() + offset; + const QPoint offset = w->pos(); + w = w->parentWidget(); + touchEvent->setTarget(w); + for (int i = 0; i < touchEvent->pointCount(); ++i) { + auto &pt = touchEvent->point(i); + QMutableEventPoint::setPosition(pt, pt.position() + offset); + } } - } #ifndef QT_NO_GESTURES - if (!eventAccepted && !gesturePendingWidget.isNull()) { - // the first widget subscribed to a gesture gets an implicit grab - d->activateImplicitTouchGrab(gesturePendingWidget, touchEvent); - } + if (!eventAccepted && !gesturePendingWidget.isNull()) { + // the first widget subscribed to a gesture gets an implicit grab for all + // points, also for events and event points that have not been accepted. + d->activateImplicitTouchGrab(gesturePendingWidget, touchEvent, QApplicationPrivate::GrabAllPoints); + } #endif - touchEvent->setAccepted(eventAccepted); - break; - } - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - { - QWidget *widget = static_cast<QWidget *>(receiver); - // We may get here if the widget is subscribed to a gesture, - // but has not accepted TouchBegin. Propagate touch events - // only if TouchBegin has been accepted. - if (widget->testAttribute(Qt::WA_WState_AcceptedTouchBeginEvent)) - res = d->notify_helper(widget, e); - break; - } - case QEvent::RequestSoftwareInputPanel: - inputMethod()->show(); - break; - case QEvent::CloseSoftwareInputPanel: - inputMethod()->hide(); - break; + touchEvent->setAccepted(eventAccepted); + break; + } + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + // We may get here if the widget is subscribed to a gesture, + // but has not accepted TouchBegin. Propagate touch events + // only if TouchBegin has been accepted. + if (w->testAttribute(Qt::WA_WState_AcceptedTouchBeginEvent)) + res = d->notify_helper(w, e); + break; + case QEvent::RequestSoftwareInputPanel: + inputMethod()->show(); + break; + case QEvent::CloseSoftwareInputPanel: + inputMethod()->hide(); + break; #ifndef QT_NO_GESTURES - case QEvent::NativeGesture: - { - // only propagate the first gesture event (after the GID_BEGIN) - QWidget *w = static_cast<QWidget *>(receiver); - while (w) { - e->ignore(); - res = d->notify_helper(w, e); - if ((res && e->isAccepted()) || w->isWindow()) - break; - w = w->parentWidget(); - } - break; - } - case QEvent::Gesture: - case QEvent::GestureOverride: - { - if (receiver->isWidgetType()) { - QWidget *w = static_cast<QWidget *>(receiver); + case QEvent::NativeGesture: + while (w) { + e->ignore(); + res = d->notify_helper(w, e); + if (res && e->isAccepted()) + break; + if (w->isWindow()) + break; + w = w->parentWidget(); + } + break; + case QEvent::Gesture: + case QEvent::GestureOverride: { QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(e); QList<QGesture *> allGestures = gestureEvent->gestures(); @@ -3541,7 +3171,7 @@ QT_WARNING_POP wd->gestureContext.find(type); bool deliver = contextit != wd->gestureContext.end() && (g->state() == Qt::GestureStarted || w == receiver || - (contextit.value() & Qt::ReceivePartialGestures)); + (contextit.value() & Qt::ReceivePartialGestures)); if (deliver) { allGestures.removeAt(i); gestures.append(g); @@ -3552,11 +3182,11 @@ QT_WARNING_POP if (!gestures.isEmpty()) { // we have gestures for this w QGestureEvent ge(gestures); ge.t = gestureEvent->t; - ge.spont = gestureEvent->spont; + ge.m_spont = gestureEvent->spontaneous(); ge.m_accept = wasAccepted; ge.m_accepted = gestureEvent->m_accepted; res = d->notify_helper(w, &ge); - gestureEvent->spont = false; + gestureEvent->m_spont = false; eventAccepted = ge.isAccepted(); for (int i = 0; i < gestures.size(); ++i) { QGesture *g = gestures.at(i); @@ -3579,46 +3209,40 @@ QT_WARNING_POP break; w = w->parentWidget(); } - for (QGesture *g : qAsConst(allGestures)) + for (QGesture *g : std::as_const(allGestures)) gestureEvent->setAccepted(g, false); gestureEvent->m_accept = false; // to make sure we check individual gestures - } else { - res = d->notify_helper(receiver, e); + break; } - break; - } #endif // QT_NO_GESTURES #ifdef Q_OS_MAC - // Enable touch events on enter, disable on leave. - typedef void (*RegisterTouchWindowFn)(QWindow *, bool); - case QEvent::Enter: - if (receiver->isWidgetType()) { - QWidget *w = static_cast<QWidget *>(receiver); + // Enable touch events on enter, disable on leave. + typedef void (*RegisterTouchWindowFn)(QWindow *, bool); + case QEvent::Enter: if (w->testAttribute(Qt::WA_AcceptTouchEvents)) { RegisterTouchWindowFn registerTouchWindow = reinterpret_cast<RegisterTouchWindowFn> (platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow")); if (registerTouchWindow) registerTouchWindow(w->window()->windowHandle(), true); } - } - res = d->notify_helper(receiver, e); - break; - case QEvent::Leave: - if (receiver->isWidgetType()) { - QWidget *w = static_cast<QWidget *>(receiver); + res = d->notify_helper(receiver, e); + break; + case QEvent::Leave: if (w->testAttribute(Qt::WA_AcceptTouchEvents)) { RegisterTouchWindowFn registerTouchWindow = reinterpret_cast<RegisterTouchWindowFn> (platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow")); if (registerTouchWindow) registerTouchWindow(w->window()->windowHandle(), false); } - } - res = d->notify_helper(receiver, e); - break; + res = d->notify_helper(receiver, e); + break; #endif - default: + default: + res = d->notify_helper(receiver, e); + break; + } + } else { res = d->notify_helper(receiver, e); - break; } return res; @@ -3675,11 +3299,12 @@ bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) bool QApplicationPrivate::inPopupMode() { - return QApplicationPrivate::popupWidgets != nullptr; + return QGuiApplicationPrivate::activePopupWindow() != nullptr; } static void ungrabKeyboardForPopup(QWidget *popup) { + qCDebug(lcWidgetPopup) << "ungrab keyboard for" << popup; if (QWidget::keyboardGrabber()) qt_widget_private(QWidget::keyboardGrabber())->stealKeyboardGrab(true); else @@ -3688,6 +3313,7 @@ static void ungrabKeyboardForPopup(QWidget *popup) static void ungrabMouseForPopup(QWidget *popup) { + qCDebug(lcWidgetPopup) << "ungrab mouse for" << popup; if (QWidget::mouseGrabber()) qt_widget_private(QWidget::mouseGrabber())->stealMouseGrab(true); else @@ -3707,38 +3333,23 @@ static void grabForPopup(QWidget *popup) ungrabKeyboardForPopup(popup); } } + qCDebug(lcWidgetPopup) << "grabbed mouse and keyboard?" << popupGrabOk << "for popup" << popup; } -extern QWidget *qt_popup_down; -extern bool qt_replay_popup_mouse_event; - void QApplicationPrivate::closePopup(QWidget *popup) { - if (!popupWidgets) + QWindow *win = popup->windowHandle(); + if (!win) + return; + if (!QGuiApplicationPrivate::closePopup(win)) return; - popupWidgets->removeAll(popup); - - if (popup == qt_popup_down) { - qt_button_down = nullptr; - qt_popup_down = nullptr; - } - if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup - delete QApplicationPrivate::popupWidgets; - QApplicationPrivate::popupWidgets = nullptr; + const QWindow *nextRemainingPopup = QGuiApplicationPrivate::activePopupWindow(); + if (!nextRemainingPopup) { // this was the last popup if (popupGrabOk) { popupGrabOk = false; - if (popup->geometry().contains(QPoint(QGuiApplicationPrivate::mousePressX, - QGuiApplicationPrivate::mousePressY)) - || popup->testAttribute(Qt::WA_NoMouseReplay)) { - // mouse release event or inside - qt_replay_popup_mouse_event = false; - } else { // mouse press event - qt_replay_popup_mouse_event = true; - } - // transfer grab back to mouse grabber if any, otherwise release the grab ungrabMouseForPopup(popup); @@ -3757,30 +3368,23 @@ void QApplicationPrivate::closePopup(QWidget *popup) } } - } else { + } else if (const auto *popupWin = qobject_cast<const QWidgetWindow *>(nextRemainingPopup)) { // A popup was closed, so the previous popup gets the focus. - QWidget* aw = QApplicationPrivate::popupWidgets->constLast(); - if (QWidget *fw = aw->focusWidget()) + if (QWidget *fw = popupWin->widget()->focusWidget()) fw->setFocus(Qt::PopupFocusReason); // can become nullptr due to setFocus() above - if (QApplicationPrivate::popupWidgets && - QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard - grabForPopup(aw); + if (QGuiApplicationPrivate::popupCount() == 1) // grab mouse/keyboard + grabForPopup(popupWin->widget()); } } -int openPopupCount = 0; - void QApplicationPrivate::openPopup(QWidget *popup) { - openPopupCount++; - if (!popupWidgets) // create list - popupWidgets = new QWidgetList; - popupWidgets->append(popup); // add to end of list + QGuiApplicationPrivate::activatePopup(popup->windowHandle()); - if (QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard + if (QGuiApplicationPrivate::popupCount() == 1) // grab mouse/keyboard grabForPopup(popup); // popups are not focus-handled by the window system (the first @@ -3788,7 +3392,7 @@ void QApplicationPrivate::openPopup(QWidget *popup) // new popup gets the focus if (popup->focusWidget()) { popup->focusWidget()->setFocus(Qt::PopupFocusReason); - } else if (popupWidgets->count() == 1) { // this was the first popup + } else if (QGuiApplicationPrivate::popupCount() == 1) { // this was the first popup if (QWidget *fw = QApplication::focusWidget()) { QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); QCoreApplication::sendEvent(fw, &e); @@ -3820,51 +3424,6 @@ Qt::NavigationMode QApplication::navigationMode() { return QApplicationPrivate::navigationMode; } - -# if QT_DEPRECATED_SINCE(5, 13) -/*! - Sets whether Qt should use focus navigation suitable for use with a - minimal keypad. - - This feature is available in Qt for Embedded Linux, and Windows CE only. - - \note On Windows CE this feature is disabled by default for touch device - mkspecs. To enable keypad navigation, build Qt with - QT_KEYPAD_NAVIGATION defined. - - \deprecated - - \sa setNavigationMode() -*/ -void QApplication::setKeypadNavigationEnabled(bool enable) -{ - if (enable) { - QApplication::setNavigationMode(Qt::NavigationModeKeypadTabOrder); - } else { - QApplication::setNavigationMode(Qt::NavigationModeNone); - } -} - -/*! - Returns \c true if Qt is set to use keypad navigation; otherwise returns - false. The default value is false. - - This feature is available in Qt for Embedded Linux, and Windows CE only. - - \note On Windows CE this feature is disabled by default for touch device - mkspecs. To enable keypad navigation, build Qt with - QT_KEYPAD_NAVIGATION defined. - - \deprecated - - \sa navigationMode() -*/ -bool QApplication::keypadNavigationEnabled() -{ - return QApplicationPrivate::navigationMode == Qt::NavigationModeKeypadTabOrder || - QApplicationPrivate::navigationMode == Qt::NavigationModeKeypadDirectional; -} -# endif #endif /*! @@ -3872,7 +3431,7 @@ bool QApplication::keypadNavigationEnabled() \since 4.3 Causes an alert to be shown for \a widget if the window is not the active - window. The alert is shown for \a msec miliseconds. If \a msec is zero (the + window. The alert is shown for \a msec milliseconds. If \a msec is zero (the default), then the alert is shown indefinitely until the window becomes active again. @@ -4163,18 +3722,12 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) bool QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent) { bool containsPress = false; - for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { - QTouchEvent::TouchPoint &touchPoint = touchEvent->_touchPoints[i]; - - // preserve the sub-pixel resolution - const QPointF screenPos = touchPoint.screenRect().center(); - const QPointF delta = screenPos - screenPos.toPoint(); - touchPoint.d->pos = widget->mapFromGlobal(screenPos.toPoint()) + delta; - touchPoint.d->startPos = widget->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta; - touchPoint.d->lastPos = widget->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta; + for (int i = 0; i < touchEvent->pointCount(); ++i) { + auto &pt = touchEvent->point(i); + QMutableEventPoint::setPosition(pt, widget->mapFromGlobal(pt.globalPosition())); - if (touchPoint.state() == Qt::TouchPointPressed) + if (pt.state() == QEventPoint::State::Pressed) containsPress = true; } return containsPress; @@ -4198,111 +3751,111 @@ void QApplicationPrivate::cleanupMultitouch_sys() { } -QWidget *QApplicationPrivate::findClosestTouchPointTarget(QTouchDevice *device, const QTouchEvent::TouchPoint &touchPoint) +QWidget *QApplicationPrivate::findClosestTouchPointTarget(const QPointingDevice *device, const QEventPoint &touchPoint) { - const QPointF screenPos = touchPoint.screenPos(); + const QPointF globalPos = touchPoint.globalPosition(); int closestTouchPointId = -1; QObject *closestTarget = nullptr; - qreal closestDistance = qreal(0.); - QHash<ActiveTouchPointsKey, ActiveTouchPointsValue>::const_iterator it = activeTouchPoints.constBegin(), - ite = activeTouchPoints.constEnd(); - while (it != ite) { - if (it.key().device == device && it.key().touchPointId != touchPoint.id()) { - const QTouchEvent::TouchPoint &touchPoint = it->touchPoint; - qreal dx = screenPos.x() - touchPoint.screenPos().x(); - qreal dy = screenPos.y() - touchPoint.screenPos().y(); + qreal closestDistance = 0; + const QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(device); + for (auto &epd : devPriv->activePoints.values()) { + const auto &pt = epd.eventPoint; + if (pt.id() != touchPoint.id()) { + qreal dx = globalPos.x() - pt.globalPosition().x(); + qreal dy = globalPos.y() - pt.globalPosition().y(); qreal distance = dx * dx + dy * dy; if (closestTouchPointId == -1 || distance < closestDistance) { - closestTouchPointId = touchPoint.id(); + closestTouchPointId = pt.id(); closestDistance = distance; - closestTarget = it.value().target.data(); + closestTarget = QMutableEventPoint::target(pt); } } - ++it; } return static_cast<QWidget *>(closestTarget); } -void QApplicationPrivate::activateImplicitTouchGrab(QWidget *widget, QTouchEvent *touchEvent) +void QApplicationPrivate::activateImplicitTouchGrab(QWidget *widget, QTouchEvent *touchEvent, + ImplicitTouchGrabMode grabMode) { if (touchEvent->type() != QEvent::TouchBegin) return; - for (int i = 0, tc = touchEvent->touchPoints().count(); i < tc; ++i) { - const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i); - activeTouchPoints[QGuiApplicationPrivate::ActiveTouchPointsKey(touchEvent->device(), touchPoint.id())].target = widget; + // If the widget dispatched the event further (see QGraphicsProxyWidget), then + // there might already be an implicit grabber. Don't override that. A widget that + // has partially recognized a gesture needs to grab all points. + for (int i = 0; i < touchEvent->pointCount(); ++i) { + auto &ep = touchEvent->point(i); + if (!QMutableEventPoint::target(ep) && (ep.isAccepted() || grabMode == GrabAllPoints)) + QMutableEventPoint::setTarget(ep, widget); } + // TODO setExclusiveGrabber() to be consistent with Qt Quick? } -bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, - QTouchDevice *device, - const QList<QTouchEvent::TouchPoint> &touchPoints, - ulong timestamp) +bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, const QTouchEvent *te) { QApplicationPrivate *d = self; - typedef QPair<Qt::TouchPointStates, QList<QTouchEvent::TouchPoint> > StatesAndTouchPoints; + // TODO get rid of this QPair + typedef QPair<QEventPoint::State, QList<QEventPoint> > StatesAndTouchPoints; QHash<QWidget *, StatesAndTouchPoints> widgetsNeedingEvents; - for (int i = 0; i < touchPoints.count(); ++i) { - QTouchEvent::TouchPoint touchPoint = touchPoints.at(i); - // explicitly detach from the original touch point that we got, so even - // if the touchpoint structs are reused, we will make a copy that we'll - // deliver to the user (which might want to store the struct for later use). - touchPoint.d = touchPoint.d->detach(); - + const auto *device = te->pointingDevice(); + auto touchPoints = te->points(); // touch points will be mutated + for (auto &touchPoint : touchPoints) { // update state QPointer<QObject> target; - ActiveTouchPointsKey touchInfoKey(device, touchPoint.id()); - ActiveTouchPointsValue &touchInfo = d->activeTouchPoints[touchInfoKey]; - if (touchPoint.state() == Qt::TouchPointPressed) { - if (device->type() == QTouchDevice::TouchPad) { - // on touch-pads, send all touch points to the same widget - target = d->activeTouchPoints.isEmpty() - ? QPointer<QObject>() - : d->activeTouchPoints.constBegin().value().target; + if (touchPoint.state() == QEventPoint::State::Pressed) { + if (device->type() == QInputDevice::DeviceType::TouchPad) { + // on touchpads, send all touch points to the same widget: + // pick the first non-null target if possible + target = QPointingDevicePrivate::get(device)->firstActiveTarget(); } - if (!target) { + if (target.isNull()) { // determine which widget this event will go to if (!window) - window = QApplication::topLevelAt(touchPoint.screenPos().toPoint()); + window = QApplication::topLevelAt(touchPoint.globalPosition().toPoint()); if (!window) continue; - target = window->childAt(window->mapFromGlobal(touchPoint.screenPos().toPoint())); + target = window->childAt(window->mapFromGlobal(touchPoint.globalPosition().toPoint())); if (!target) target = window; } - if (device->type() == QTouchDevice::TouchScreen) { + bool usingClosestWidget = false; + if (device->type() == QInputDevice::DeviceType::TouchScreen) { QWidget *closestWidget = d->findClosestTouchPointTarget(device, touchPoint); QWidget *widget = static_cast<QWidget *>(target.data()); if (closestWidget && (widget->isAncestorOf(closestWidget) || closestWidget->isAncestorOf(widget))) { target = closestWidget; + usingClosestWidget = true; } } - touchInfo.target = target; + // on touch pads, implicitly grab all touch points + // on touch screens, grab touch points that are redirected to the closest widget + if (device->type() == QInputDevice::DeviceType::TouchPad || usingClosestWidget) + QMutableEventPoint::setTarget(touchPoint, target); } else { - target = touchInfo.target; + target = QMutableEventPoint::target(touchPoint); if (!target) continue; } - Q_ASSERT(target.data() != nullptr); + Q_ASSERT(!target.isNull()); QWidget *targetWidget = static_cast<QWidget *>(target.data()); -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS // Single-touch events are normally not sent unless WA_TouchPadAcceptSingleTouchEvents is set. // In Qt 4 this check was in OS X-only code. That behavior is preserved here by the #ifdef. if (touchPoints.count() == 1 - && device->type() == QTouchDevice::TouchPad + && device->type() == QInputDevice::DeviceType::TouchPad && !targetWidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents)) continue; #endif StatesAndTouchPoints &maskAndPoints = widgetsNeedingEvents[targetWidget]; - maskAndPoints.first |= touchPoint.state(); + maskAndPoints.first = QEventPoint::State(maskAndPoints.first | touchPoint.state()); maskAndPoints.second.append(touchPoint); } @@ -4319,13 +3872,13 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, QEvent::Type eventType; switch (it.value().first) { - case Qt::TouchPointPressed: + case QEventPoint::State::Pressed: eventType = QEvent::TouchBegin; break; - case Qt::TouchPointReleased: + case QEventPoint::State::Released: eventType = QEvent::TouchEnd; break; - case Qt::TouchPointStationary: + case QEventPoint::State::Stationary: // don't send the event if nothing changed continue; default: @@ -4333,14 +3886,10 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, break; } - QTouchEvent touchEvent(eventType, - device, - QGuiApplication::keyboardModifiers(), - it.value().first, - it.value().second); + QMutableTouchEvent touchEvent(eventType, device, QGuiApplication::keyboardModifiers(), + it.value().second); bool containsPress = updateTouchPointsForWidget(widget, &touchEvent); - touchEvent.setTimestamp(timestamp); - touchEvent.setWindow(window->windowHandle()); + touchEvent.setTimestamp(te->timestamp()); touchEvent.setTarget(widget); if (containsPress) @@ -4351,7 +3900,9 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, { // if the TouchBegin handler recurses, we assume that means the event // has been implicitly accepted and continue to send touch events - if (QApplication::sendSpontaneousEvent(widget, &touchEvent) && touchEvent.isAccepted()) { + bool res = te->spontaneous() ? QApplication::sendSpontaneousEvent(widget, &touchEvent) + : QApplication::sendEvent(widget, &touchEvent); + if (res && touchEvent.isAccepted()) { accepted = true; if (!widget.isNull()) widget->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent); @@ -4364,7 +3915,9 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, || QGestureManager::gesturePending(widget) #endif ) { - if (QApplication::sendSpontaneousEvent(widget, &touchEvent) && touchEvent.isAccepted()) + bool res = te->spontaneous() ? QApplication::sendSpontaneousEvent(widget, &touchEvent) + : QApplication::sendEvent(widget, &touchEvent); + if (res && touchEvent.isAccepted()) accepted = true; // widget can be deleted on TouchEnd if (touchEvent.type() == QEvent::TouchEnd && !widget.isNull()) @@ -4376,31 +3929,30 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, return accepted; } -void QApplicationPrivate::translateTouchCancel(QTouchDevice *device, ulong timestamp) +void QApplicationPrivate::translateTouchCancel(const QPointingDevice *device, ulong timestamp) { - QTouchEvent touchEvent(QEvent::TouchCancel, device, QGuiApplication::keyboardModifiers()); + QMutableTouchEvent touchEvent(QEvent::TouchCancel, device, QGuiApplication::keyboardModifiers()); touchEvent.setTimestamp(timestamp); - QHash<ActiveTouchPointsKey, ActiveTouchPointsValue>::const_iterator it - = self->activeTouchPoints.constBegin(), ite = self->activeTouchPoints.constEnd(); + QSet<QWidget *> widgetsNeedingCancel; - while (it != ite) { - QWidget *widget = static_cast<QWidget *>(it->target.data()); - if (widget) - widgetsNeedingCancel.insert(widget); - ++it; + const QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(device); + for (auto &epd : devPriv->activePoints.values()) { + const auto &pt = epd.eventPoint; + QObject *target = QMutableEventPoint::target(pt); + if (target && target->isWidgetType()) + widgetsNeedingCancel.insert(static_cast<QWidget *>(target)); } for (QSet<QWidget *>::const_iterator widIt = widgetsNeedingCancel.constBegin(), widItEnd = widgetsNeedingCancel.constEnd(); widIt != widItEnd; ++widIt) { QWidget *widget = *widIt; - touchEvent.setWindow(widget->windowHandle()); touchEvent.setTarget(widget); QApplication::sendSpontaneousEvent(widget, &touchEvent); } } -void QApplicationPrivate::notifyThemeChanged() +void QApplicationPrivate::handleThemeChanged() { - QGuiApplicationPrivate::notifyThemeChanged(); + QGuiApplicationPrivate::handleThemeChanged(); qt_init_tooltip_palette(); } @@ -4437,6 +3989,11 @@ QPixmap QApplicationPrivate::applyQIconStyleHelper(QIcon::Mode mode, const QPixm return QApplication::style()->generatedIconPixmap(mode, base, &opt); } +void *QApplication::resolveInterface(const char *name, int revision) const +{ + return QGuiApplication::resolveInterface(name, revision); +} + QT_END_NAMESPACE #include "moc_qapplication.cpp" |