summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qguiapplication.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/kernel/qguiapplication.cpp')
-rw-r--r--src/gui/kernel/qguiapplication.cpp389
1 files changed, 289 insertions, 100 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 741a02875a..0b53f34200 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,12 +17,15 @@
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformtheme.h>
#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformkeymapper.h>
#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QFileInfo>
#include <QtCore/QStandardPaths>
#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>
@@ -50,6 +54,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qwindowsysteminterface_p.h>
#include "private/qwindow_p.h"
+#include "private/qicon_p.h"
#include "private/qcursor_p.h"
#if QT_CONFIG(opengl)
# include "private/qopenglcontext_p.h"
@@ -96,12 +101,16 @@
#include <qtgui_tracepoints_p.h>
-#include <ctype.h>
+#include <private/qtools_p.h>
+
#include <limits>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcPopup, "qt.gui.popup");
+
using namespace Qt::StringLiterals;
+using namespace QtMiscUtils;
// Helper macro for static functions to check on the existence of the application class.
#define CHECK_QAPP_INSTANCE(...) \
@@ -173,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;
@@ -229,7 +241,7 @@ static void initThemeHints()
static bool checkNeedPortalSupport()
{
#if QT_CONFIG(dbus)
- return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, "flatpak-info"_L1).isEmpty() || qEnvironmentVariableIsSet("SNAP");
+ return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP");
#else
return false;
#endif // QT_CONFIG(dbus)
@@ -256,20 +268,20 @@ struct QWindowGeometrySpecification
static inline int nextGeometryToken(const QByteArray &a, int &pos, char *op)
{
*op = 0;
- const int size = a.size();
+ const qsizetype size = a.size();
if (pos >= size)
return -1;
*op = a.at(pos);
if (*op == '+' || *op == '-' || *op == 'x')
pos++;
- else if (isdigit(*op))
+ else if (isAsciiDigit(*op))
*op = 'x'; // If it starts with a digit, it is supposed to be a width specification.
else
return -1;
const int numberPos = pos;
- for ( ; pos < size && isdigit(a.at(pos)); ++pos) ;
+ for ( ; pos < size && isAsciiDigit(a.at(pos)); ++pos) ;
bool ok;
const int result = a.mid(numberPos, pos - numberPos).toInt(&ok);
@@ -557,10 +569,12 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
\list
\li \c {altgr}, detect the key \c {AltGr} found on some keyboards as
Qt::GroupSwitchModifier (since Qt 5.12).
- \li \c {darkmode=[1|2]} controls how Qt responds to the activation
+ \li \c {darkmode=[0|1|2]} controls how Qt responds to the activation
of the \e{Dark Mode for applications} introduced in Windows 10
1903 (since Qt 5.15).
+ A value of 0 disables dark mode support.
+
A value of 1 causes Qt to switch the window borders to black
when \e{Dark Mode for applications} is activated and no High
Contrast Theme is in use. This is intended for applications
@@ -579,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
@@ -599,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.
@@ -749,7 +761,8 @@ QString QGuiApplication::applicationDisplayName()
of unread messages or similar.
The badge will be overlaid on the application's icon in the Dock
- on \macos, the home screen icon on iOS, or the task bar on Windows.
+ on \macos, the home screen icon on iOS, or the task bar on Windows
+ and Linux.
If the number is outside the range supported by the platform, the
number will be clamped to the supported range. If the number does
@@ -770,9 +783,9 @@ void QGuiApplication::setBadgeNumber(qint64 number)
\brief the base name of the desktop entry for this application
\since 5.7
- This is the file name, without the full path, of the desktop entry
- that represents this application according to the freedesktop desktop
- entry specification.
+ This is the file name, without the full path or the trailing ".desktop"
+ extension of the desktop entry that represents this application
+ according to the freedesktop desktop entry specification.
This property gives a precise indication of what desktop entry represents
the application and it is needed by the windowing system to retrieve
@@ -786,6 +799,15 @@ void QGuiApplication::setDesktopFileName(const QString &name)
if (!QGuiApplicationPrivate::desktopFileName)
QGuiApplicationPrivate::desktopFileName = new QString;
*QGuiApplicationPrivate::desktopFileName = name;
+ if (name.endsWith(QLatin1String(".desktop"))) { // ### Qt 7: remove
+ const QString filePath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, name);
+ if (!filePath.isEmpty()) {
+ qWarning("QGuiApplication::setDesktopFileName: the specified desktop file name "
+ "ends with .desktop. For compatibility reasons, the .desktop suffix will "
+ "be removed. Please specify a desktop file name without .desktop suffix");
+ (*QGuiApplicationPrivate::desktopFileName).chop(8);
+ }
+ }
}
QString QGuiApplication::desktopFileName()
@@ -812,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)
@@ -943,9 +965,48 @@ 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.
+
+ \sa QWindow::requestActivate()
*/
QWindow *QGuiApplication::focusWindow()
{
@@ -1175,19 +1236,31 @@ 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");
-Q_LOGGING_CATEGORY(lcQpaTheme, "qt.qpa.theme");
-Q_LOGGING_CATEGORY(lcPtrDispatch, "qt.pointer.dispatch");
+Q_STATIC_LOGGING_CATEGORY(lcQpaPluginLoading, "qt.qpa.plugin");
+Q_STATIC_LOGGING_CATEGORY(lcQpaTheme, "qt.qpa.theme");
+Q_STATIC_LOGGING_CATEGORY(lcPtrDispatch, "qt.pointer.dispatch");
static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
{
@@ -1217,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.";
@@ -1305,7 +1382,7 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
if (!platformArguments.isEmpty()) {
if (QObject *nativeInterface = QGuiApplicationPrivate::platform_integration->nativeInterface()) {
for (const QString &argument : std::as_const(platformArguments)) {
- const int equalsPos = argument.indexOf(u'=');
+ const qsizetype equalsPos = argument.indexOf(u'=');
const QByteArray name =
equalsPos != -1 ? argument.left(equalsPos).toUtf8() : argument.toUtf8();
const QVariant value =
@@ -1319,13 +1396,18 @@ static void init_platform(const QString &pluginNamesWithArguments, const QString
fontSmoothingGamma = platformIntegration->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
QCoreApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus,
!QGuiApplication::styleHints()->showShortcutsInContextMenus());
+
+ if (const auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
+ QCoreApplication::setAttribute(Qt::AA_DontShowIconsInMenus,
+ !platformTheme->themeHint(QPlatformTheme::ShowIconsInMenus).toBool());
+ }
}
static void init_plugins(const QList<QByteArray> &pluginList)
{
for (int i = 0; i < pluginList.size(); ++i) {
QByteArray pluginSpec = pluginList.at(i);
- int colonPos = pluginSpec.indexOf(':');
+ qsizetype colonPos = pluginSpec.indexOf(':');
QObject *plugin;
if (colonPos < 0)
plugin = QGenericPluginFactory::create(QLatin1StringView(pluginSpec), QString());
@@ -1496,7 +1578,7 @@ void QGuiApplicationPrivate::createPlatformIntegration()
init_platform(QLatin1StringView(platformName), platformPluginPath, platformThemeName, argc, argv);
if (const QPlatformTheme *theme = platformTheme())
- QStyleHintsPrivate::get(QGuiApplication::styleHints())->setAppearance(theme->appearance());
+ QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(theme->colorScheme());
if (!icon.isEmpty())
forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon);
@@ -1515,7 +1597,11 @@ void QGuiApplicationPrivate::createEventDispatcher()
if (platform_integration == nullptr)
createPlatformIntegration();
- // The platform integration should not mess with the event dispatcher
+ // The platform integration should not result in creating an event dispatcher
+ Q_ASSERT_X(!threadData.loadRelaxed()->eventDispatcher, "QGuiApplication",
+ "Creating the platform integration resulted in creating an event dispatcher");
+
+ // Nor should it mess with the QCoreApplication's event dispatcher
Q_ASSERT(!eventDispatcher);
eventDispatcher = platform_integration->createEventDispatcher();
@@ -1592,7 +1678,7 @@ void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::init()
++i;
if (argv[i] && *argv[i]) {
session_id = QString::fromLatin1(argv[i]);
- int p = session_id.indexOf(u'_');
+ qsizetype p = session_id.indexOf(u'_');
if (p >= 0) {
session_key = session_id.mid(p +1);
session_id = session_id.left(p);
@@ -1746,6 +1832,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
platform_integration = nullptr;
window_list.clear();
+ popup_list.clear();
screen_list.clear();
self = nullptr;
@@ -1808,7 +1895,7 @@ Qt::KeyboardModifiers QGuiApplication::queryKeyboardModifiers()
{
CHECK_QAPP_INSTANCE(Qt::KeyboardModifiers{})
QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
- return pi->queryKeyboardModifiers();
+ return pi->keyMapper()->queryKeyboardModifiers();
}
/*!
@@ -1866,9 +1953,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
@@ -1958,7 +2046,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);
@@ -1966,13 +2055,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()) {
@@ -1984,8 +2075,10 @@ bool QGuiApplication::event(QEvent *e)
return true;
}
}
+ break;
+ default:
+ break;
}
-
return QCoreApplication::event(e);
}
@@ -2043,8 +2136,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));
@@ -2052,6 +2145,9 @@ void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::processWindowSystemEvent(
case QWindowSystemInterfacePrivate::WindowScreenChanged:
QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e));
break;
+ case QWindowSystemInterfacePrivate::WindowDevicePixelRatioChanged:
+ QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *>(e));
+ break;
case QWindowSystemInterfacePrivate::SafeAreaMarginsChanged:
QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(static_cast<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *>(e));
break;
@@ -2180,7 +2276,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
@@ -2200,6 +2296,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 ?
@@ -2209,12 +2308,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;
}
}
@@ -2256,6 +2357,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,
@@ -2264,9 +2373,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)) {
@@ -2318,6 +2433,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);
@@ -2354,6 +2470,7 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh
mouse_buttons, e->modifiers, e->phase, e->inverted, e->source, device);
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(window, &ev);
+ e->eventAccepted = ev.isAccepted();
#else
Q_UNUSED(e);
#endif // QT_CONFIG(wheelevent)
@@ -2371,6 +2488,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;
@@ -2379,7 +2501,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)
@@ -2396,9 +2518,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
@@ -2461,18 +2590,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;
@@ -2487,8 +2621,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);
@@ -2552,28 +2685,28 @@ void QGuiApplicationPrivate::processWindowStateChangedEvent(QWindowSystemInterfa
void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *wse)
{
- if (QWindow *window = wse->window.data()) {
- if (window->screen() == wse->screen.data())
- 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);
- }
+ QWindow *window = wse->window.data();
+ if (!window)
+ 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
+ if (window->screen() == wse->screen.data())
+ 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);
}
}
+void QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *wde)
+{
+ if (wde->window.isNull())
+ return;
+ QWindowPrivate::get(wde->window)->updateDevicePixelRatio();
+}
+
void QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *wse)
{
if (wse->window.isNull())
@@ -2590,31 +2723,23 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate::
if (self)
self->handleThemeChanged();
+ QIconPrivate::clearIconCache();
+
QEvent themeChangeEvent(QEvent::ThemeChange);
const QWindowList windows = tce->window ? QWindowList{tce->window} : window_list;
for (auto *window : windows)
QGuiApplication::sendSpontaneousEvent(window, &themeChangeEvent);
-
- QStyleHintsPrivate::get(QGuiApplication::styleHints())->setAppearance(appearance());
-}
-
-/*!
- \internal
- \brief QGuiApplicationPrivate::appearance
- \return the platform theme's appearance
- or Qt::Appearance::Unknown if a platform theme cannot be established
- Qt::Appearance.
- */
-Qt::Appearance QGuiApplicationPrivate::appearance()
-{
- return platformTheme() ? platformTheme()->appearance()
- : Qt::Appearance::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)) {
@@ -2684,6 +2809,7 @@ void QGuiApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::Cl
if (e->window.data()->d_func()->blockedByModalWindow && !e->window.data()->d_func()->inClose) {
// a modal window is blocking this window, don't allow close events through, unless they
// originate from a call to QWindow::close.
+ e->eventAccepted = false;
return;
}
@@ -2738,6 +2864,7 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T
}
if (!window)
return;
+ active_popup_on_press = activePopupWindow();
pointData.target = window;
} else {
if (e->nullWindow()) {
@@ -2765,12 +2892,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()
@@ -2880,17 +3020,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) {
@@ -2943,6 +3083,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:
@@ -3013,7 +3154,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
@@ -3027,6 +3169,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.
@@ -3067,7 +3215,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(),
@@ -3077,7 +3226,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
mouseEventType,
Qt::MouseEventSynthesizedByQt,
false,
- device);
+ device,
+ touchPoint->id());
fake.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
processMouseEvent(&fake);
}
@@ -3156,6 +3306,10 @@ void QGuiApplicationPrivate::processScreenLogicalDotsPerInchChange(QWindowSystem
s->d_func()->updateGeometry();
}
+ for (QWindow *window : QGuiApplication::allWindows())
+ if (window->screen() == e->screen)
+ QWindowPrivate::get(window)->updateDevicePixelRatio();
+
resetCachedDevicePixelRatio();
}
@@ -3214,6 +3368,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);
@@ -3389,6 +3553,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;
@@ -3439,7 +3612,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)
@@ -3606,9 +3780,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()
*/
@@ -3664,7 +3842,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();
@@ -3728,6 +3912,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;
}
@@ -4245,7 +4431,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();
@@ -4255,7 +4441,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();
}
@@ -4318,9 +4504,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);
}