diff options
Diffstat (limited to 'src/gui/kernel/qguiapplication.cpp')
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 308 |
1 files changed, 226 insertions, 82 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index fe40e05536..9ef61ef4fa 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -9,6 +9,7 @@ #include <qpa/qplatformintegrationfactory_p.h> #include "private/qevent_p.h" #include "private/qeventpoint_p.h" +#include "private/qiconloader_p.h" #include "qfont.h" #include "qpointingdevice.h" #include <qpa/qplatformfontdatabase.h> @@ -16,6 +17,7 @@ #include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformtheme.h> #include <qpa/qplatformintegration.h> +#include <qpa/qplatformkeymapper.h> #include <QtCore/QAbstractEventDispatcher> #include <QtCore/QFileInfo> @@ -23,6 +25,7 @@ #include <QtCore/QVariant> #include <QtCore/private/qcoreapplication_p.h> #include <QtCore/private/qabstracteventdispatcher_p.h> +#include <QtCore/private/qminimalflatset_p.h> #include <QtCore/qmutex.h> #include <QtCore/private/qthread_p.h> #include <QtCore/private/qlocking_p.h> @@ -104,6 +107,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcPopup, "qt.gui.popup"); + using namespace Qt::StringLiterals; using namespace QtMiscUtils; @@ -177,12 +182,15 @@ Q_CONSTINIT QClipboard *QGuiApplicationPrivate::qt_clipboard = nullptr; Q_CONSTINIT QList<QScreen *> QGuiApplicationPrivate::screen_list; Q_CONSTINIT QWindowList QGuiApplicationPrivate::window_list; +Q_CONSTINIT QWindowList QGuiApplicationPrivate::popup_list; +Q_CONSTINIT const QWindow *QGuiApplicationPrivate::active_popup_on_press = nullptr; Q_CONSTINIT QWindow *QGuiApplicationPrivate::focus_window = nullptr; Q_CONSTINIT static QBasicMutex applicationFontMutex; Q_CONSTINIT QFont *QGuiApplicationPrivate::app_font = nullptr; Q_CONSTINIT QStyleHints *QGuiApplicationPrivate::styleHints = nullptr; Q_CONSTINIT bool QGuiApplicationPrivate::obey_desktop_settings = true; +Q_CONSTINIT bool QGuiApplicationPrivate::popup_closed_on_press = false; Q_CONSTINIT QInputDeviceManager *QGuiApplicationPrivate::m_inputDeviceManager = nullptr; @@ -585,13 +593,10 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME \c none disables them. \li \c {fontengine=freetype}, uses the FreeType font engine. - \li \c {fontengine=directwrite}, uses the experimental DirectWrite - font database and defaults to using the DirectWrite font + \li \c {fontengine=gdi}, uses the legacy GDI-based + font database and defaults to using the GDI font engine (which is otherwise only used for some font types - or font properties.) This affects font selection and aims - to provide font naming more consistent with other platforms, - but does not support all font formats, such as Postscript - Type-1 or Microsoft FNT fonts. + or font properties.) (Since Qt 6.8). \li \c {menus=[native|none]}, controls the use of native menus. Native menus are implemented using Win32 API and are simpler than @@ -605,7 +610,8 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME \li \c {nocolorfonts} Turn off DirectWrite Color fonts (since Qt 5.8). - \li \c {nodirectwrite} Turn off DirectWrite fonts (since Qt 5.8). + \li \c {nodirectwrite} Turn off DirectWrite fonts (since Qt 5.8). This implicitly + also selects the GDI font engine. \li \c {nomousefromtouch} Ignores mouse events synthesized from touch events by the operating system. @@ -828,7 +834,7 @@ QWindow *QGuiApplication::modalWindow() CHECK_QAPP_INSTANCE(nullptr) if (QGuiApplicationPrivate::self->modalWindowList.isEmpty()) return nullptr; - return QGuiApplicationPrivate::self->modalWindowList.first(); + return QGuiApplicationPrivate::self->modalWindowList.constFirst(); } static void updateBlockedStatusRecursion(QWindow *window, bool shouldBeBlocked) @@ -959,6 +965,43 @@ bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blocking return false; } +QWindow *QGuiApplicationPrivate::activePopupWindow() +{ + // might be the same as focusWindow() if that's a popup + return QGuiApplicationPrivate::popup_list.isEmpty() ? + nullptr : QGuiApplicationPrivate::popup_list.constLast(); +} + +void QGuiApplicationPrivate::activatePopup(QWindow *popup) +{ + if (!popup->isVisible()) + return; + popup_list.removeOne(popup); // ensure that there's only one entry, and it's the last + qCDebug(lcPopup) << "appending popup" << popup << "to existing" << popup_list; + popup_list.append(popup); +} + +bool QGuiApplicationPrivate::closePopup(QWindow *popup) +{ + const auto removed = QGuiApplicationPrivate::popup_list.removeAll(popup); + qCDebug(lcPopup) << "removed?" << removed << "popup" << popup << "; remaining" << popup_list; + return removed; // >= 1 if something was removed +} + +/*! + Returns \c true if there are no more open popups. +*/ +bool QGuiApplicationPrivate::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; + QWindow *popup; + while ((popup = activePopupWindow()) && maxiter--) + popup->close(); // this will call QApplicationPrivate::closePopup + return QGuiApplicationPrivate::popup_list.isEmpty(); +} + /*! Returns the QWindow that receives events tied to focus, such as key events. @@ -1193,14 +1236,26 @@ QWindow *QGuiApplication::topLevelAt(const QPoint &pos) \li \c xcb is a plugin for the X11 window system, used on some desktop Linux platforms. \endlist + \note Calling this function without a QGuiApplication will return the default + platform name, if available. The default platform name is not affected by the + \c{-platform} command line option, or the \c QT_QPA_PLATFORM environment variable. + For more information about the platform plugins for embedded Linux devices, see \l{Qt for Embedded Linux}. */ QString QGuiApplication::platformName() { - return QGuiApplicationPrivate::platform_name ? - *QGuiApplicationPrivate::platform_name : QString(); + if (!QGuiApplication::instance()) { +#ifdef QT_QPA_DEFAULT_PLATFORM_NAME + return QStringLiteral(QT_QPA_DEFAULT_PLATFORM_NAME); +#else + return QString(); +#endif + } else { + return QGuiApplicationPrivate::platform_name ? + *QGuiApplicationPrivate::platform_name : QString(); + } } Q_LOGGING_CATEGORY(lcQpaPluginLoading, "qt.qpa.plugin"); @@ -1235,6 +1290,10 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) { if (availablePlugins.contains(name)) { + if (name == QStringLiteral("xcb") && QVersionNumber::compare(QLibraryInfo::version(), QVersionNumber(6, 5, 0)) >= 0) { + qCWarning(lcQpaPluginLoading).nospace().noquote() + << "From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin."; + } qCInfo(lcQpaPluginLoading).nospace().noquote() << "Could not load the Qt platform plugin \"" << name << "\" in \"" << QDir::toNativeSeparators(platformPluginPath) << "\" even though it was found."; @@ -1514,7 +1573,7 @@ void QGuiApplicationPrivate::createPlatformIntegration() init_platform(QLatin1StringView(platformName), platformPluginPath, platformThemeName, argc, argv); if (const QPlatformTheme *theme = platformTheme()) - QStyleHintsPrivate::get(QGuiApplication::styleHints())->setColorScheme(theme->colorScheme()); + QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(theme->colorScheme()); if (!icon.isEmpty()) forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon); @@ -1768,6 +1827,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() platform_integration = nullptr; window_list.clear(); + popup_list.clear(); screen_list.clear(); self = nullptr; @@ -1830,7 +1890,7 @@ Qt::KeyboardModifiers QGuiApplication::queryKeyboardModifiers() { CHECK_QAPP_INSTANCE(Qt::KeyboardModifiers{}) QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration(); - return pi->queryKeyboardModifiers(); + return pi->keyMapper()->queryKeyboardModifiers(); } /*! @@ -1888,9 +1948,10 @@ QFunctionPointer QGuiApplication::platformFunction(const QByteArray &function) Generally, no user interaction can take place before calling exec(). - To make your application perform idle processing, e.g., 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(). + To make your application perform idle processing, e.g., executing a + special 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 @@ -1980,7 +2041,8 @@ bool QGuiApplication::notify(QObject *object, QEvent *event) */ bool QGuiApplication::event(QEvent *e) { - if (e->type() == QEvent::LanguageChange) { + switch (e->type()) { + case QEvent::LanguageChange: // if the layout direction was set explicitly, then don't override it here if (layout_direction == Qt::LayoutDirectionAuto) setLayoutDirection(layout_direction); @@ -1988,13 +2050,15 @@ bool QGuiApplication::event(QEvent *e) if (topLevelWindow->flags() != Qt::Desktop) postEvent(topLevelWindow, new QEvent(QEvent::LanguageChange)); } - } else if (e->type() == QEvent::ApplicationFontChange || - e->type() == QEvent::ApplicationPaletteChange) { + break; + case QEvent::ApplicationFontChange: + case QEvent::ApplicationPaletteChange: for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) { if (topLevelWindow->flags() != Qt::Desktop) postEvent(topLevelWindow, new QEvent(e->type())); } - } else if (e->type() == QEvent::Quit) { + break; + case QEvent::Quit: // Close open windows. This is done in order to deliver de-expose // events while the event loop is still running. for (QWindow *topLevelWindow : QGuiApplication::topLevelWindows()) { @@ -2006,8 +2070,10 @@ bool QGuiApplication::event(QEvent *e) return true; } } + break; + default: + break; } - return QCoreApplication::event(e); } @@ -2065,8 +2131,8 @@ void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::processWindowSystemEvent( case QWindowSystemInterfacePrivate::Leave: QGuiApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e)); break; - case QWindowSystemInterfacePrivate::ActivatedWindow: - QGuiApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e)); + case QWindowSystemInterfacePrivate::FocusWindow: + QGuiApplicationPrivate::processFocusWindowEvent(static_cast<QWindowSystemInterfacePrivate::FocusWindowEvent *>(e)); break; case QWindowSystemInterfacePrivate::WindowStateChanged: QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowStateChangedEvent *>(e)); @@ -2205,7 +2271,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo QWindowSystemInterfacePrivate::MouseEvent moveEvent(window, e->timestamp, e->localPos, e->globalPos, e->buttons ^ button, e->modifiers, Qt::NoButton, e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove, - e->source, e->nonClientArea); + e->source, e->nonClientArea, device, e->eventPointId); if (e->synthetic()) moveEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic; processMouseEvent(&moveEvent); // mouse move excluding state change @@ -2225,6 +2291,9 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo bool doubleClick = false; auto persistentEPD = devPriv->pointById(0); + if (e->synthetic(); auto *originalDeviceEPD = devPriv->queryPointById(e->eventPointId)) + QMutableEventPoint::update(originalDeviceEPD->eventPoint, persistentEPD->eventPoint); + if (mouseMove) { QGuiApplicationPrivate::lastCursorPosition = globalPoint; const auto doubleClickDistance = (e->device && e->device->type() == QInputDevice::DeviceType::Mouse ? @@ -2234,12 +2303,14 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo qAbs(globalPoint.y() - pressPos.y()) > doubleClickDistance) mousePressButton = Qt::NoButton; } else { + static unsigned long lastPressTimestamp = 0; mouse_buttons = e->buttons; if (mousePress) { ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval()); - doubleClick = e->timestamp - persistentEPD->eventPoint.pressTimestamp() + doubleClick = e->timestamp - lastPressTimestamp < doubleClickInterval && button == mousePressButton; mousePressButton = button; + lastPressTimestamp = e ->timestamp; } } @@ -2281,6 +2352,14 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo } #endif + const auto *activePopup = activePopupWindow(); + if (type == QEvent::MouseButtonPress) + active_popup_on_press = activePopup; + if (window->d_func()->blockedByModalWindow && !activePopup) { + // a modal window is blocking this window, don't allow mouse events through + return; + } + QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, e->buttons, e->modifiers, e->source, device); Q_ASSERT(devPriv->pointById(0) == persistentEPD); // we don't expect reallocation in QPlatformCursor::pointerEvenmt() // restore globalLastPosition to avoid invalidating the velocity calculations, @@ -2289,9 +2368,15 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo persistentEPD = nullptr; // incoming and synth events can cause reallocation during delivery, so don't use this again // ev now contains a detached copy of the QEventPoint from QPointingDevicePrivate::activePoints ev.setTimestamp(e->timestamp); - if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) { - // a modal window is blocking this window, don't allow mouse events through - return; + + if (activePopup && activePopup != window && (!popup_closed_on_press || type == QEvent::MouseButtonRelease)) { + // If the popup handles the event, we're done. + auto *handlingPopup = window->d_func()->forwardToPopup(&ev, active_popup_on_press); + if (handlingPopup) { + if (type == QEvent::MouseButtonPress) + active_popup_on_press = handlingPopup; + return; + } } if (doubleClick && (ev.type() == QEvent::MouseButtonPress)) { @@ -2343,6 +2428,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo } } if (type == QEvent::MouseButtonRelease && e->buttons == Qt::NoButton) { + popup_closed_on_press = false; if (auto *persistentEPD = devPriv->queryPointById(0)) { ev.setExclusiveGrabber(persistentEPD->eventPoint, nullptr); ev.clearPassiveGrabbers(persistentEPD->eventPoint); @@ -2397,6 +2483,11 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE window = QGuiApplication::focusWindow(); } + if (!window) { + e->eventAccepted = false; + return; + } + #if defined(Q_OS_ANDROID) static bool backKeyPressAccepted = false; static bool menuKeyPressAccepted = false; @@ -2405,7 +2496,7 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE #if !defined(Q_OS_MACOS) // FIXME: Include OS X in this code path by passing the key event through // QPlatformInputContext::filterEvent(). - if (e->keyType == QEvent::KeyPress && window) { + if (e->keyType == QEvent::KeyPress) { if (QWindowSystemInterface::handleShortcutEvent(window, e->timestamp, e->key, e->modifiers, e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers, e->unicode, e->repeat, e->repeatCount)) { #if defined(Q_OS_ANDROID) @@ -2422,9 +2513,16 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE e->unicode, e->repeat, e->repeatCount); ev.setTimestamp(e->timestamp); + const auto *activePopup = activePopupWindow(); + if (activePopup && activePopup != window) { + // If the popup handles the event, we're done. + if (window->d_func()->forwardToPopup(&ev, active_popup_on_press)) + return; + } + // only deliver key events when we have a window, and no modal window is blocking this window - if (window && !window->d_func()->blockedByModalWindow) + if (!window->d_func()->blockedByModalWindow) QGuiApplication::sendSpontaneousEvent(window, &ev); #ifdef Q_OS_ANDROID else @@ -2487,18 +2585,23 @@ void QGuiApplicationPrivate::processLeaveEvent(QWindowSystemInterfacePrivate::Le QCoreApplication::sendSpontaneousEvent(e->leave.data(), &event); } -void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e) +void QGuiApplicationPrivate::processFocusWindowEvent(QWindowSystemInterfacePrivate::FocusWindowEvent *e) { QWindow *previous = QGuiApplicationPrivate::focus_window; - QWindow *newFocus = e->activated.data(); + QWindow *newFocus = e->focused.data(); if (previous == newFocus) return; - if (newFocus) + bool activatedPopup = false; + if (newFocus) { if (QPlatformWindow *platformWindow = newFocus->handle()) if (platformWindow->isAlertState()) platformWindow->setAlertState(false); + activatedPopup = (newFocus->flags() & Qt::WindowType_Mask) == Qt::Popup; + if (activatedPopup) + activatePopup(newFocus); + } QObject *previousFocusObject = previous ? previous->focusObject() : nullptr; @@ -2513,8 +2616,7 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate if (previous) { Qt::FocusReason r = e->reason; - if ((r == Qt::OtherFocusReason || r == Qt::ActiveWindowFocusReason) && - newFocus && (newFocus->flags() & Qt::Popup) == Qt::Popup) + if ((r == Qt::OtherFocusReason || r == Qt::ActiveWindowFocusReason) && activatedPopup) r = Qt::PopupFocusReason; QFocusEvent focusOut(QEvent::FocusOut, r); QCoreApplication::sendSpontaneousEvent(previous, &focusOut); @@ -2578,27 +2680,18 @@ void QGuiApplicationPrivate::processWindowStateChangedEvent(QWindowSystemInterfa void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *wse) { - if (QWindow *window = wse->window.data()) { - if (window->screen() == wse->screen.data()) - return; + QWindow *window = wse->window.data(); + if (!window) + return; - if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) { - if (QScreen *screen = wse->screen.data()) - topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */); - else // Fall back to default behavior, and try to find some appropriate screen - topLevelWindow->setScreen(nullptr); - } + if (window->screen() == wse->screen.data()) + return; - // We may have changed scaling; trigger resize event if needed, - // except on Windows, where we send resize events during WM_DPICHANGED - // event handling. FIXME: unify DPI change handling across all platforms. -#ifndef Q_OS_WIN - if (window->handle()) { - QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window)); - processGeometryChangeEvent(&gce); - } -#endif - QWindowPrivate::get(window)->updateDevicePixelRatio(); + if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) { + if (QScreen *screen = wse->screen.data()) + topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */); + else // Fall back to default behavior, and try to find some appropriate screen + topLevelWindow->setScreen(nullptr); } } @@ -2622,7 +2715,6 @@ void QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(QWindowSystemInt void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate::ThemeChangeEvent *tce) { - QStyleHintsPrivate::get(QGuiApplication::styleHints())->setColorScheme(colorScheme()); if (self) self->handleThemeChanged(); @@ -2634,22 +2726,15 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate:: QGuiApplication::sendSpontaneousEvent(window, &themeChangeEvent); } -/*! - \internal - \brief QGuiApplicationPrivate::colorScheme - \return the platform theme's color scheme - or Qt::ColorScheme::Unknown if a platform theme cannot be established - */ -Qt::ColorScheme QGuiApplicationPrivate::colorScheme() -{ - return platformTheme() ? platformTheme()->colorScheme() - : Qt::ColorScheme::Unknown; -} - void QGuiApplicationPrivate::handleThemeChanged() { + const auto newColorScheme = platformTheme() ? platformTheme()->colorScheme() + : Qt::ColorScheme::Unknown; + QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(newColorScheme); + updatePalette(); + QIconLoader::instance()->updateSystemTheme(); QAbstractFileIconProviderPrivate::clearIconTypeCache(); if (!(applicationResourceFlags & ApplicationFontExplicitlySet)) { @@ -2774,6 +2859,7 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T } if (!window) return; + active_popup_on_press = activePopupWindow(); pointData.target = window; } else { if (e->nullWindow()) { @@ -2801,12 +2887,25 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T } } + const auto *activePopup = activePopupWindow(); + if (window->d_func()->blockedByModalWindow && !activePopup) { + // a modal window is blocking this window, don't allow events through + return; + } + QTabletEvent tabletEvent(type, device, local, e->global, e->pressure, e->xTilt, e->yTilt, e->tangentialPressure, e->rotation, e->z, e->modifiers, button, e->buttons); tabletEvent.setAccepted(false); tabletEvent.setTimestamp(e->timestamp); + + if (activePopup && activePopup != window) { + // If the popup handles the event, we're done. + if (window->d_func()->forwardToPopup(&tabletEvent, active_popup_on_press)) + return; + } + QGuiApplication::sendSpontaneousEvent(window, &tabletEvent); pointData.state = e->buttons; if (!tabletEvent.isAccepted() @@ -2916,17 +3015,17 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To // Send the TouchCancel to all windows with active touches and clean up. QTouchEvent touchEvent(QEvent::TouchCancel, device, e->modifiers); touchEvent.setTimestamp(e->timestamp); - QSet<QWindow *> windowsNeedingCancel; + constexpr qsizetype Prealloc = decltype(devPriv->activePoints)::mapped_container_type::PreallocatedSize; + QMinimalVarLengthFlatSet<QWindow *, Prealloc> windowsNeedingCancel; for (auto &epd : devPriv->activePoints.values()) { if (QWindow *w = QMutableEventPoint::window(epd.eventPoint)) windowsNeedingCancel.insert(w); } - for (QSet<QWindow *>::const_iterator winIt = windowsNeedingCancel.constBegin(), - winItEnd = windowsNeedingCancel.constEnd(); winIt != winItEnd; ++winIt) { - QGuiApplication::sendSpontaneousEvent(*winIt, &touchEvent); - } + for (QWindow *w : windowsNeedingCancel) + QGuiApplication::sendSpontaneousEvent(w, &touchEvent); + if (!self->synthesizedMousePoints.isEmpty() && !e->synthetic()) { for (QHash<QWindow *, SynthesizedMouseData>::const_iterator synthIt = self->synthesizedMousePoints.constBegin(), synthItEnd = self->synthesizedMousePoints.constEnd(); synthIt != synthItEnd; ++synthIt) { @@ -2979,6 +3078,7 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To if (!window) window = QGuiApplication::topLevelAt(tempPt.globalPosition().toPoint()); QMutableEventPoint::setWindow(ep, window); + active_popup_on_press = activePopupWindow(); break; case QEventPoint::State::Released: @@ -3049,7 +3149,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To break; } - if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) { + const auto *activePopup = activePopupWindow(); + if (window->d_func()->blockedByModalWindow && !activePopup) { // a modal window is blocking this window, don't allow touch events through // QTBUG-37371 temporary fix; TODO: revisit when we have a forwarding solution @@ -3063,6 +3164,12 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To continue; } + if (activePopup && activePopup != window) { + // If the popup handles the event, we're done. + if (window->d_func()->forwardToPopup(&touchEvent, active_popup_on_press)) + return; + } + // Note: after the call to sendSpontaneousEvent, touchEvent.position() will have // changed to reflect the local position inside the last (random) widget it tried // to deliver the touch event to, and will therefore be invalid afterwards. @@ -3103,7 +3210,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To } // All touch events that are not accepted by the application will be translated to // left mouse button events instead (see AA_SynthesizeMouseForUnhandledTouchEvents docs). - // TODO why go through QPA? Why not just send a QMouseEvent right from here? + // Sending a QPA event (rather than simply sending a QMouseEvent) takes care of + // side-effects such as double-click synthesis. QWindowSystemInterfacePrivate::MouseEvent fake(window, e->timestamp, window->mapFromGlobal(touchPoint->globalPosition().toPoint()), touchPoint->globalPosition(), @@ -3113,7 +3221,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To mouseEventType, Qt::MouseEventSynthesizedByQt, false, - device); + device, + touchPoint->id()); fake.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic; processMouseEvent(&fake); } @@ -3254,6 +3363,16 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E const bool wasExposed = p->exposed; p->exposed = e->isExposed && window->screen(); + // We expect that the platform plugins send DevicePixelRatioChange events. + // As a fail-safe make a final check here to make sure the cached DPR value is + // always up to date before sending the expose event. + if (e->isExposed && !e->region.isEmpty()) { + const bool dprWasChanged = QWindowPrivate::get(window)->updateDevicePixelRatio(); + if (dprWasChanged) + qWarning() << "The cached device pixel ratio value was stale on window expose. " + << "Please file a QTBUG which explains how to reproduce."; + } + // We treat expose events for an already exposed window as paint events if (wasExposed && p->exposed && shouldSynthesizePaintEvents) { QPaintEvent paintEvent(e->region); @@ -3429,6 +3548,15 @@ void QGuiApplicationPrivate::updatePalette() } } +QEvent::Type QGuiApplicationPrivate::contextMenuEventType() +{ + switch (QGuiApplication::styleHints()->contextMenuTrigger()) { + case Qt::ContextMenuTrigger::Press: return QEvent::MouseButtonPress; + case Qt::ContextMenuTrigger::Release: return QEvent::MouseButtonRelease; + } + return QEvent::None; +} + void QGuiApplicationPrivate::clearPalette() { delete app_pal; @@ -3479,7 +3607,8 @@ bool QGuiApplicationPrivate::setPalette(const QPalette &palette) */ QPalette QGuiApplicationPrivate::basePalette() const { - return platformTheme() ? *platformTheme()->palette() : Qt::gray; + const auto pf = platformTheme(); + return pf && pf->palette() ? *pf->palette() : Qt::gray; } void QGuiApplicationPrivate::handlePaletteChanged(const char *className) @@ -3646,9 +3775,13 @@ void QGuiApplicationPrivate::notifyWindowIconChanged() The default is \c true. - If this property is \c true, the applications quits when the last visible - \l{Primary and Secondary Windows}{primary window} (i.e. top level window - with no transient parent) is closed. + If this property is \c true, the application will attempt to + quit when the last visible \l{Primary and Secondary Windows}{primary window} + (i.e. top level window with no transient parent) is closed. + + Note that attempting a quit may not necessarily result in the + application quitting, for example if there still are active + QEventLoopLocker instances, or the QEvent::Quit event is ignored. \sa quit(), QWindow::close() */ @@ -3704,7 +3837,13 @@ bool QGuiApplicationPrivate::lastWindowClosed() const bool QGuiApplicationPrivate::canQuitAutomatically() { - if (quitOnLastWindowClosed && !lastWindowClosed()) + // The automatic quit functionality is triggered by + // both QEventLoopLocker and maybeLastWindowClosed. + // Although the former is a QCoreApplication feature + // we don't want to quit the application when there + // are open windows, regardless of whether the app + // also quits automatically on maybeLastWindowClosed. + if (!lastWindowClosed()) return false; return QCoreApplicationPrivate::canQuitAutomatically(); @@ -3768,6 +3907,8 @@ Qt::ApplicationState QGuiApplication::applicationState() */ void QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy) { + if (qApp) + qWarning("setHighDpiScaleFactorRoundingPolicy must be called before creating the QGuiApplication instance"); QGuiApplicationPrivate::highDpiScaleFactorRoundingPolicy = policy; } @@ -4285,7 +4426,7 @@ const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA8Text() { #ifdef Q_OS_WIN if (!m_a8ColorProfile) - m_a8ColorProfile = QColorTrcLut::fromGamma(2.31); // This is a hard-coded thing for Windows text rendering + m_a8ColorProfile = QColorTrcLut::fromGamma(2.31f); // This is a hard-coded thing for Windows text rendering return m_a8ColorProfile.get(); #else return colorProfileForA32Text(); @@ -4295,7 +4436,7 @@ const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA8Text() const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA32Text() { if (!m_a32ColorProfile) - m_a32ColorProfile = QColorTrcLut::fromGamma(fontSmoothingGamma); + m_a32ColorProfile = QColorTrcLut::fromGamma(float(fontSmoothingGamma)); return m_a32ColorProfile.get(); } @@ -4358,9 +4499,12 @@ void *QGuiApplication::resolveInterface(const char *name, int revision) const #if QT_CONFIG(xcb) QT_NATIVE_INTERFACE_RETURN_IF(QX11Application, platformNativeInterface()); #endif -#if defined(Q_OS_UNIX) +#if QT_CONFIG(wayland) QT_NATIVE_INTERFACE_RETURN_IF(QWaylandApplication, platformNativeInterface()); #endif +#if defined(Q_OS_VISIONOS) + QT_NATIVE_INTERFACE_RETURN_IF(QVisionOSApplication, platformIntegration); +#endif return QCoreApplication::resolveInterface(name, revision); } |