diff options
Diffstat (limited to 'src/gui/kernel/qguiapplication.cpp')
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 168 |
1 files changed, 138 insertions, 30 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 636956aafa..1630103afe 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -107,6 +107,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcPopup, "qt.gui.popup"); + using namespace Qt::StringLiterals; using namespace QtMiscUtils; @@ -180,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; @@ -960,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. @@ -1531,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); @@ -1785,6 +1827,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() platform_integration = nullptr; window_list.clear(); + popup_list.clear(); screen_list.clear(); self = nullptr; @@ -2306,6 +2349,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, @@ -2314,9 +2365,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)) { @@ -2368,6 +2425,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); @@ -2422,6 +2480,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; @@ -2430,7 +2493,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) @@ -2447,9 +2510,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 @@ -2520,10 +2590,15 @@ void QGuiApplicationPrivate::processFocusWindowEvent(QWindowSystemInterfacePriva 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; @@ -2538,8 +2613,7 @@ void QGuiApplicationPrivate::processFocusWindowEvent(QWindowSystemInterfacePriva 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); @@ -2643,28 +2717,18 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate:: QIconPrivate::clearIconCache(); - QStyleHintsPrivate::get(QGuiApplication::styleHints())->setColorScheme(colorScheme()); - QEvent themeChangeEvent(QEvent::ThemeChange); const QWindowList windows = tce->window ? QWindowList{tce->window} : window_list; for (auto *window : windows) 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(); @@ -2792,6 +2856,7 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T } if (!window) return; + active_popup_on_press = activePopupWindow(); pointData.target = window; } else { if (e->nullWindow()) { @@ -2819,12 +2884,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() @@ -2997,6 +3075,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: @@ -3067,7 +3146,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 @@ -3081,6 +3161,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. @@ -3457,6 +3543,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; @@ -3675,9 +3770,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() */ @@ -3733,7 +3832,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(); @@ -4316,7 +4421,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(); @@ -4326,7 +4431,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(); } @@ -4392,6 +4497,9 @@ void *QGuiApplication::resolveInterface(const char *name, int revision) const #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); } |