summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/networkinformation/android/jar/build.gradle4
-rw-r--r--src/plugins/platforms/android/CMakeLists.txt3
-rw-r--r--src/plugins/platforms/android/androidbackendregister.cpp52
-rw-r--r--src/plugins/platforms/android/androidbackendregister.h67
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp6
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp52
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp78
-rw-r--r--src/plugins/platforms/android/androidjnimain.h6
-rw-r--r--src/plugins/platforms/android/androidjnimenu.cpp18
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp20
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.cpp15
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.h3
-rw-r--r--src/plugins/platforms/android/qandroidplatformwindow.cpp13
-rw-r--r--src/plugins/platforms/cocoa/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibility.mm17
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm19
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm17
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h8
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm94
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm3
-rw-r--r--src/plugins/platforms/cocoa/qnsview_drawing.mm40
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm2
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp4
-rw-r--r--src/plugins/platforms/eglfs/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/ios/CMakeLists.txt19
-rw-r--r--src/plugins/platforms/ios/SwiftIntegration.cmake78
-rw-r--r--src/plugins/platforms/ios/module.modulemap4
-rw-r--r--src/plugins/platforms/ios/qiosapplication.swift198
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.h5
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.h2
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.mm21
-rw-r--r--src/plugins/platforms/ios/qiosglobal.mm20
-rw-r--r--src/plugins/platforms/ios/qiosintegration.h31
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm50
-rw-r--r--src/plugins/platforms/ios/qiostheme.h5
-rw-r--r--src/plugins/platforms/ios/qiostheme.mm35
-rw-r--r--src/plugins/platforms/ios/quiaccessibilityelement.mm13
-rw-r--r--src/plugins/platforms/ios/quiwindow.mm9
-rw-r--r--src/plugins/platforms/linuxfb/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/minimal/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/minimalegl/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/offscreen/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/qnx/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/qnx/qqnxintegration.cpp16
-rw-r--r--src/plugins/platforms/vkkhrdisplay/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/vnc/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/wasm/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/wasm/qwasmaccessibility.cpp4
-rw-r--r--src/plugins/platforms/wasm/qwasmdom.cpp11
-rw-r--r--src/plugins/platforms/wasm/qwasmevent.cpp2
-rw-r--r--src/plugins/platforms/wasm/qwasmevent.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp7
-rw-r--r--src/plugins/platforms/wasm/wasm_shell.html5
-rw-r--r--src/plugins/platforms/windows/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsmimeregistry.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.h1
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp76
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h3
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp8
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp133
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h6
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp8
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h11
-rw-r--r--src/plugins/platforms/xcb/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp27
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.h6
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp19
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp5
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp140
-rw-r--r--src/plugins/styles/modernwindows/qwindows11style.cpp38
75 files changed, 1324 insertions, 278 deletions
diff --git a/src/plugins/networkinformation/android/jar/build.gradle b/src/plugins/networkinformation/android/jar/build.gradle
index 68a9381ad2..ea6d06c257 100644
--- a/src/plugins/networkinformation/android/jar/build.gradle
+++ b/src/plugins/networkinformation/android/jar/build.gradle
@@ -7,7 +7,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.0.2'
+ classpath 'com.android.tools.build:gradle:8.4.0'
}
}
@@ -26,7 +26,7 @@ android {
compileSdk 34
defaultConfig {
- minSdkVersion 23
+ minSdkVersion 28
}
sourceSets {
diff --git a/src/plugins/platforms/android/CMakeLists.txt b/src/plugins/platforms/android/CMakeLists.txt
index d5a275a76c..aaf62dd7e7 100644
--- a/src/plugins/platforms/android/CMakeLists.txt
+++ b/src/plugins/platforms/android/CMakeLists.txt
@@ -9,7 +9,7 @@ qt_find_package(EGL)
qt_internal_add_plugin(QAndroidIntegrationPlugin
OUTPUT_NAME qtforandroid
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES android
+ DEFAULT_IF "android" IN_LIST QT_QPA_PLATFORMS
SOURCES
androidcontentfileengine.cpp androidcontentfileengine.h
androiddeadlockprotector.h
@@ -41,6 +41,7 @@ qt_internal_add_plugin(QAndroidIntegrationPlugin
qandroidplatformwindow.cpp qandroidplatformwindow.h
qandroidsystemlocale.cpp qandroidsystemlocale.h
androidwindowembedding.cpp androidwindowembedding.h
+ androidbackendregister.cpp androidbackendregister.h
NO_UNITY_BUILD_SOURCES
# Conflicting symbols and macros with androidjnimain.cpp
# TODO: Unify the usage of FIND_AND_CHECK_CLASS, and similar
diff --git a/src/plugins/platforms/android/androidbackendregister.cpp b/src/plugins/platforms/android/androidbackendregister.cpp
new file mode 100644
index 0000000000..bfd86138aa
--- /dev/null
+++ b/src/plugins/platforms/android/androidbackendregister.cpp
@@ -0,0 +1,52 @@
+// Copyright (C) 2024 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 "androidbackendregister.h"
+
+#include "androidjnimain.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcAndroidBackendRegister, "qt.qpa.androidbackendregister")
+
+Q_DECLARE_JNI_CLASS(BackendRegister, "org/qtproject/qt/android/BackendRegister");
+
+bool AndroidBackendRegister::registerNatives()
+{
+ return QtJniTypes::BackendRegister::registerNativeMethods(
+ { Q_JNI_NATIVE_SCOPED_METHOD(registerBackend, AndroidBackendRegister),
+ Q_JNI_NATIVE_SCOPED_METHOD(unregisterBackend, AndroidBackendRegister) });
+}
+
+void AndroidBackendRegister::registerBackend(JNIEnv *, jclass, jclass interfaceClass,
+ jobject interface)
+{
+ if (AndroidBackendRegister *reg = QtAndroid::backendRegister()) {
+ const QJniObject classObject(static_cast<jobject>(interfaceClass));
+ QString name = classObject.callMethod<jstring>("getName").toString();
+ name.replace('.', '/');
+
+ QMutexLocker lock(&reg->m_registerMutex);
+ reg->m_register[name] = QJniObject(interface);
+ } else {
+ qCWarning(lcAndroidBackendRegister)
+ << "AndroidBackendRegister pointer is null, cannot register functionality";
+ }
+}
+
+void AndroidBackendRegister::unregisterBackend(JNIEnv *, jclass, jclass interfaceClass)
+{
+ if (AndroidBackendRegister *reg = QtAndroid::backendRegister()) {
+ const QJniObject classObject(static_cast<jobject>(interfaceClass));
+ QString name = classObject.callMethod<jstring>("getName").toString();
+ name.replace('.', '/');
+
+ QMutexLocker lock(&reg->m_registerMutex);
+ reg->m_register.remove(name);
+ } else {
+ qCWarning(lcAndroidBackendRegister)
+ << "AndroidBackendRegister pointer is null, cannot unregister functionality";
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidbackendregister.h b/src/plugins/platforms/android/androidbackendregister.h
new file mode 100644
index 0000000000..c186f7e107
--- /dev/null
+++ b/src/plugins/platforms/android/androidbackendregister.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2024 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
+
+#ifndef ANDROIDBACKENDREGISTER_H
+#define ANDROIDBACKENDREGISTER_H
+
+#include <type_traits>
+
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjnitypes.h>
+#include <QtCore/qjniobject.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcAndroidBackendRegister)
+
+template <typename T>
+using ValidInterfaceType = std::enable_if_t<std::is_base_of_v<QtJniTypes::JObjectBase, T>, bool>;
+
+class AndroidBackendRegister
+{
+public:
+ static bool registerNatives();
+
+ template <typename T, ValidInterfaceType<T> = true>
+ [[nodiscard]] T getInterface()
+ {
+ QMutexLocker lock(&m_registerMutex);
+ return m_register.value(QString(QtJniTypes::Traits<T>::className().data()));
+ }
+
+ template <typename Object>
+ using IsObjectType =
+ typename std::disjunction<std::is_base_of<QJniObject, Object>,
+ std::is_base_of<QtJniTypes::JObjectBase, Object>>;
+
+ template <typename Interface, typename Ret, typename... Args,
+ ValidInterfaceType<Interface> = true>
+ auto callInterface(const char *func, Args... args)
+ {
+ if (const auto obj = getInterface<Interface>(); obj.isValid())
+ return obj.template callMethod<Ret, Args...>(func, std::forward<Args>(args)...);
+
+ if constexpr (IsObjectType<Ret>::value)
+ return Ret(QJniObject());
+ if constexpr (!std::is_same_v<Ret, void>)
+ return Ret{};
+ }
+
+private:
+ QMutex m_registerMutex;
+ QMap<QString, QJniObject> m_register;
+
+ static void registerBackend(JNIEnv *, jclass, jclass interfaceClass, jobject interface);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(registerBackend)
+ static void unregisterBackend(JNIEnv *, jclass, jclass interfaceClass);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(unregisterBackend)
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDBACKENDREGISTER_H
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp
index da5b63ef21..2010a9e03b 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.cpp
+++ b/src/plugins/platforms/android/androidjniaccessibility.cpp
@@ -82,7 +82,7 @@ namespace QtAndroidAccessibility
void initialize()
{
- QtAndroid::qtActivityDelegate().callMethod<void>("initializeAccessibility");
+ QtAndroid::initializeAccessibility();
}
bool isActive()
@@ -470,6 +470,7 @@ namespace QtAndroidAccessibility
QAccessible::Role role;
QStringList actions;
QString description;
+ QString identifier;
bool hasTextSelection = false;
int selectionStart = 0;
int selectionEnd = 0;
@@ -485,6 +486,7 @@ namespace QtAndroidAccessibility
info.role = iface->role();
info.actions = QAccessibleBridgeUtils::effectiveActionNames(iface);
info.description = descriptionForInterface(iface);
+ info.identifier = QAccessibleBridgeUtils::accessibleId(iface);
QAccessibleTextInterface *textIface = iface->textInterface();
if (textIface && (textIface->selectionCount() > 0)) {
info.hasTextSelection = true;
@@ -550,6 +552,8 @@ namespace QtAndroidAccessibility
//CALL_METHOD(node, "setText", "(Ljava/lang/CharSequence;)V", jdesc)
env->CallVoidMethod(node, m_setContentDescriptionMethodID, jdesc);
+ QJniObject(node).callMethod<void>("setViewIdResourceName", info.identifier);
+
return true;
}
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp
index d074e73b9e..266d027b3c 100644
--- a/src/plugins/platforms/android/androidjniinput.cpp
+++ b/src/plugins/platforms/android/androidjniinput.cpp
@@ -24,6 +24,8 @@ Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
using namespace QtAndroid;
Q_DECLARE_JNI_CLASS(QtLayout, "org/qtproject/qt/android/QtLayout")
+Q_DECLARE_JNI_CLASS(QtLayoutInterface, "org/qtproject/qt/android/QtLayoutInterface")
+Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
namespace QtAndroidInput
{
@@ -98,48 +100,48 @@ namespace QtAndroidInput
QJniObject qtLayout()
{
- return qtActivityDelegate().callMethod<QtJniTypes::QtLayout>("getQtLayout");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ return reg->callInterface<QtJniTypes::QtLayoutInterface, QtJniTypes::QtLayout>(
+ "getQtLayout");
}
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
{
qCDebug(lcQpaInputMethods) << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd;
- qtInputDelegate().callMethod<void>("updateSelection",
- selStart,
- selEnd,
- candidatesStart,
- candidatesEnd);
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtInputInterface, void>("updateSelection", selStart, selEnd,
+ candidatesStart, candidatesEnd);
}
void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType)
{
- qtInputDelegate().callMethod<void>("showSoftwareKeyboard",
- QtAndroidPrivate::activity(),
- qtLayout().object<QtJniTypes::QtLayout>(),
- left,
- top,
- width,
- height,
- inputHints,
- enterKeyType);
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtInputInterface, void>(
+ "showSoftwareKeyboard", QtAndroidPrivate::activity(),
+ qtLayout().object<QtJniTypes::QtLayout>(), left, top, width, height, inputHints,
+ enterKeyType);
qCDebug(lcQpaInputMethods) << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints << enterKeyType;
}
void resetSoftwareKeyboard()
{
- qtInputDelegate().callMethod<void>("resetSoftwareKeyboard");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtInputInterface, void>("resetSoftwareKeyboard");
qCDebug(lcQpaInputMethods) << "@@@ RESETSOFTWAREKEYBOARD";
}
void hideSoftwareKeyboard()
{
- qtInputDelegate().callMethod<void>("hideSoftwareKeyboard");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtInputInterface, void>("hideSoftwareKeyboard");
qCDebug(lcQpaInputMethods) << "@@@ HIDESOFTWAREKEYBOARD";
}
bool isSoftwareKeyboardVisible()
{
- return qtInputDelegate().callMethod<jboolean>("isSoftwareKeyboardVisible");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ return reg->callInterface<QtJniTypes::QtInputInterface, jboolean>(
+ "isSoftwareKeyboardVisible");
}
QRect softwareKeyboardRect()
@@ -149,17 +151,17 @@ namespace QtAndroidInput
int getSelectHandleWidth()
{
- return qtInputDelegate().callMethod<jint>("getSelectHandleWidth");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ return reg->callInterface<QtJniTypes::QtInputInterface, jint>("getSelectHandleWidth");
}
void updateHandles(int mode, QPoint editMenuPos, uint32_t editButtons, QPoint cursor, QPoint anchor, bool rtl)
{
- qtInputDelegate().callMethod<void>("updateHandles",
- QtAndroidPrivate::activity(),
- qtLayout().object<QtJniTypes::QtLayout>(),
- mode, editMenuPos.x(), editMenuPos.y(), editButtons,
- cursor.x(), cursor.y(),
- anchor.x(), anchor.y(), rtl);
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtInputInterface, void>(
+ "updateHandles", QtAndroidPrivate::activity(),
+ qtLayout().object<QtJniTypes::QtLayout>(), mode, editMenuPos.x(), editMenuPos.y(),
+ editButtons, cursor.x(), cursor.y(), anchor.x(), anchor.y(), rtl);
}
// from https://developer.android.com/reference/android/view/MotionEvent#getButtonState()
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 9fdcf3936b..5bd2b924fc 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -54,9 +54,6 @@ static jobject m_resourcesObj = nullptr;
static jclass m_qtActivityClass = nullptr;
static jclass m_qtServiceClass = nullptr;
-static QtJniTypes::QtActivityDelegateBase m_activityDelegate = nullptr;
-static QtJniTypes::QtInputDelegate m_inputDelegate = nullptr;
-
static int m_pendingApplicationState = -1;
static QBasicMutex m_platformMutex;
@@ -85,7 +82,7 @@ static double m_density = 1.0;
static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr;
static AndroidContentFileEngineHandler *m_androidContentFileEngineHandler = nullptr;
-
+static AndroidBackendRegister *m_backendRegister = nullptr;
static const char m_qtTag[] = "Qt";
static const char m_classErrorMsg[] = "Can't find class \"%s\"";
@@ -93,7 +90,8 @@ static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
-Q_DECLARE_JNI_CLASS(QtEmbeddedDelegateFactory, "org/qtproject/qt/android/QtEmbeddedDelegateFactory")
+Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface")
+Q_DECLARE_JNI_CLASS(QtAccessibilityInterface, "org/qtproject/qt/android/QtAccessibilityInterface");
namespace QtAndroid
{
@@ -184,36 +182,9 @@ namespace QtAndroid
// TODO move calls from here to where they logically belong
void setSystemUiVisibility(SystemUiVisibility uiVisibility)
{
- qtActivityDelegate().callMethod<void>("setSystemUiVisibility", jint(uiVisibility));
- }
-
- // FIXME: avoid direct access to QtActivityDelegate
- QtJniTypes::QtActivityDelegateBase qtActivityDelegate()
- {
- using namespace QtJniTypes;
- if (!m_activityDelegate.isValid()) {
- if (isQtApplication()) {
- auto context = QtAndroidPrivate::activity();
- m_activityDelegate = context.callMethod<QtActivityDelegateBase>("getActivityDelegate");
- } else {
- m_activityDelegate = QJniObject::callStaticMethod<QtActivityDelegateBase>(
- Traits<QtEmbeddedDelegateFactory>::className(),
- "getActivityDelegate",
- QtAndroidPrivate::activity());
- }
- }
-
- return m_activityDelegate;
- }
-
- QtJniTypes::QtInputDelegate qtInputDelegate()
- {
- if (!m_inputDelegate.isValid()) {
- m_inputDelegate = qtActivityDelegate().callMethod<QtJniTypes::QtInputDelegate>(
- "getInputDelegate");
- }
-
- return m_inputDelegate;
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtWindowInterface, void>("setSystemUiVisibility",
+ jint(uiVisibility));
}
bool isQtApplication()
@@ -234,36 +205,46 @@ namespace QtAndroid
return true;
}
+ void initializeAccessibility()
+ {
+ m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
+ "initializeAccessibility");
+ }
+
void notifyAccessibilityLocationChange(uint accessibilityObjectId)
{
- qtActivityDelegate().callMethod<void>("notifyLocationChange", accessibilityObjectId);
+ m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
+ "notifyLocationChange", accessibilityObjectId);
}
void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
{
- qtActivityDelegate().callMethod<void>("notifyObjectHide",
- accessibilityObjectId, parentObjectId);
+ m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
+ "notifyObjectHide", accessibilityObjectId, parentObjectId);
}
void notifyObjectShow(uint parentObjectId)
{
- qtActivityDelegate().callMethod<void>("notifyObjectShow",
- parentObjectId);
+ m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
+ "notifyObjectShow", parentObjectId);
}
void notifyObjectFocus(uint accessibilityObjectId)
{
- qtActivityDelegate().callMethod<void>("notifyObjectFocus", accessibilityObjectId);
+ m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
+ "notifyObjectFocus", accessibilityObjectId);
}
void notifyValueChanged(uint accessibilityObjectId, jstring value)
{
- qtActivityDelegate().callMethod<void>("notifyValueChanged", accessibilityObjectId, value);
+ m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
+ "notifyValueChanged", accessibilityObjectId, value);
}
void notifyScrolledEvent(uint accessibilityObjectId)
{
- qtActivityDelegate().callMethod<void>("notifyScrolledEvent", accessibilityObjectId);
+ m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>(
+ "notifyScrolledEvent", accessibilityObjectId);
}
void notifyNativePluginIntegrationReady(bool ready)
@@ -387,6 +368,11 @@ namespace QtAndroid
return m_assets;
}
+ AndroidBackendRegister *backendRegister()
+ {
+ return m_backendRegister;
+ }
+
} // namespace QtAndroid
static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString)
@@ -397,6 +383,7 @@ static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring pa
m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler();
m_mainLibraryHnd = nullptr;
+ m_backendRegister = new AndroidBackendRegister();
const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString());
@@ -544,6 +531,8 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
m_androidPlatformIntegration = nullptr;
delete m_androidAssetsFileEngineHandler;
m_androidAssetsFileEngineHandler = nullptr;
+ delete m_backendRegister;
+ m_backendRegister = nullptr;
sem_post(&m_exitSemaphore);
}
@@ -891,7 +880,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
|| !QtAndroidDialogHelpers::registerNatives(env)
|| !QAndroidPlatformClipboard::registerNatives(env)
|| !QAndroidPlatformWindow::registerNatives(env)
- || !QtAndroidWindowEmbedding::registerNatives(env)) {
+ || !QtAndroidWindowEmbedding::registerNatives(env)
+ || !AndroidBackendRegister::registerNatives()) {
__android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
return -1;
}
diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h
index 99fff96d2b..9d616b18fb 100644
--- a/src/plugins/platforms/android/androidjnimain.h
+++ b/src/plugins/platforms/android/androidjnimain.h
@@ -13,6 +13,7 @@
#include <QImage>
#include <private/qjnihelpers_p.h>
#include <QtCore/QJniObject>
+#include <androidbackendregister.h>
QT_BEGIN_NAMESPACE
@@ -33,6 +34,7 @@ namespace QtAndroid
{
QBasicMutex *platformInterfaceMutex();
QAndroidPlatformIntegration *androidPlatformIntegration();
+ AndroidBackendRegister *backendRegister();
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration);
void setQtThread(QThread *thread);
void setViewVisibility(jobject view, bool visible);
@@ -48,9 +50,6 @@ namespace QtAndroid
AAssetManager *assetManager();
jclass applicationClass();
- QtJniTypes::QtActivityDelegateBase qtActivityDelegate();
- QtJniTypes::QtInputDelegate qtInputDelegate();
-
// Keep synchronized with flags in ActivityDelegate.java
enum SystemUiVisibility {
SYSTEM_UI_VISIBILITY_NORMAL = 0,
@@ -63,6 +62,7 @@ namespace QtAndroid
jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env);
jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = nullptr);
+ void initializeAccessibility();
void notifyAccessibilityLocationChange(uint accessibilityObjectId);
void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId);
void notifyObjectShow(uint parentObjectId);
diff --git a/src/plugins/platforms/android/androidjnimenu.cpp b/src/plugins/platforms/android/androidjnimenu.cpp
index 8bf37d1af2..5f8a0b92e9 100644
--- a/src/plugins/platforms/android/androidjnimenu.cpp
+++ b/src/plugins/platforms/android/androidjnimenu.cpp
@@ -20,6 +20,8 @@ QT_BEGIN_NAMESPACE
using namespace QtAndroid;
+Q_DECLARE_JNI_CLASS(QtMenuInterface, "org/qtproject/qt/android/QtMenuInterface");
+
namespace QtAndroidMenu
{
static QList<QAndroidPlatformMenu *> pendingContextMenus;
@@ -44,12 +46,14 @@ namespace QtAndroidMenu
void resetMenuBar()
{
- qtActivityDelegate().callMethod<void>("resetOptionsMenu");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtMenuInterface, void>("resetOptionsMenu");
}
void openOptionsMenu()
{
- qtActivityDelegate().callMethod<void>("openOptionsMenu");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtMenuInterface, void>("openOptionsMenu");
}
void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect)
@@ -59,16 +63,18 @@ namespace QtAndroidMenu
pendingContextMenus.append(visibleMenu);
visibleMenu = menu;
menu->aboutToShow();
- qtActivityDelegate().callMethod<void>("openContextMenu",
- anchorRect.x(), anchorRect.y(),
- anchorRect.width(), anchorRect.height());
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtMenuInterface, void>("openContextMenu", anchorRect.x(),
+ anchorRect.y(), anchorRect.width(),
+ anchorRect.height());
}
void hideContextMenu(QAndroidPlatformMenu *menu)
{
QMutexLocker lock(&visibleMenuMutex);
if (visibleMenu == menu) {
- qtActivityDelegate().callMethod<void>("closeContextMenu");
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtMenuInterface, void>("closeContextMenu");
pendingContextMenus.clear();
} else {
pendingContextMenus.removeOne(menu);
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp
index 9e20b7ac4b..4d752a3cc3 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp
+++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp
@@ -57,6 +57,7 @@ Q_DECLARE_JNI_CLASS(Resources, "android/content/res/Resources")
Q_DECLARE_JNI_CLASS(Size, "android/util/Size")
Q_DECLARE_JNI_CLASS(QtNative, "org/qtproject/qt/android/QtNative")
Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager")
+Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface")
Q_DECLARE_JNI_CLASS(DisplayMode, "android/view/Display$Mode")
@@ -162,7 +163,10 @@ void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
return;
m_windowStack.prepend(window);
- QtAndroid::qtActivityDelegate().callMethod<void>("addTopLevelWindow", window->nativeWindow());
+
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtWindowInterface, void>("addTopLevelWindow",
+ window->nativeWindow());
if (window->window()->isVisible())
topVisibleWindowChanged();
@@ -175,7 +179,9 @@ void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
if (m_windowStack.contains(window))
qWarning() << "Failed to remove window";
- QtAndroid::qtActivityDelegate().callMethod<void>("removeTopLevelWindow", window->nativeViewId());
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtWindowInterface, void>("removeTopLevelWindow",
+ window->nativeViewId());
topVisibleWindowChanged();
}
@@ -187,7 +193,10 @@ void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
return;
if (index > 0) {
m_windowStack.move(index, 0);
- QtAndroid::qtActivityDelegate().callMethod<void>("bringChildToFront", window->nativeViewId());
+
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtWindowInterface, void>("bringChildToFront",
+ window->nativeViewId());
}
topVisibleWindowChanged();
}
@@ -198,7 +207,10 @@ void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
if (index == -1 || index == (m_windowStack.size() - 1))
return;
m_windowStack.move(index, m_windowStack.size() - 1);
- QtAndroid::qtActivityDelegate().callMethod<void>("bringChildToBack", window->nativeViewId());
+
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ reg->callInterface<QtJniTypes::QtWindowInterface, void>("bringChildToBack",
+ window->nativeViewId());
topVisibleWindowChanged();
}
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp
index 7b9072df69..99eeabac1d 100644
--- a/src/plugins/platforms/android/qandroidplatformtheme.cpp
+++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp
@@ -161,7 +161,10 @@ QJsonObject AndroidStyle::loadStyleData()
if (!stylePath.isEmpty() && !stylePath.endsWith(slashChar))
stylePath += slashChar;
- if (QAndroidPlatformIntegration::colorScheme() == Qt::ColorScheme::Dark)
+ const Qt::ColorScheme colorScheme = QAndroidPlatformTheme::instance()
+ ? QAndroidPlatformTheme::instance()->colorScheme()
+ : QAndroidPlatformIntegration::colorScheme();
+ if (colorScheme == Qt::ColorScheme::Dark)
stylePath += "darkUiMode/"_L1;
Q_ASSERT(!stylePath.isEmpty());
@@ -423,9 +426,19 @@ void QAndroidPlatformTheme::showPlatformMenuBar()
Qt::ColorScheme QAndroidPlatformTheme::colorScheme() const
{
+ if (m_colorSchemeOverride != Qt::ColorScheme::Unknown)
+ return m_colorSchemeOverride;
return QAndroidPlatformIntegration::colorScheme();
}
+void QAndroidPlatformTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+ m_colorSchemeOverride = scheme;
+ QMetaObject::invokeMethod(qGuiApp, [this]{
+ updateColorScheme();
+ });
+}
+
static inline int paletteType(QPlatformTheme::Palette type)
{
switch (type) {
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.h b/src/plugins/platforms/android/qandroidplatformtheme.h
index ce3d6d5f73..1b4ab5664d 100644
--- a/src/plugins/platforms/android/qandroidplatformtheme.h
+++ b/src/plugins/platforms/android/qandroidplatformtheme.h
@@ -40,6 +40,8 @@ public:
QPlatformMenuItem *createPlatformMenuItem() const override;
void showPlatformMenuBar() override;
Qt::ColorScheme colorScheme() const override;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
+
const QPalette *palette(Palette type = SystemPalette) const override;
const QFont *font(Font type = SystemFont) const override;
QIconEngine *createIconEngine(const QString &iconName) const override;
@@ -57,6 +59,7 @@ private:
std::shared_ptr<AndroidStyle> m_androidStyleData;
QPalette m_defaultPalette;
QFont m_systemFont;
+ Qt::ColorScheme m_colorSchemeOverride = Qt::ColorScheme::Unknown;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp
index 979f0fb98a..2482160573 100644
--- a/src/plugins/platforms/android/qandroidplatformwindow.cpp
+++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp
@@ -16,6 +16,10 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
+Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
+Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
+ "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
+
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
: QPlatformWindow(window), m_nativeQtWindow(nullptr),
m_surfaceContainerType(SurfaceContainer::TextureView), m_nativeParentQtWindow(nullptr),
@@ -55,10 +59,13 @@ QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
m_nativeParentQtWindow = androidParent->nativeWindow();
}
+ AndroidBackendRegister *reg = QtAndroid::backendRegister();
+ QtJniTypes::QtInputConnectionListener listener =
+ reg->callInterface<QtJniTypes::QtInputInterface, QtJniTypes::QtInputConnectionListener>(
+ "getInputConnectionListener");
+
m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
- QNativeInterface::QAndroidApplication::context(),
- m_nativeParentQtWindow,
- QtAndroid::qtInputDelegate());
+ QNativeInterface::QAndroidApplication::context(), m_nativeParentQtWindow, listener);
m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
if (window->isTopLevel())
diff --git a/src/plugins/platforms/cocoa/CMakeLists.txt b/src/plugins/platforms/cocoa/CMakeLists.txt
index 92e681d8fb..491c61703f 100644
--- a/src/plugins/platforms/cocoa/CMakeLists.txt
+++ b/src/plugins/platforms/cocoa/CMakeLists.txt
@@ -7,7 +7,7 @@
qt_internal_add_plugin(QCocoaIntegrationPlugin
OUTPUT_NAME qcocoa
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES cocoa
+ DEFAULT_IF "cocoa" IN_LIST QT_QPA_PLATFORMS
PLUGIN_TYPE platforms
SOURCES
main.mm
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
index c5e40a4087..40c1e90511 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
@@ -36,6 +36,23 @@ void QCocoaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
}
switch (event->type()) {
+ case QAccessible::Announcement: {
+ auto *announcementEvent = static_cast<QAccessibleAnnouncementEvent *>(event);
+ auto priorityLevel = (announcementEvent->priority() == QAccessible::AnnouncementPriority::Assertive)
+ ? NSAccessibilityPriorityHigh
+ : NSAccessibilityPriorityMedium;
+ NSDictionary *announcementInfo = @{
+ NSAccessibilityPriorityKey: [NSNumber numberWithInt:priorityLevel],
+ NSAccessibilityAnnouncementKey: announcementEvent->message().toNSString()
+ };
+ // post event for application element, as the comment for
+ // NSAccessibilityAnnouncementRequestedNotification in the
+ // NSAccessibilityConstants.h header says
+ NSAccessibilityPostNotificationWithUserInfo(NSApp,
+ NSAccessibilityAnnouncementRequestedNotification,
+ announcementInfo);
+ break;
+ }
case QAccessible::Focus: {
NSAccessibilityPostNotification(element, NSAccessibilityFocusedUIElementChangedNotification);
break;
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
index 8d4d6d683d..b319dd072e 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
@@ -520,6 +520,12 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
return nil;
}
+- (NSString*) accessibilityIdentifier {
+ if (QAccessibleInterface *iface = self.qtInterface)
+ return QAccessibleBridgeUtils::accessibleId(iface).toNSString();
+ return nil;
+}
+
- (BOOL) isAccessibilityEnabled {
if (QAccessibleInterface *iface = self.qtInterface)
return !iface->state().disabled;
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index be562e5455..2249658189 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -471,25 +471,6 @@ void QCocoaScreen::deliverUpdateRequests()
if (!platformWindow->updatesWithDisplayLink())
continue;
- // QTBUG-107198: Skip updates in a live resize for a better resize experience.
- if (platformWindow->isContentView() && platformWindow->view().inLiveResize) {
- const QSurface::SurfaceType surfaceType = window->surfaceType();
- const bool usesMetalLayer = surfaceType == QWindow::MetalSurface || surfaceType == QWindow::VulkanSurface;
- const bool usesNonDefaultContentsPlacement = [platformWindow->view() layerContentsPlacement]
- != NSViewLayerContentsPlacementScaleAxesIndependently;
- if (usesMetalLayer && usesNonDefaultContentsPlacement) {
- static bool deliverDisplayLinkUpdatesDuringLiveResize =
- qEnvironmentVariableIsSet("QT_MAC_DISPLAY_LINK_UPDATE_IN_RESIZE");
- if (!deliverDisplayLinkUpdatesDuringLiveResize) {
- // Must keep the link running, we do not know what the event
- // handlers for UpdateRequest (which is not sent now) would do,
- // would they trigger a new requestUpdate() or not.
- pauseUpdates = false;
- continue;
- }
- }
- }
-
platformWindow->deliverUpdateRequest();
// Another update request was triggered, keep the display link running
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index c49d83feae..97e0f633a7 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -44,6 +44,7 @@ public:
static const char *name;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
void handleSystemThemeChange();
#ifndef QT_NO_SHORTCUT
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index f4fbfadbe4..d9135c76c8 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -478,6 +478,23 @@ Qt::ColorScheme QCocoaTheme::colorScheme() const
return m_colorScheme;
}
+void QCocoaTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+ NSAppearance *appearance = nil;
+ switch (scheme) {
+ case Qt::ColorScheme::Dark:
+ appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
+ break;
+ case Qt::ColorScheme::Light:
+ appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
+ break;
+ case Qt::ColorScheme::Unknown:
+ break;
+ }
+ if (appearance != NSApp.effectiveAppearance)
+ NSApplication.sharedApplication.appearance = appearance;
+}
+
/*
Update the theme's color scheme based on the current appearance.
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index ba1bc052fb..2036d4bf4c 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -80,6 +80,8 @@ public:
QRect normalGeometry() const override;
void setCocoaGeometry(const QRect &rect);
+ QMargins safeAreaMargins() const override;
+
void setVisible(bool visible) override;
void setWindowFlags(Qt::WindowFlags flags) override;
void setWindowState(Qt::WindowStates state) override;
@@ -122,6 +124,7 @@ public:
Q_NOTIFICATION_HANDLER(NSWindowDidMoveNotification) void windowDidMove();
Q_NOTIFICATION_HANDLER(NSWindowDidResizeNotification) void windowDidResize();
+ Q_NOTIFICATION_HANDLER(NSWindowWillStartLiveResizeNotification) void windowWillStartLiveResize();
Q_NOTIFICATION_HANDLER(NSWindowDidEndLiveResizeNotification) void windowDidEndLiveResize();
Q_NOTIFICATION_HANDLER(NSWindowDidBecomeKeyNotification) void windowDidBecomeKey();
Q_NOTIFICATION_HANDLER(NSWindowDidResignKeyNotification) void windowDidResignKey();
@@ -186,6 +189,8 @@ public:
Q_DECLARE_FLAGS(RecreationReasons, RecreationReason)
Q_FLAG(RecreationReasons)
+ bool inLiveResize() const override;
+
protected:
void recreateWindowIfNeeded();
QCocoaNSWindow *createNSWindow(bool shouldBePanel);
@@ -232,6 +237,7 @@ public: // for QNSView
bool m_inSetVisible = false;
bool m_inSetGeometry = false;
bool m_inSetStyleMask = false;
+ bool m_inLiveResize = false;
QCocoaMenuBar *m_menubar = nullptr;
@@ -241,6 +247,8 @@ public: // for QNSView
int m_registerTouchCount = 0;
bool m_resizableTransientParent = false;
+ QMargins m_lastReportedSafeAreaMargins;
+
static const int NoAlertRequest;
NSInteger m_alertRequest = NoAlertRequest;
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 4a245a0f8a..b4c8ae4ba1 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -24,6 +24,7 @@
#include <qpa/qplatformscreen.h>
#include <QtGui/private/qcoregraphics_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QtGui/private/qmetallayer_p.h>
#include <QDebug>
@@ -285,6 +286,54 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
// will call QPlatformWindow::setGeometry(rect) during resize confirmation (see qnsview.mm)
}
+QMargins QCocoaWindow::safeAreaMargins() const
+{
+ // The safe area of the view reflects the area not covered by navigation
+ // bars, tab bars, toolbars, and other ancestor views that might obscure
+ // the current view (by setting additionalSafeAreaInsets). If the window
+ // uses NSWindowStyleMaskFullSizeContentView this also includes the area
+ // of the view covered by the title bar.
+ QMarginsF viewSafeAreaMargins = {
+ m_view.safeAreaInsets.left,
+ m_view.safeAreaInsets.top,
+ m_view.safeAreaInsets.right,
+ m_view.safeAreaInsets.bottom
+ };
+
+ // The screen's safe area insets represent the distances from the screen's
+ // edges at which content isn't obscured. The view's safe area margins do
+ // not include the screen's insets automatically, so we need to manually
+ // merge them.
+ auto screenRect = m_view.window.screen.frame;
+ auto screenInsets = m_view.window.screen.safeAreaInsets;
+ auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
+ [m_view.window convertRectToScreen:
+ [m_view convertRect:m_view.bounds toView:nil]]
+ );
+
+ // The margins are relative to the screen the window is on.
+ // Note that we do not want represent the area outside of the
+ // screen as being outside of the safe area.
+ QMarginsF screenSafeAreaMargins = {
+ screenInsets.left ?
+ qMax(0.0f, screenInsets.left - screenRelativeViewBounds.left())
+ : 0.0f,
+ screenInsets.top ?
+ qMax(0.0f, screenInsets.top - screenRelativeViewBounds.top())
+ : 0.0f,
+ screenInsets.right ?
+ qMax(0.0f, screenInsets.right
+ - (screenRect.size.width - screenRelativeViewBounds.right()))
+ : 0.0f,
+ screenInsets.bottom ?
+ qMax(0.0f, screenInsets.bottom
+ - (screenRect.size.height - screenRelativeViewBounds.bottom()))
+ : 0.0f
+ };
+
+ return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
+}
+
bool QCocoaWindow::startSystemMove()
{
switch (NSApp.currentEvent.type) {
@@ -514,7 +563,7 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
auto *nsWindow = transientCocoaWindow->nativeWindow();
// We only upgrade the window level for "special" windows, to work
- // around Qt Designer parenting the designer windows to the widget
+ // around Qt Widgets Designer parenting the designer windows to the widget
// palette window (QTBUG-31779). This should be fixed in designer.
if (type != Qt::Window)
windowLevel = qMax(windowLevel, nsWindow.level);
@@ -1236,8 +1285,26 @@ void QCocoaWindow::windowDidResize()
handleWindowStateChanged();
}
+void QCocoaWindow::windowWillStartLiveResize()
+{
+ // Track live resizing for all windows, including
+ // child windows, so we know if it's safe to update
+ // the window unthrottled outside of the main thread.
+ m_inLiveResize = true;
+}
+
+bool QCocoaWindow::inLiveResize() const
+{
+ // Use member variable to track this instead of reflecting
+ // NSView.inLiveResize directly, so it can be called from
+ // non-main threads.
+ return m_inLiveResize;
+}
+
void QCocoaWindow::windowDidEndLiveResize()
{
+ m_inLiveResize = false;
+
if (!isContentView())
return;
@@ -1435,6 +1502,12 @@ void QCocoaWindow::handleGeometryChange()
QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
+ // Changing the window geometry may affect the safe area margins
+ if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
+ m_lastReportedSafeAreaMargins = safeAreaMargins();
+ QWindowSystemInterface::handleSafeAreaMarginsChanged(window());
+ }
+
// Guard against processing window system events during QWindow::setGeometry
// calls, which Qt and Qt applications do not expect.
if (!m_inSetGeometry)
@@ -1617,6 +1690,23 @@ bool QCocoaWindow::updatesWithDisplayLink() const
void QCocoaWindow::deliverUpdateRequest()
{
qCDebug(lcQpaDrawing) << "Delivering update request to" << window();
+
+ if (auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(m_view.layer)) {
+ // We attempt a read lock here, so that the animation/render thread is
+ // prioritized lower than the main thread's displayLayer processing.
+ // Without this the two threads might fight over the next drawable,
+ // starving the main thread's presentation of the resized layer.
+ if (!qtMetalLayer.displayLock.tryLockForRead()) {
+ qCDebug(lcQpaDrawing) << "Deferring update request"
+ << "due to" << qtMetalLayer << "needing display";
+ return;
+ }
+
+ // But we don't hold the lock, as the update request can recurse
+ // back into setNeedsDisplay, which would deadlock.
+ qtMetalLayer.displayLock.unlock();
+ }
+
QPlatformWindow::deliverUpdateRequest();
}
@@ -1663,7 +1753,7 @@ void QCocoaWindow::setupPopupMonitor()
| NSEventMaskMouseMoved;
s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
handler:^(NSEvent *e){
- if (!QGuiApplicationPrivate::instance()->popupActive()) {
+ if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
removePopupMonitor();
return;
}
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index 48ffa5c1cc..c63a8e2879 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -36,6 +36,9 @@
#endif
#include "qcocoaintegration.h"
#include <QtGui/private/qmacmimeregistry_p.h>
+#include <QtGui/private/qmetallayer_p.h>
+
+#include <QuartzCore/CATransaction.h>
@interface QNSView (Drawing) <CALayerDelegate>
- (void)initDrawing;
diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm
index bf102e43f8..61691ab4fb 100644
--- a/src/plugins/platforms/cocoa/qnsview_drawing.mm
+++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm
@@ -75,7 +75,10 @@
// too late at this point and the QWindow will be non-functional,
// but we can at least print a warning.
if ([MTLCreateSystemDefaultDevice() autorelease]) {
- return [CAMetalLayer layer];
+ static bool allowPresentsWithTransaction =
+ !qEnvironmentVariableIsSet("QT_MTL_NO_TRANSACTION");
+ return allowPresentsWithTransaction ?
+ [QMetalLayer layer] : [CAMetalLayer layer];
} else {
qCWarning(lcQpaDrawing) << "Failed to create QWindow::MetalSurface."
<< "Metal is not supported by any of the GPUs in this system.";
@@ -222,8 +225,39 @@
return;
}
- qCDebug(lcQpaDrawing) << "[QNSView displayLayer]" << m_platformWindow->window();
- m_platformWindow->handleExposeEvent(QRectF::fromCGRect(self.bounds).toRect());
+ const auto handleExposeEvent = [&]{
+ const auto bounds = QRectF::fromCGRect(self.bounds).toRect();
+ qCDebug(lcQpaDrawing) << "[QNSView displayLayer]" << m_platformWindow->window() << bounds;
+ m_platformWindow->handleExposeEvent(bounds);
+ };
+
+ if (auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(self.layer)) {
+ const bool presentedWithTransaction = qtMetalLayer.presentsWithTransaction;
+ qtMetalLayer.presentsWithTransaction = YES;
+
+ handleExposeEvent();
+
+ // If the expose event resulted in a secondary thread requesting that its
+ // drawable should be presented on the main thread with transaction, do so.
+ if (auto mainThreadPresentation = qtMetalLayer.mainThreadPresentation) {
+ mainThreadPresentation();
+ qtMetalLayer.mainThreadPresentation = nil;
+ }
+
+ qtMetalLayer.presentsWithTransaction = presentedWithTransaction;
+
+ // We're done presenting, but we must wait to unlock the display lock
+ // until the display cycle finishes, as otherwise the render thread may
+ // step in and present before the transaction commits. The display lock
+ // is recursive, so setNeedsDisplay can be safely called in the meantime
+ // without any issue.
+ QMetaObject::invokeMethod(m_platformWindow, [qtMetalLayer]{
+ qCDebug(lcMetalLayer) << "Unlocking" << qtMetalLayer << "after finishing display-cycle";
+ qtMetalLayer.displayLock.unlock();
+ }, Qt::QueuedConnection);
+ } else {
+ handleExposeEvent();
+ }
}
@end
diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm
index 74ba6f65ac..78ad1a7e99 100644
--- a/src/plugins/platforms/cocoa/qnswindow.mm
+++ b/src/plugins/platforms/cocoa/qnswindow.mm
@@ -351,7 +351,7 @@ NSWindow<QNSWindowProtocol> *qnswindow_cast(NSWindow *window)
// not Qt). However, an active popup is expected to grab any mouse event within the
// application, so we need to handle those explicitly and trust Qt's isWindowBlocked
// implementation to eat events that shouldn't be delivered anyway.
- if (isMouseEvent(theEvent) && QGuiApplicationPrivate::instance()->popupActive()
+ if (isMouseEvent(theEvent) && QGuiApplicationPrivate::instance()->activePopupWindow()
&& QGuiApplicationPrivate::instance()->isWindowBlocked(m_platformWindow->window(), nullptr)) {
qCDebug(lcQpaWindow) << "Mouse event over modally blocked window" << m_platformWindow->window()
<< "while popup is open - redirecting";
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp
index 2866c68de2..71a59cb08f 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp
@@ -84,10 +84,12 @@ QT_WARNING_POP
case QPaintDevice::PdmDevicePixelRatio:
return 1;
case QPaintDevice::PdmDevicePixelRatioScaled:
- return qRound(devicePixelRatioFScale());
+ return int(devicePixelRatioFScale());
case QPaintDevice::PdmWidthMM:
case QPaintDevice::PdmHeightMM:
break;
+ default:
+ break;
}
return -1;
diff --git a/src/plugins/platforms/eglfs/CMakeLists.txt b/src/plugins/platforms/eglfs/CMakeLists.txt
index a0a6116a45..cb4b5d1eb9 100644
--- a/src/plugins/platforms/eglfs/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/CMakeLists.txt
@@ -92,7 +92,7 @@ endif()
qt_internal_add_plugin(QEglFSIntegrationPlugin
OUTPUT_NAME qeglfs
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES eglfs
+ DEFAULT_IF "eglfs" IN_LIST QT_QPA_PLATFORMS
SOURCES
qeglfsmain.cpp
DEFINES
diff --git a/src/plugins/platforms/ios/CMakeLists.txt b/src/plugins/platforms/ios/CMakeLists.txt
index 4cc3efc91e..51c1b52cf3 100644
--- a/src/plugins/platforms/ios/CMakeLists.txt
+++ b/src/plugins/platforms/ios/CMakeLists.txt
@@ -5,10 +5,23 @@
## QIOSIntegrationPlugin Plugin:
#####################################################################
+if(VISIONOS)
+ include(SwiftIntegration.cmake)
+
+ qt_install(TARGETS QIOSIntegrationPluginSwift
+ EXPORT "${INSTALL_CMAKE_NAMESPACE}QIOSIntegrationPluginTargets"
+ DESTINATION "${INSTALL_LIBDIR}"
+ )
+ qt_internal_add_targets_to_additional_targets_export_file(
+ TARGETS QIOSIntegrationPluginSwift
+ EXPORT_NAME_PREFIX "${INSTALL_CMAKE_NAMESPACE}QIOSIntegrationPlugin"
+ )
+endif()
+
qt_internal_add_plugin(QIOSIntegrationPlugin
OUTPUT_NAME qios
STATIC # Force static, even in shared builds
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES ios
+ DEFAULT_IF "ios" IN_LIST QT_QPA_PLATFORMS
PLUGIN_TYPE platforms
SOURCES
plugin.mm
@@ -86,3 +99,7 @@ qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT (TVOS OR VISIONOS)
)
add_subdirectory(optional)
+
+if(VISIONOS)
+ target_link_libraries(QIOSIntegrationPlugin PRIVATE QIOSIntegrationPluginSwift)
+endif()
diff --git a/src/plugins/platforms/ios/SwiftIntegration.cmake b/src/plugins/platforms/ios/SwiftIntegration.cmake
new file mode 100644
index 0000000000..d52edb3ad2
--- /dev/null
+++ b/src/plugins/platforms/ios/SwiftIntegration.cmake
@@ -0,0 +1,78 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set(CMAKE_Swift_COMPILER_TARGET arm64-apple-xros)
+if($CACHE{CMAKE_OSX_SYSROOT} MATCHES "^[a-z]+simulator$")
+ set(CMAKE_Swift_COMPILER_TARGET "${CMAKE_Swift_COMPILER_TARGET}-simulator")
+endif()
+
+cmake_policy(SET CMP0157 NEW)
+enable_language(Swift)
+
+# Verify that we have a new enough compiler
+if("${CMAKE_Swift_COMPILER_VERSION}" VERSION_LESS 5.9)
+ message(FATAL_ERROR "Swift 5.9 required for C++ interoperability")
+endif()
+
+get_target_property(QT_CORE_INCLUDES Qt6::Core INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(QT_GUI_INCLUDES Qt6::Gui INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(QT_CORE_PRIVATE_INCLUDES Qt6::CorePrivate INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(QT_GUI_PRIVATE_INCLUDES Qt6::GuiPrivate INTERFACE_INCLUDE_DIRECTORIES)
+
+set(target QIOSIntegrationPluginSwift)
+# Swift library
+set(SWIFT_SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/qiosapplication.swift"
+)
+add_library(${target} STATIC ${SWIFT_SOURCES})
+set_target_properties(${target} PROPERTIES
+ Swift_MODULE_NAME ${target})
+target_include_directories(${target} PUBLIC
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ "${QT_CORE_INCLUDES}"
+ "${QT_GUI_INCLUDES}"
+ "${QT_CORE_PRIVATE_INCLUDES}"
+ "${QT_GUI_PRIVATE_INCLUDES}"
+
+)
+target_compile_options(${target} PUBLIC
+ $<$<COMPILE_LANGUAGE:Swift>:-cxx-interoperability-mode=default>
+ $<$<COMPILE_LANGUAGE:Swift>:-Xcc -std=c++17>)
+
+# Swift to C++ bridging header
+set(SWIFT_BRIDGING_HEADER "${CMAKE_CURRENT_BINARY_DIR}/qiosswiftintegration.h")
+list(TRANSFORM QT_CORE_INCLUDES PREPEND "-I")
+list(TRANSFORM QT_GUI_INCLUDES PREPEND "-I")
+list(TRANSFORM QT_CORE_PRIVATE_INCLUDES PREPEND "-I")
+list(TRANSFORM QT_GUI_PRIVATE_INCLUDES PREPEND "-I")
+add_custom_command(
+ COMMAND
+ ${CMAKE_Swift_COMPILER} -frontend -typecheck
+ ${SWIFT_SOURCES}
+ -I ${CMAKE_CURRENT_SOURCE_DIR}
+ ${QT_CORE_INCLUDES}
+ ${QT_GUI_INCLUDES}
+ ${QT_CORE_PRIVATE_INCLUDES}
+ ${QT_GUI_PRIVATE_INCLUDES}
+ -sdk ${CMAKE_OSX_SYSROOT}
+ -module-name ${target}
+ -cxx-interoperability-mode=default
+ -Xcc -std=c++17
+ -emit-clang-header-path "${SWIFT_BRIDGING_HEADER}"
+ -target ${CMAKE_Swift_COMPILER_TARGET}
+ OUTPUT
+ "${SWIFT_BRIDGING_HEADER}"
+ DEPENDS
+ ${SWIFT_SOURCES}
+ )
+
+set(header_target "${target}Header")
+add_custom_target(${header_target}
+ DEPENDS "${SWIFT_BRIDGING_HEADER}"
+)
+# Make sure the "'__bridge_transfer' casts have no effect when not using ARC"
+# warning doesn't break warnings-are-error builds.
+target_compile_options(${target} INTERFACE
+ -Wno-error=arc-bridge-casts-disallowed-in-nonarc)
+
+add_dependencies(${target} ${header_target})
diff --git a/src/plugins/platforms/ios/module.modulemap b/src/plugins/platforms/ios/module.modulemap
new file mode 100644
index 0000000000..af42b3e1f5
--- /dev/null
+++ b/src/plugins/platforms/ios/module.modulemap
@@ -0,0 +1,4 @@
+module QIOSIntegrationPlugin {
+ header "qiosapplicationdelegate.h"
+ header "qiosintegration.h"
+}
diff --git a/src/plugins/platforms/ios/qiosapplication.swift b/src/plugins/platforms/ios/qiosapplication.swift
new file mode 100644
index 0000000000..9eb172a896
--- /dev/null
+++ b/src/plugins/platforms/ios/qiosapplication.swift
@@ -0,0 +1,198 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import SwiftUI
+import CompositorServices
+import QIOSIntegrationPlugin
+import RealityKit
+
+struct QIOSSwiftApplication: App {
+ @UIApplicationDelegateAdaptor private var appDelegate: QIOSApplicationDelegate
+
+ var body: some SwiftUI.Scene {
+ WindowGroup() {
+ ImmersiveSpaceControlView()
+ }
+
+ ImmersiveSpace(id: "QIOSImmersiveSpace") {
+ CompositorLayer(configuration: QIOSLayerConfiguration()) { layerRenderer in
+ QIOSIntegration.instance().renderCompositorLayer(layerRenderer)
+
+ // Handle any events in the scene.
+ layerRenderer.onSpatialEvent = { eventCollection in
+ QIOSIntegration.instance().handleSpatialEvents(jsonStringFromEventCollection(eventCollection))
+ }
+ }
+ }
+ // CompositorLayer immersive spaces are always full, and should not need
+ // to set the immersion style, but lacking this we get a warning in the
+ // console about not being able to "configure an immersive space with
+ // selected style 'AutomaticImmersionStyle' since it is not in the list
+ // of supported styles for this type of content: 'FullImmersionStyle'."
+ .immersionStyle(selection: .constant(.full), in: .full)
+ }
+}
+
+public struct QIOSLayerConfiguration: CompositorLayerConfiguration {
+ public func makeConfiguration(capabilities: LayerRenderer.Capabilities,
+ configuration: inout LayerRenderer.Configuration) {
+ // Use reflection to pull out underlying C handles
+ // FIXME: Use proper bridging APIs when available
+ let capabilitiesMirror = Mirror(reflecting: capabilities)
+ let configurationMirror = Mirror(reflecting: configuration)
+ QIOSIntegration.instance().configureCompositorLayer(
+ capabilitiesMirror.descendant("c_capabilities") as? cp_layer_renderer_capabilities_t,
+ configurationMirror.descendant("box", "value") as? cp_layer_renderer_configuration_t
+ )
+ }
+}
+
+public func runSwiftAppMain() {
+ QIOSSwiftApplication.main()
+}
+
+public class ImmersiveState: ObservableObject {
+ static let shared = ImmersiveState()
+ @Published var showImmersiveSpace: Bool = false
+}
+
+struct ImmersiveSpaceControlView: View {
+ @ObservedObject private var immersiveState = ImmersiveState.shared
+
+ @Environment(\.openImmersiveSpace) var openImmersiveSpace
+ @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
+
+ var body: some View {
+ VStack {}
+ .onChange(of: immersiveState.showImmersiveSpace) { _, newValue in
+ Task {
+ if newValue {
+ await openImmersiveSpace(id: "QIOSImmersiveSpace")
+ } else {
+ await dismissImmersiveSpace()
+ }
+ }
+ }
+ }
+}
+
+public class ImmersiveSpaceManager : NSObject {
+ @objc public static func openImmersiveSpace() {
+ ImmersiveState.shared.showImmersiveSpace = true
+ }
+
+ @objc public static func dismissImmersiveSpace() {
+ ImmersiveState.shared.showImmersiveSpace = false
+ }
+}
+
+extension SpatialEventCollection.Event.Kind: Encodable {
+ enum CodingKeys: String, CodingKey {
+ case touch
+ case directPinch
+ case indirectPinch
+ case pointer
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ switch self {
+ case .touch:
+ try container.encode("touch")
+ case .directPinch:
+ try container.encode("directPinch")
+ case .indirectPinch:
+ try container.encode("indirectPinch")
+ case .pointer:
+ try container.encode("pointer")
+ @unknown default:
+ try container.encode("unknown")
+ }
+ }
+}
+extension SpatialEventCollection.Event.Phase: Encodable {
+ enum CodingKeys: String, CodingKey {
+ case active
+ case ending
+ case cancled
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ switch self {
+ case .active:
+ try container.encode("active")
+ case .ended:
+ try container.encode("ended")
+ case .cancelled:
+ try container.encode("canceled")
+ @unknown default:
+ try container.encode("unknown")
+ }
+ }
+}
+extension SpatialEventCollection.Event.InputDevicePose: Encodable {
+ enum CodingKeys: String, CodingKey {
+ case altitude
+ case azimuth
+ case pose3D
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(altitude.radians, forKey: .altitude)
+ try container.encode(azimuth.radians, forKey: .azimuth)
+ try container.encode(pose3D, forKey: .pose3D)
+ }
+}
+
+extension SpatialEventCollection.Event: Encodable {
+ enum CodingKeys: String, CodingKey {
+ case id
+ case timestamp
+ case kind
+ case location
+ case phase
+ case modifierKeys
+ case inputDevicePose
+ case location3D
+ case selectionRay
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(id.hashValue, forKey: .id)
+ try container.encode(timestamp, forKey: .timestamp)
+ try container.encode(kind, forKey: .kind)
+ try container.encode(location, forKey: .location)
+ try container.encode(phase, forKey: .phase)
+ try container.encode(modifierKeys.rawValue, forKey: .modifierKeys)
+ try container.encode(inputDevicePose, forKey: .inputDevicePose)
+ try container.encode(location3D, forKey: .location3D)
+ try container.encode(selectionRay, forKey: .selectionRay)
+ }
+}
+
+extension SpatialEventCollection: Encodable {
+ enum CodingKeys: String, CodingKey {
+ case events
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(Array(self), forKey: .events)
+ }
+}
+
+func jsonStringFromEventCollection(_ eventCollection: SpatialEventCollection) -> String {
+ let encoder = JSONEncoder()
+ encoder.dateEncodingStrategy = .iso8601
+
+ do {
+ let jsonData = try encoder.encode(eventCollection)
+ return String(data: jsonData, encoding: .utf8) ?? "{}"
+ } catch {
+ print("Failed to encode event collection: \(error)")
+ return "{}"
+ }
+}
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.h b/src/plugins/platforms/ios/qiosapplicationdelegate.h
index 39bb9fdedb..7e12d64cbf 100644
--- a/src/plugins/platforms/ios/qiosapplicationdelegate.h
+++ b/src/plugins/platforms/ios/qiosapplicationdelegate.h
@@ -1,6 +1,9 @@
// 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
+#ifndef QIOSAPPLICATIONDELEGATE_H
+#define QIOSAPPLICATIONDELEGATE_H
+
#import <UIKit/UIKit.h>
#import <QtGui/QtGui>
@@ -8,3 +11,5 @@
@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate>
@end
+
+#endif // QIOSAPPLICATIONDELEGATE_H
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h
index b40024ec19..5eee0556f5 100644
--- a/src/plugins/platforms/ios/qioseventdispatcher.h
+++ b/src/plugins/platforms/ios/qioseventdispatcher.h
@@ -16,6 +16,8 @@ public:
static QIOSEventDispatcher* create();
bool processPostedEvents() override;
+ static bool isQtApplication();
+
protected:
explicit QIOSEventDispatcher(QObject *parent = nullptr);
};
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm
index 24d9d88294..710a834bfd 100644
--- a/src/plugins/platforms/ios/qioseventdispatcher.mm
+++ b/src/plugins/platforms/ios/qioseventdispatcher.mm
@@ -5,6 +5,10 @@
#include "qiosapplicationdelegate.h"
#include "qiosglobal.h"
+#if defined(Q_OS_VISIONOS)
+#include "qiosswiftintegration.h"
+#endif
+
#include <QtCore/qprocessordetection.h>
#include <QtCore/private/qcoreapplication_p.h>
#include <QtCore/private/qthread_p.h>
@@ -173,12 +177,16 @@ namespace
QAppleLogActivity UIApplicationMain;
QAppleLogActivity applicationDidFinishLaunching;
} logActivity;
+
+ static bool s_isQtApplication = false;
}
using namespace QT_PREPEND_NAMESPACE(QtPrivate);
extern "C" int qt_main_wrapper(int argc, char *argv[])
{
+ s_isQtApplication = true;
+
@autoreleasepool {
size_t defaultStackSize = 512 * kBytesPerKiloByte; // Same as secondary threads
@@ -202,8 +210,16 @@ extern "C" int qt_main_wrapper(int argc, char *argv[])
logActivity.UIApplicationMain = QT_APPLE_LOG_ACTIVITY(
lcEventDispatcher().isDebugEnabled(), "UIApplicationMain").enter();
+#if defined(Q_OS_VISIONOS)
+ Q_UNUSED(argc);
+ Q_UNUSED(argv);
+ qCDebug(lcEventDispatcher) << "Starting Swift app";
+ QIOSIntegrationPluginSwift::runSwiftAppMain();
+ Q_UNREACHABLE();
+#else
qCDebug(lcEventDispatcher) << "Running UIApplicationMain";
return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class]));
+#endif
}
}
@@ -424,6 +440,11 @@ QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent)
QWindowSystemInterface::setSynchronousWindowSystemEvents(true);
}
+bool QIOSEventDispatcher::isQtApplication()
+{
+ return s_isQtApplication;
+}
+
/*!
Override of the CoreFoundation posted events runloop source callback
so that we can send window system (QPA) events in addition to sending
diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm
index 25ccf2961b..1722e09aaa 100644
--- a/src/plugins/platforms/ios/qiosglobal.mm
+++ b/src/plugins/platforms/ios/qiosglobal.mm
@@ -5,6 +5,8 @@
#include "qiosapplicationdelegate.h"
#include "qiosviewcontroller.h"
#include "qiosscreen.h"
+#include "quiwindow.h"
+#include "qioseventdispatcher.h"
#include <QtCore/private/qcore_mac_p.h>
@@ -17,17 +19,13 @@ Q_LOGGING_CATEGORY(lcQpaWindowScene, "qt.qpa.window.scene");
bool isQtApplication()
{
- if (qt_apple_isApplicationExtension())
- return false;
-
// Returns \c true if the plugin is in full control of the whole application. This means
// that we control the application delegate and the top view controller, and can take
// actions that impacts all parts of the application. The opposite means that we are
// embedded inside a native iOS application, and should be more focused on playing along
// with native UIControls, and less inclined to change structures that lies outside the
// scope of our QWindows/UIViews.
- static bool isQt = ([qt_apple_sharedApplication().delegate isKindOfClass:[QIOSApplicationDelegate class]]);
- return isQt;
+ return QIOSEventDispatcher::isQtApplication();
}
bool isRunningOnVisionOS()
@@ -126,9 +124,15 @@ UIView *rootViewForScreen(QScreen *screen)
Q_UNUSED(iosScreen);
#endif
- UIWindow *uiWindow = windowScene.keyWindow;
- if (!uiWindow && windowScene.windows.count)
- uiWindow = windowScene.windows[0];
+ UIWindow *uiWindow = qt_objc_cast<QUIWindow*>(windowScene.keyWindow);
+ if (!uiWindow) {
+ for (UIWindow *win in windowScene.windows) {
+ if (qt_objc_cast<QUIWindow*>(win)) {
+ uiWindow = win;
+ break;
+ }
+ }
+ }
return uiWindow.rootViewController.view;
}
diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h
index 2c7d33cc94..6c2014d048 100644
--- a/src/plugins/platforms/ios/qiosintegration.h
+++ b/src/plugins/platforms/ios/qiosintegration.h
@@ -16,11 +16,24 @@
#include "qiostextinputoverlay.h"
#endif
+#if defined(Q_OS_VISIONOS)
+#include <swift/bridging>
+#endif
+
QT_BEGIN_NAMESPACE
+using namespace QNativeInterface;
+
class QIOSServices;
-class QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration
+class
+#if defined(Q_OS_VISIONOS)
+ SWIFT_IMMORTAL_REFERENCE
+#endif
+QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration
+#if defined(Q_OS_VISIONOS)
+ , public QVisionOSApplication
+#endif
{
Q_OBJECT
public:
@@ -77,6 +90,18 @@ public:
QIOSApplicationState applicationState;
+#if defined(Q_OS_VISIONOS)
+ void openImmersiveSpace() override;
+ void dismissImmersiveSpace() override;
+
+ using CompositorLayer = QVisionOSApplication::ImmersiveSpaceCompositorLayer;
+ void setImmersiveSpaceCompositorLayer(CompositorLayer *layer) override;
+
+ void configureCompositorLayer(cp_layer_renderer_capabilities_t, cp_layer_renderer_configuration_t);
+ void renderCompositorLayer(cp_layer_renderer_t);
+ void handleSpatialEvents(const char *jsonString);
+#endif
+
private:
QPlatformFontDatabase *m_fontDatabase;
#if QT_CONFIG(clipboard)
@@ -90,6 +115,10 @@ private:
#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
QIOSTextInputOverlay m_textInputOverlay;
#endif
+
+#if defined(Q_OS_VISIONOS)
+ CompositorLayer *m_immersiveSpaceCompositorLayer = nullptr;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm
index 7cd21f83f6..76173ce830 100644
--- a/src/plugins/platforms/ios/qiosintegration.mm
+++ b/src/plugins/platforms/ios/qiosintegration.mm
@@ -17,6 +17,10 @@
#include "qiosservices.h"
#include "qiosoptionalplugininterface.h"
+#if defined(Q_OS_VISIONOS)
+#include "qiosswiftintegration.h"
+#endif
+
#include <QtGui/qpointingdevice.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qrhibackingstore_p.h>
@@ -296,6 +300,52 @@ void QIOSIntegration::setApplicationBadge(qint64 number)
// ---------------------------------------------------------
+#if defined(Q_OS_VISIONOS)
+void QIOSIntegration::openImmersiveSpace()
+{
+ [ImmersiveSpaceManager openImmersiveSpace];
+}
+
+void QIOSIntegration::dismissImmersiveSpace()
+{
+ [ImmersiveSpaceManager dismissImmersiveSpace];
+}
+
+void QIOSIntegration::setImmersiveSpaceCompositorLayer(CompositorLayer *layer)
+{
+ m_immersiveSpaceCompositorLayer = layer;
+}
+
+void QIOSIntegration::configureCompositorLayer(cp_layer_renderer_capabilities_t capabilities,
+ cp_layer_renderer_configuration_t configuration)
+{
+ if (m_immersiveSpaceCompositorLayer)
+ m_immersiveSpaceCompositorLayer->configure(capabilities, configuration);
+}
+
+void QIOSIntegration::renderCompositorLayer(cp_layer_renderer_t renderer)
+{
+ if (m_immersiveSpaceCompositorLayer)
+ m_immersiveSpaceCompositorLayer->render(renderer);
+}
+
+void QIOSIntegration::handleSpatialEvents(const char *jsonString)
+{
+ if (m_immersiveSpaceCompositorLayer) {
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(QByteArray(jsonString), &error);
+ if (error.error != QJsonParseError::NoError) {
+ qWarning() << "Error parsing JSON: " << error.errorString();
+ return;
+ }
+ m_immersiveSpaceCompositorLayer->handleSpatialEvents(doc.object());
+ }
+}
+
+#endif
+
+// ---------------------------------------------------------
+
void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
{
if (!window || !window->handle())
diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h
index f0a404a61a..70e2c37ff1 100644
--- a/src/plugins/platforms/ios/qiostheme.h
+++ b/src/plugins/platforms/ios/qiostheme.h
@@ -4,6 +4,8 @@
#ifndef QIOSTHEME_H
#define QIOSTHEME_H
+#import <UIKit/UIKit.h>
+
#include <QtCore/QHash>
#include <QtGui/QPalette>
#include <qpa/qplatformtheme.h>
@@ -22,6 +24,7 @@ public:
QVariant themeHint(ThemeHint hint) const override;
Qt::ColorScheme colorScheme() const override;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
QPlatformMenuItem* createPlatformMenuItem() const override;
@@ -37,9 +40,11 @@ public:
static const char *name;
static void initializeSystemPalette();
+ static void applyTheme(UIWindow *window);
private:
static QPalette s_systemPalette;
+ static inline Qt::ColorScheme s_colorSchemeOverride = Qt::ColorScheme::Unknown;
QMacNotificationObserver m_contentSizeCategoryObserver;
};
diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm
index 3853de9cf1..0b420a875a 100644
--- a/src/plugins/platforms/ios/qiostheme.mm
+++ b/src/plugins/platforms/ios/qiostheme.mm
@@ -154,6 +154,9 @@ Qt::ColorScheme QIOSTheme::colorScheme() const
// the OS reports itself as always being in dark mode.
return Qt::ColorScheme::Dark;
#else
+ if (s_colorSchemeOverride != Qt::ColorScheme::Unknown)
+ return s_colorSchemeOverride;
+
// Set the appearance based on the QUIWindow
// Fallback to the UIScreen if no window is created yet
UIUserInterfaceStyle appearance = UIScreen.mainScreen.traitCollection.userInterfaceStyle;
@@ -171,6 +174,38 @@ Qt::ColorScheme QIOSTheme::colorScheme() const
#endif
}
+void QIOSTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+#if defined(Q_OS_VISIONOS)
+ Q_UNUSED(scheme);
+#else
+ s_colorSchemeOverride = scheme;
+
+ const NSArray<UIWindow *> *windows = qt_apple_sharedApplication().windows;
+ for (UIWindow *window in windows) {
+ // don't apply a theme to windows we don't own
+ if (qt_objc_cast<QUIWindow*>(window))
+ applyTheme(window);
+ }
+#endif
+}
+
+void QIOSTheme::applyTheme(UIWindow *window)
+{
+ const UIUserInterfaceStyle style = []{
+ switch (s_colorSchemeOverride) {
+ case Qt::ColorScheme::Dark:
+ return UIUserInterfaceStyleDark;
+ case Qt::ColorScheme::Light:
+ return UIUserInterfaceStyleLight;
+ case Qt::ColorScheme::Unknown:
+ return UIUserInterfaceStyleUnspecified;
+ }
+ }();
+
+ window.overrideUserInterfaceStyle = style;
+}
+
const QFont *QIOSTheme::font(Font type) const
{
const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm
index 39b2cb8a50..fa54f61967 100644
--- a/src/plugins/platforms/ios/quiaccessibilityelement.mm
+++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm
@@ -10,6 +10,8 @@
#include "uistrings_p.h"
#include "qioswindow.h"
+#include <QtGui/private/qaccessiblebridgeutils_p.h>
+
QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
@implementation QMacAccessibilityElement
@@ -70,6 +72,17 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
return iface->text(QAccessible::Name).toNSString();
}
+
+- (NSString*)accessibilityIdentifier
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (!iface) {
+ qWarning() << "invalid accessible interface for: " << self.axid;
+ return @"";
+ }
+ return QAccessibleBridgeUtils::accessibleId(iface).toNSString();
+}
+
- (NSString*)accessibilityHint
{
QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
diff --git a/src/plugins/platforms/ios/quiwindow.mm b/src/plugins/platforms/ios/quiwindow.mm
index 7c910b6d9e..783e243e10 100644
--- a/src/plugins/platforms/ios/quiwindow.mm
+++ b/src/plugins/platforms/ios/quiwindow.mm
@@ -22,6 +22,15 @@
return self;
}
+- (instancetype)initWithWindowScene:(UIWindowScene *)windowScene
+{
+ if ((self = [super initWithWindowScene:windowScene]))
+ self->_sendingEvent = NO;
+
+ QIOSTheme::applyTheme(self);
+ return self;
+}
+
- (void)sendEvent:(UIEvent *)event
{
QScopedValueRollback<BOOL> sendingEvent(self->_sendingEvent, YES);
diff --git a/src/plugins/platforms/linuxfb/CMakeLists.txt b/src/plugins/platforms/linuxfb/CMakeLists.txt
index 9f75f53828..ba18cea50c 100644
--- a/src/plugins/platforms/linuxfb/CMakeLists.txt
+++ b/src/plugins/platforms/linuxfb/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QLinuxFbIntegrationPlugin
OUTPUT_NAME qlinuxfb
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES linuxfb
+ DEFAULT_IF "linuxfb" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qlinuxfbintegration.cpp qlinuxfbintegration.h
diff --git a/src/plugins/platforms/minimal/CMakeLists.txt b/src/plugins/platforms/minimal/CMakeLists.txt
index f3683deccf..18d8828134 100644
--- a/src/plugins/platforms/minimal/CMakeLists.txt
+++ b/src/plugins/platforms/minimal/CMakeLists.txt
@@ -10,7 +10,7 @@ qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
qt_internal_add_plugin(QMinimalIntegrationPlugin
OUTPUT_NAME qminimal
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES minimal
+ DEFAULT_IF "minimal" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qminimalbackingstore.cpp qminimalbackingstore.h
diff --git a/src/plugins/platforms/minimalegl/CMakeLists.txt b/src/plugins/platforms/minimalegl/CMakeLists.txt
index a6ec8be781..b93f325b8f 100644
--- a/src/plugins/platforms/minimalegl/CMakeLists.txt
+++ b/src/plugins/platforms/minimalegl/CMakeLists.txt
@@ -10,7 +10,7 @@ qt_find_package(EGL)
qt_internal_add_plugin(QMinimalEglIntegrationPlugin
OUTPUT_NAME qminimalegl
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES minimalegl
+ DEFAULT_IF "minimalegl" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qminimaleglintegration.cpp qminimaleglintegration.h
diff --git a/src/plugins/platforms/offscreen/CMakeLists.txt b/src/plugins/platforms/offscreen/CMakeLists.txt
index 09ad9a384d..907c2c9cc6 100644
--- a/src/plugins/platforms/offscreen/CMakeLists.txt
+++ b/src/plugins/platforms/offscreen/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QOffscreenIntegrationPlugin
OUTPUT_NAME qoffscreen
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES offscreen
+ DEFAULT_IF "offscreen" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qoffscreencommon.cpp qoffscreencommon.h
diff --git a/src/plugins/platforms/qnx/CMakeLists.txt b/src/plugins/platforms/qnx/CMakeLists.txt
index 9fb412d8a4..0f9deaa00b 100644
--- a/src/plugins/platforms/qnx/CMakeLists.txt
+++ b/src/plugins/platforms/qnx/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QQnxIntegrationPlugin
OUTPUT_NAME qqnx
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES qnx
+ DEFAULT_IF "qnx" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp main.h
qqnxabstractcover.h
diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp
index cc10f6a00e..b308c956f2 100644
--- a/src/plugins/platforms/qnx/qqnxintegration.cpp
+++ b/src/plugins/platforms/qnx/qqnxintegration.cpp
@@ -49,6 +49,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qrhibackingstore_p.h>
#if !defined(QT_NO_OPENGL)
#include "qqnxglcontext.h"
@@ -328,8 +329,19 @@ QPlatformWindow *QQnxIntegration::createPlatformWindow(QWindow *window) const
QPlatformBackingStore *QQnxIntegration::createPlatformBackingStore(QWindow *window) const
{
- qCDebug(lcQpaQnx) << Q_FUNC_INFO;
- return new QQnxRasterBackingStore(window);
+ QSurface::SurfaceType surfaceType = window->surfaceType();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO << surfaceType;
+ switch (surfaceType) {
+ case QSurface::RasterSurface:
+ return new QQnxRasterBackingStore(window);
+#if !defined(QT_NO_OPENGL)
+ // Return a QRhiBackingStore for non-raster surface windows
+ case QSurface::OpenGLSurface:
+ return new QRhiBackingStore(window);
+#endif
+ default:
+ return nullptr;
+ }
}
#if !defined(QT_NO_OPENGL)
diff --git a/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
index 719e5c45e6..406487f1e9 100644
--- a/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
+++ b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
@@ -6,7 +6,7 @@ qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
qt_internal_add_plugin(QVkKhrDisplayIntegrationPlugin
OUTPUT_NAME qvkkhrdisplay
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES vkkhrdisplay
+ DEFAULT_IF "vkkhrdisplay" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qvkkhrdisplayintegration.cpp qvkkhrdisplayintegration.h
diff --git a/src/plugins/platforms/vnc/CMakeLists.txt b/src/plugins/platforms/vnc/CMakeLists.txt
index 25cb399bd0..34370807ae 100644
--- a/src/plugins/platforms/vnc/CMakeLists.txt
+++ b/src/plugins/platforms/vnc/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QVncIntegrationPlugin
OUTPUT_NAME qvnc
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES vnc
+ DEFAULT_IF "vnc" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qvnc.cpp qvnc_p.h
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt
index 90c7ec2118..775946aaf9 100644
--- a/src/plugins/platforms/wasm/CMakeLists.txt
+++ b/src/plugins/platforms/wasm/CMakeLists.txt
@@ -7,7 +7,7 @@
qt_internal_add_plugin(QWasmIntegrationPlugin
OUTPUT_NAME qwasm
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES wasm
+ DEFAULT_IF "wasm" IN_LIST QT_QPA_PLATFORMS
PLUGIN_TYPE platforms
SOURCES
main.cpp
diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
index 4c3cb46ba3..2e430176be 100644
--- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp
+++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
@@ -284,6 +284,10 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac
element = document.call<emscripten::val>("createElement", std::string("div"));
}
+ QString id = QAccessibleBridgeUtils::accessibleId(iface);
+ if (iface->role() != QAccessible::PageTabList)
+ element.call<void>("setAttribute", std::string("id"), id.toStdString());
+
return element;
}();
diff --git a/src/plugins/platforms/wasm/qwasmdom.cpp b/src/plugins/platforms/wasm/qwasmdom.cpp
index 6b2b3d0933..96790ca71f 100644
--- a/src/plugins/platforms/wasm/qwasmdom.cpp
+++ b/src/plugins/platforms/wasm/qwasmdom.cpp
@@ -104,7 +104,10 @@ void DataTransfer::toMimeDataWithFile(std::function<void(QMimeData *)> callback)
if (--m_remainingItemCount > 0)
return;
- mimeData->setUrls(fileUrls);
+ QList<QUrl> allUrls;
+ allUrls.append(mimeData->urls());
+ allUrls.append(fileUrls);
+ mimeData->setUrls(allUrls);
m_callback(mimeData);
@@ -201,7 +204,11 @@ void DataTransfer::toMimeDataWithFile(std::function<void(QMimeData *)> callback)
mimeContext->mimeData->setHtml(data);
else if (itemMimeType.isEmpty() || itemMimeType == "text/plain")
mimeContext->mimeData->setText(data); // the type can be empty
- else {
+ else if (itemMimeType.isEmpty() || itemMimeType == "text/uri-list") {
+ QList<QUrl> urls;
+ urls.append(data);
+ mimeContext->mimeData->setUrls(urls);
+ } else {
// TODO improve encoding
if (data.startsWith("QB64")) {
data.remove(0, 4);
diff --git a/src/plugins/platforms/wasm/qwasmevent.cpp b/src/plugins/platforms/wasm/qwasmevent.cpp
index c1d6ce3a2a..e418263655 100644
--- a/src/plugins/platforms/wasm/qwasmevent.cpp
+++ b/src/plugins/platforms/wasm/qwasmevent.cpp
@@ -106,7 +106,7 @@ KeyEvent::KeyEvent(EventType type, emscripten::val event) : Event(type, event)
const auto code = event["code"].as<std::string>();
const auto webKey = event["key"].as<std::string>();
deadKey = isDeadKeyEvent(webKey.c_str());
-
+ autoRepeat = event["repeat"].as<bool>();
modifiers = KeyboardModifier::getForEvent(event);
key = webKeyToQtKey(code, webKey, deadKey, modifiers);
diff --git a/src/plugins/platforms/wasm/qwasmevent.h b/src/plugins/platforms/wasm/qwasmevent.h
index 6ada5393e3..bd0fb39f11 100644
--- a/src/plugins/platforms/wasm/qwasmevent.h
+++ b/src/plugins/platforms/wasm/qwasmevent.h
@@ -153,6 +153,7 @@ struct KeyEvent : public Event
QFlags<Qt::KeyboardModifier> modifiers;
bool deadKey;
QString text;
+ bool autoRepeat;
};
struct MouseEvent : public Event
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index b8197c5113..99e9bb22f1 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -128,9 +128,8 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
});
emscripten::val keyFocusWindow;
- if (QWasmIntegration::get()->inputContext()) {
- QWasmInputContext *wasmContext =
- static_cast<QWasmInputContext *>(QWasmIntegration::get()->inputContext());
+ if (QWasmInputContext *wasmContext =
+ qobject_cast<QWasmInputContext *>(QWasmIntegration::get()->inputContext())) {
// if there is an touchscreen input context,
// use that window for key input
keyFocusWindow = wasmContext->m_inputElement;
@@ -502,7 +501,7 @@ bool QWasmWindow::processKey(const KeyEvent &event)
const auto result = QWindowSystemInterface::handleKeyEvent(
0, event.type == EventType::KeyDown ? QEvent::KeyPress : QEvent::KeyRelease, event.key,
- event.modifiers, event.text);
+ event.modifiers, event.text, event.autoRepeat);
return clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded
? ProceedToNativeEvent
: result;
diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html
index 702ea1f59d..6e93955552 100644
--- a/src/plugins/platforms/wasm/wasm_shell.html
+++ b/src/plugins/platforms/wasm/wasm_shell.html
@@ -1,3 +1,8 @@
+<!--
+Copyright (C) 2024 The Qt Company Ltd.
+SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+-->
+
<!doctype html>
<html lang="en-us">
<head>
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt
index 4b92317978..8cd84e208b 100644
--- a/src/plugins/platforms/windows/CMakeLists.txt
+++ b/src/plugins/platforms/windows/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QWindowsIntegrationPlugin
OUTPUT_NAME qwindows
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES windows
+ DEFAULT_IF "windows" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qtwindowsglobal.h
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index de65a2171d..8c0261d568 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -201,6 +201,8 @@ QWindowsContext::~QWindowsContext()
if (d->m_powerDummyWindow)
DestroyWindow(d->m_powerDummyWindow);
+ d->m_screenManager.destroyWindow();
+
unregisterWindowClasses();
if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) {
#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
diff --git a/src/plugins/platforms/windows/qwindowsmimeregistry.cpp b/src/plugins/platforms/windows/qwindowsmimeregistry.cpp
index 8d147e8fa0..00cd792b5c 100644
--- a/src/plugins/platforms/windows/qwindowsmimeregistry.cpp
+++ b/src/plugins/platforms/windows/qwindowsmimeregistry.cpp
@@ -944,7 +944,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *
if (canGetData(CF_DIB, pDataObj)) {
qCDebug(lcQpaMime) << "Decoding DIB";
QImage img;
- QByteArray data = getData(CF_DIBV5, pDataObj);
+ QByteArray data = getData(CF_DIB, pDataObj);
QBuffer buffer(&data);
if (readDib(buffer, img))
return img;
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index a50f9fd4b0..1f22fb4f60 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -698,11 +698,15 @@ void QWindowsScreenManager::initialize()
handleScreenChanges();
}
-QWindowsScreenManager::~QWindowsScreenManager()
+void QWindowsScreenManager::destroyWindow()
{
+ qCDebug(lcQpaScreen) << "Destroying display change observer" << m_displayChangeObserver;
DestroyWindow(m_displayChangeObserver);
+ m_displayChangeObserver = nullptr;
}
+QWindowsScreenManager::~QWindowsScreenManager() = default;
+
bool QWindowsScreenManager::isSingleScreen()
{
return QWindowsContext::instance()->screenManager().screens().size() < 2;
diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h
index 0467ab2a0c..ea6a29efe3 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.h
+++ b/src/plugins/platforms/windows/qwindowsscreen.h
@@ -105,6 +105,7 @@ public:
QWindowsScreenManager();
void initialize();
+ void destroyWindow();
~QWindowsScreenManager();
void clearScreens();
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index e8a324aedb..b6017c7692 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -301,6 +301,9 @@ void QWindowsTheme::populateLightSystemBasePalette(QPalette &result)
void QWindowsTheme::populateDarkSystemBasePalette(QPalette &result)
{
+ QColor foreground, background,
+ accent, accentDark, accentDarker, accentDarkest,
+ accentLight, accentLighter, accentLightest;
#if QT_CONFIG(cpp_winrt)
using namespace winrt::Windows::UI::ViewManagement;
const auto settings = UISettings();
@@ -308,32 +311,37 @@ void QWindowsTheme::populateDarkSystemBasePalette(QPalette &result)
// We have to craft a palette from these colors. The settings.UIElementColor(UIElementType) API
// returns the old system colors, not the dark mode colors. If the background is black (which it
// usually), then override it with a dark gray instead so that we can go up and down the lightness.
- const QColor foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground));
- const QColor background = [&settings]() -> QColor {
- auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background));
- if (systemBackground == Qt::black)
- systemBackground = QColor(0x1E, 0x1E, 0x1E);
- return systemBackground;
- }();
-
- const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
- const QColor accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
- const QColor accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
- const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
- const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
- const QColor accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
- const QColor accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
-#else
- const QColor foreground = Qt::white;
- const QColor background = QColor(0x1E, 0x1E, 0x1E);
- const QColor accent = qt_accentColor(AccentColorNormal);
- const QColor accentDark = accent.darker(120);
- const QColor accentDarker = accentDark.darker(120);
- const QColor accentDarkest = accentDarker.darker(120);
- const QColor accentLight = accent.lighter(120);
- const QColor accentLighter = accentLight.lighter(120);
- const QColor accentLightest = accentLighter.lighter(120);
+ if (QWindowsTheme::queryColorScheme() == Qt::ColorScheme::Dark) {
+ // the system is actually running in dark mode, so UISettings will give us dark colors
+ foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground));
+ background = [&settings]() -> QColor {
+ auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background));
+ if (systemBackground == Qt::black)
+ systemBackground = QColor(0x1E, 0x1E, 0x1E);
+ return systemBackground;
+ }();
+
+ accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
+ accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
+ accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
+ accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
+ accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
+ accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
+ accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
+ } else
#endif
+ {
+ // If the system is running in light mode, then we need to make up our own dark palette
+ foreground = Qt::white;
+ background = QColor(0x1E, 0x1E, 0x1E);
+ accent = qt_accentColor(AccentColorNormal);
+ accentDark = accent.darker(120);
+ accentDarker = accentDark.darker(120);
+ accentDarkest = accentDarker.darker(120);
+ accentLight = accent.lighter(120);
+ accentLighter = accentLight.lighter(120);
+ accentLightest = accentLighter.lighter(120);
+ }
const QColor linkColor = accent;
const QColor buttonColor = background.lighter(200);
@@ -549,13 +557,25 @@ Qt::ColorScheme QWindowsTheme::effectiveColorScheme()
{
if (queryHighContrast())
return Qt::ColorScheme::Unknown;
- return s_colorScheme;
+ if (s_colorSchemeOverride != Qt::ColorScheme::Unknown)
+ return s_colorSchemeOverride;
+ if (s_colorScheme != Qt::ColorScheme::Unknown)
+ return s_colorScheme;
+ return queryColorScheme();
+}
+
+void QWindowsTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+ s_colorSchemeOverride = scheme;
+ handleSettingsChanged();
}
void QWindowsTheme::handleSettingsChanged()
{
- const auto newColorScheme = QWindowsTheme::queryColorScheme();
- const bool colorSchemeChanged = newColorScheme != QWindowsTheme::s_colorScheme;
+ const auto oldColorScheme = s_colorScheme;
+ s_colorScheme = Qt::ColorScheme::Unknown; // make effectiveColorScheme() query registry
+ const auto newColorScheme = effectiveColorScheme();
+ const bool colorSchemeChanged = newColorScheme != oldColorScheme;
s_colorScheme = newColorScheme;
auto integration = QWindowsIntegration::instance();
integration->updateApplicationBadge();
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index 6109122944..a89fb1e5bd 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -32,6 +32,7 @@ public:
QVariant themeHint(ThemeHint) const override;
Qt::ColorScheme colorScheme() const override;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
static void handleSettingsChanged();
@@ -79,6 +80,8 @@ private:
static QWindowsTheme *m_instance;
static inline Qt::ColorScheme s_colorScheme = Qt::ColorScheme::Unknown;
+ static inline Qt::ColorScheme s_colorSchemeOverride = Qt::ColorScheme::Unknown;
+
QPalette *m_palettes[NPalettes];
QFont *m_fonts[NFonts];
QList<QSize> m_fileIconSizes;
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 5d96d40af5..4b7ce0a979 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -850,7 +850,8 @@ static inline bool shouldApplyDarkFrame(const QWindow *w)
{
if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
return false;
- // the application has explicitly opted out of dark frames
+
+ // the user of the application has explicitly opted out of dark frames
if (!QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames))
return false;
@@ -2481,6 +2482,11 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
GetWindowPlacement(m_data.hwnd, &windowPlacement);
const RECT geometry = RECTfromQRect(m_data.restoreGeometry);
windowPlacement.rcNormalPosition = geometry;
+ // Even if the window is hidden, windowPlacement's showCmd is not SW_HIDE, so change it
+ // manually to avoid unhiding a hidden window with the subsequent call to
+ // SetWindowPlacement().
+ if (!isVisible())
+ windowPlacement.showCmd = SW_HIDE;
SetWindowPlacement(m_data.hwnd, &windowPlacement);
}
// QTBUG-17548: We send expose events when receiving WM_Paint, but for
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
index 1abb412ccd..5892493281 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
@@ -127,6 +127,9 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event
return;
switch (event->type()) {
+ case QAccessible::Announcement:
+ QWindowsUiaMainProvider::raiseNotification(static_cast<QAccessibleAnnouncementEvent *>(event));
+ break;
case QAccessible::Focus:
QWindowsUiaMainProvider::notifyFocusChange(event);
break;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index 95ddbcced6..07cd522746 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -23,6 +23,7 @@
#include "qwindowsuiaprovidercache.h"
#include <QtCore/qloggingcategory.h>
+#include <QtGui/private/qaccessiblebridgeutils_p.h>
#include <QtGui/qaccessible.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qwindow.h>
@@ -204,6 +205,24 @@ void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
}
}
+void QWindowsUiaMainProvider::raiseNotification(QAccessibleAnnouncementEvent *event)
+{
+ if (QAccessibleInterface *accessible = event->accessibleInterface()) {
+ if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
+ BSTR message = bStrFromQString(event->message());
+ QAccessible::AnnouncementPriority prio = event->priority();
+ NotificationProcessing processing = (prio == QAccessible::AnnouncementPriority::Assertive)
+ ? NotificationProcessing_ImportantAll
+ : NotificationProcessing_All;
+ BSTR activityId = bStrFromQString(QString::fromLatin1(""));
+ UiaRaiseNotificationEvent(provider, NotificationKind_Other, processing, message, activityId);
+
+ ::SysFreeString(message);
+ ::SysFreeString(activityId);
+ }
+ }
+}
+
HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
{
HRESULT result = QComObject::QueryInterface(iid, iface);
@@ -370,6 +389,93 @@ void QWindowsUiaMainProvider::fillVariantArrayForRelation(QAccessibleInterface*
pRetVal->parray = elements;
}
+void QWindowsUiaMainProvider::setAriaProperties(QAccessibleInterface *accessible, VARIANT *pRetVal)
+{
+ Q_ASSERT(accessible);
+
+ QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface();
+ if (!attributesIface)
+ return;
+
+ QString ariaString;
+ const QList<QAccessible::Attribute> attrKeys = attributesIface->attributeKeys();
+ for (qsizetype i = 0; i < attrKeys.size(); ++i) {
+ if (i != 0)
+ ariaString += QStringLiteral(";");
+ const QAccessible::Attribute key = attrKeys.at(i);
+ const QVariant value = attributesIface->attributeValue(key);
+ // see "Core Accessibility API Mappings" spec: https://www.w3.org/TR/core-aam-1.2/
+ switch (key) {
+ case QAccessible::Attribute::Custom:
+ {
+ // forward custom attributes as-is
+ Q_ASSERT((value.canConvert<QHash<QString, QString>>()));
+ const QHash<QString, QString> attrMap = value.value<QHash<QString, QString>>();
+ for (auto [name, val] : attrMap.asKeyValueRange()) {
+ if (name != *attrMap.keyBegin())
+ ariaString += QStringLiteral(";");
+ ariaString += name + QStringLiteral("=") + val;
+ }
+ break;
+ }
+ case QAccessible::Attribute::Level:
+ Q_ASSERT(value.canConvert<int>());
+ ariaString += QStringLiteral("level=") + QString::number(value.toInt());
+ break;
+ default:
+ break;
+ }
+ }
+
+ setVariantString(ariaString, pRetVal);
+}
+
+void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal)
+{
+ Q_ASSERT(accessible);
+
+ QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface();
+ if (!attributesIface)
+ return;
+
+ // currently, only heading styles are implemented here
+ if (accessible->role() != QAccessible::Role::Heading)
+ return;
+
+ const QVariant levelVariant = attributesIface->attributeValue(QAccessible::Attribute::Level);
+ if (!levelVariant.isValid())
+ return;
+
+ Q_ASSERT(levelVariant.canConvert<int>());
+ // UIA only has styles for heading levels 1-9
+ const int level = levelVariant.toInt();
+ if (level < 1 || level > 9)
+ return;
+
+ const int styleId = styleIdForHeadingLevel(level);
+ setVariantI4(styleId, pRetVal);
+}
+
+int QWindowsUiaMainProvider::styleIdForHeadingLevel(int headingLevel)
+{
+ // only heading levels 1-9 have a corresponding UIA style ID
+ Q_ASSERT(headingLevel > 0 && headingLevel <= 9);
+
+ static constexpr int styles[] = {
+ StyleId_Heading1,
+ StyleId_Heading2,
+ StyleId_Heading3,
+ StyleId_Heading4,
+ StyleId_Heading5,
+ StyleId_Heading6,
+ StyleId_Heading7,
+ StyleId_Heading8,
+ StyleId_Heading9,
+ };
+
+ return styles[headingLevel - 1];
+}
+
HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal)
{
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp;
@@ -393,9 +499,12 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
// Accelerator key.
setVariantString(accessible->text(QAccessible::Accelerator), pRetVal);
break;
+ case UIA_AriaPropertiesPropertyId:
+ setAriaProperties(accessible, pRetVal);
+ break;
case UIA_AutomationIdPropertyId:
// Automation ID, which can be used by tools to select a specific control in the UI.
- setVariantString(automationIdForAccessible(accessible), pRetVal);
+ setVariantString(QAccessibleBridgeUtils::accessibleId(accessible), pRetVal);
break;
case UIA_ClassNamePropertyId:
// Class name.
@@ -493,31 +602,15 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
setVariantString(name, pRetVal);
break;
}
+ case UIA_StyleIdAttributeId:
+ setStyle(accessible, pRetVal);
+ break;
default:
break;
}
return S_OK;
}
-// Generates an ID based on the name of the controls and their parents.
-QString QWindowsUiaMainProvider::automationIdForAccessible(const QAccessibleInterface *accessible)
-{
- QString result;
- if (accessible) {
- QObject *obj = accessible->object();
- while (obj) {
- QString name = obj->objectName();
- if (name.isEmpty())
- return result;
- if (!result.isEmpty())
- result.prepend(u'.');
- result.prepend(name);
- obj = obj->parent();
- }
- }
- return result;
-}
-
HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal)
{
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
index 99db0ed318..8ea343e425 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
@@ -34,6 +34,7 @@ public:
static void notifyNameChange(QAccessibleEvent *event);
static void notifySelectionChange(QAccessibleEvent *event);
static void notifyTextChange(QAccessibleEvent *event);
+ static void raiseNotification(QAccessibleAnnouncementEvent *event);
// IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override;
@@ -58,8 +59,11 @@ public:
HRESULT STDMETHODCALLTYPE GetFocus(IRawElementProviderFragment **pRetVal) override;
private:
- QString automationIdForAccessible(const QAccessibleInterface *accessible);
static void fillVariantArrayForRelation(QAccessibleInterface *accessible, QAccessible::Relation relation, VARIANT *pRetVal);
+ static void setAriaProperties(QAccessibleInterface *accessible, VARIANT *pRetVal);
+ static void setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal);
+ /** Returns the UIA style ID for a heading level from 1 to 9. */
+ static int styleIdForHeadingLevel(int headingLevel);
static QMutex m_mutex;
};
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
index 1593a07202..6954a881d0 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
@@ -69,6 +69,14 @@ HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *pProvider, EVE
return func.invoke(pProvider, id);
}
+HRESULT WINAPI UiaRaiseNotificationEvent(
+ IRawElementProviderSimple *pProvider, NotificationKind notificationKind,
+ NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId)
+{
+ static auto func = winapi_func("uiautomationcore", FN(UiaRaiseNotificationEvent));
+ return func.invoke(pProvider, notificationKind, notificationProcessing, displayString, activityId);
+}
+
#endif // defined(__MINGW32__) || defined(__MINGW64__)
#endif // QT_CONFIG(accessibility)
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h
index a192b9b0fb..4eb37bafa0 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h
@@ -14,8 +14,19 @@
#define UIA_SelectionPattern2Id 10034
#define UIA_IsReadOnlyAttributeId 40015
#define UIA_StrikethroughStyleAttributeId 40026
+#define UIA_StyleIdAttributeId 40034
#define UIA_CaretPositionAttributeId 40038
+#define StyleId_Heading1 70001
+#define StyleId_Heading2 70002
+#define StyleId_Heading3 70003
+#define StyleId_Heading4 70004
+#define StyleId_Heading5 70005
+#define StyleId_Heading6 70006
+#define StyleId_Heading7 70007
+#define StyleId_Heading8 70008
+#define StyleId_Heading9 70009
+
enum CaretPosition {
CaretPosition_Unknown = 0,
CaretPosition_EndOfLine = 1,
diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt
index e8fb442dd4..96758e7181 100644
--- a/src/plugins/platforms/xcb/CMakeLists.txt
+++ b/src/plugins/platforms/xcb/CMakeLists.txt
@@ -164,7 +164,7 @@ endif()
qt_internal_add_plugin(QXcbIntegrationPlugin
OUTPUT_NAME qxcb
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES xcb
+ DEFAULT_IF "xcb" IN_LIST QT_QPA_PLATFORMS
SOURCES
qxcbmain.cpp
DEFINES
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 40e2f47354..77157595b8 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -61,7 +61,7 @@ protected:
// get the list of targets from the current clipboard owner - we do this
// once so that multiple calls to this function don't require multiple
// server round trips...
- that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::AtomTARGETS));
+ that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::AtomTARGETS)).value_or(QByteArray());
if (format_atoms.size() > 0) {
const xcb_atom_t *targets = (const xcb_atom_t *) format_atoms.data();
@@ -91,7 +91,7 @@ protected:
{
auto requestedType = type;
if (fmt.isEmpty() || isEmpty())
- return QByteArray();
+ return QVariant();
(void)formats(); // trigger update of format list
@@ -108,7 +108,11 @@ protected:
if (fmtatom == 0)
return QVariant();
- return mimeConvertToFormat(m_clipboard->connection(), fmtatom, m_clipboard->getDataInFormat(modeAtom, fmtatom), fmt, requestedType, hasUtf8);
+ const std::optional<QByteArray> result = m_clipboard->getDataInFormat(modeAtom, fmtatom);
+ if (!result.has_value())
+ return QVariant();
+
+ return mimeConvertToFormat(m_clipboard->connection(), fmtatom, result.value(), fmt, requestedType, hasUtf8);
}
private:
@@ -776,7 +780,7 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i
return nullptr;
}
-QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm)
+std::optional<QByteArray> QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm)
{
QByteArray buf;
QByteArray tmp_buf;
@@ -841,17 +845,16 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
// could consider next request to be still part of this timed out request
setRequestor(0);
- return QByteArray();
+ return std::nullopt;
}
-QByteArray QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAtom)
+std::optional<QByteArray> QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAtom)
{
return getSelection(modeAtom, fmtAtom, atom(QXcbAtom::Atom_QT_SELECTION));
}
-QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t time)
+std::optional<QByteArray> QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t time)
{
- QByteArray buf;
xcb_window_t win = requestor();
if (time == 0) time = connection()->time();
@@ -866,17 +869,19 @@ QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target,
free(ge);
if (no_selection)
- return buf;
+ return std::nullopt;
xcb_atom_t type;
+ QByteArray buf;
if (clipboardReadProperty(win, property, true, &buf, nullptr, &type, nullptr)) {
if (type == atom(QXcbAtom::AtomINCR)) {
int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
- buf = clipboardReadIncrementalProperty(win, property, nbytes, false);
+ return clipboardReadIncrementalProperty(win, property, nbytes, false);
}
+ return buf;
}
- return buf;
+ return std::nullopt;
}
#endif // QT_NO_CLIPBOARD
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h
index 79c48a0af5..134daf4be7 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.h
+++ b/src/plugins/platforms/xcb/qxcbclipboard.h
@@ -67,13 +67,13 @@ public:
void handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event);
bool clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format);
- QByteArray clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm);
+ std::optional<QByteArray> clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm);
- QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom);
+ std::optional<QByteArray> getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom);
bool handlePropertyNotify(const xcb_generic_event_t *event);
- QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0);
+ std::optional<QByteArray> getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0);
int increment() const { return m_maxPropertyRequestDataBytes; }
int clipboardTimeout() const { return clipboard_timeout; }
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index 6e5dd770b9..7a44e4bb4f 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -1306,32 +1306,33 @@ QVariant QXcbDropData::retrieveData_sys(const QString &mimetype, QMetaType reque
QVariant QXcbDropData::xdndObtainData(const QByteArray &format, QMetaType requestedType) const
{
- QByteArray result;
-
QXcbConnection *c = drag->connection();
QXcbWindow *xcb_window = c->platformWindowFromId(drag->xdnd_dragsource);
if (xcb_window && drag->currentDrag() && xcb_window->window()->type() != Qt::Desktop) {
QMimeData *data = drag->currentDrag()->mimeData();
if (data->hasFormat(QLatin1StringView(format)))
- result = data->data(QLatin1StringView(format));
- return result;
+ return data->data(QLatin1StringView(format));
+ return QVariant();
}
QList<xcb_atom_t> atoms = drag->xdnd_types;
bool hasUtf8 = false;
xcb_atom_t a = mimeAtomForFormat(c, QLatin1StringView(format), requestedType, atoms, &hasUtf8);
if (a == XCB_NONE)
- return result;
+ return QVariant();
#ifndef QT_NO_CLIPBOARD
if (c->selectionOwner(c->atom(QXcbAtom::AtomXdndSelection)) == XCB_NONE)
- return result; // should never happen?
+ return QVariant(); // should never happen?
xcb_atom_t xdnd_selection = c->atom(QXcbAtom::AtomXdndSelection);
- result = c->clipboard()->getSelection(xdnd_selection, a, xdnd_selection, drag->targetTime());
+ const std::optional<QByteArray> result = c->clipboard()->getSelection(xdnd_selection, a, xdnd_selection, drag->targetTime());
+ if (!result.has_value())
+ return QVariant();
+ return mimeConvertToFormat(c, a, result.value(), QLatin1StringView(format), requestedType, hasUtf8);
+#else
+ return QVariant();
#endif
-
- return mimeConvertToFormat(c, a, result, QLatin1StringView(format), requestedType, hasUtf8);
}
bool QXcbDropData::hasFormat_sys(const QString &format) const
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 06f4b66edb..0ce337726e 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -23,6 +23,7 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
: QXcbObject(connection)
, m_screen(screen)
, m_number(number)
+ , m_xSettings(new QXcbXSettings(this))
{
const QByteArray cmAtomName = "_NET_WM_CM_S" + QByteArray::number(m_number);
m_net_wm_cm_atom = connection->internAtom(cmAtomName.constData());
@@ -129,10 +130,6 @@ void QXcbVirtualDesktop::setPrimaryScreen(QPlatformScreen *s)
QXcbXSettings *QXcbVirtualDesktop::xSettings() const
{
- if (!m_xSettings) {
- QXcbVirtualDesktop *self = const_cast<QXcbVirtualDesktop *>(this);
- self->m_xSettings = new QXcbXSettings(self);
- }
return m_xSettings;
}
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index f6eed5e227..6df7aa9507 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
@@ -3,11 +3,13 @@
#include "qsql_ibase_p.h"
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qendian.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qtimezone.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qdebug.h>
#include <QtCore/qlist.h>
+#include <QtCore/private/qlocale_tools_p.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qmap.h>
#include <QtCore/qmutex.h>
@@ -41,6 +43,10 @@ using namespace Qt::StringLiterals;
#define blr_boolean_dtype blr_bool
#endif
+#if (defined(QT_SUPPORTS_INT128) || defined(Q_CC_MSVC)) && (FB_API_VER >= 40)
+#define IBASE_INT128_SUPPORTED
+#endif
+
constexpr qsizetype QIBaseChunkSize = SHRT_MAX / 2;
#if (FB_API_VER >= 40)
@@ -100,6 +106,9 @@ static void initDA(XSQLDA *sqlda)
case SQL_TIMESTAMP:
#if (FB_API_VER >= 40)
case SQL_TIMESTAMP_TZ:
+#ifdef IBASE_INT128_SUPPORTED
+ case SQL_INT128:
+#endif
#endif
case SQL_TYPE_TIME:
case SQL_TYPE_DATE:
@@ -347,21 +356,14 @@ public:
QSqlQuery qry(q->createResult());
qry.setForwardOnly(true);
qry.exec(QString("select * from RDB$TIME_ZONES"_L1));
- if (qry.lastError().type()) {
- q->setLastError(QSqlError(
- QCoreApplication::translate("QIBaseDriver",
- "failed to query time zone mapping from system table"),
- qry.lastError().databaseText(),
- QSqlError::StatementError,
- qry.lastError().nativeErrorCode()));
-
+ if (qry.lastError().isValid()) {
+ qCInfo(lcIbase) << "Table 'RDB$TIME_ZONES' not found - not timezone support available";
return;
}
while (qry.next()) {
- auto record = qry.record();
- quint16 fbTzId = record.value(0).value<quint16>();
- QByteArray ianaId = record.value(1).toByteArray().simplified();
+ quint16 fbTzId = qry.value(0).value<quint16>();
+ QByteArray ianaId = qry.value(1).toByteArray().simplified();
qFbTzIdToIanaIdMap()->insert(fbTzId, ianaId);
qIanaIdToFbTzIdMap()->insert(ianaId, fbTzId);
}
@@ -408,6 +410,32 @@ protected:
QSqlRecord record() const override;
template<typename T>
+ static QString numberToHighPrecision(T val, int scale)
+ {
+ const bool negative = val < 0;
+ QString number;
+#ifdef IBASE_INT128_SUPPORTED
+ if constexpr (std::is_same_v<qinternalint128, T>) {
+ number = negative ? qint128toBasicLatin(val * -1)
+ : qint128toBasicLatin(val);
+ } else
+#endif
+ number = negative ? QString::number(qAbs(val))
+ : QString::number(val);
+ auto len = number.size();
+ scale *= -1;
+ if (scale >= len) {
+ number = QString(scale - len + 1, u'0') + number;
+ len = number.size();
+ }
+ const auto sepPos = len - scale;
+ number = number.left(sepPos) + u'.' + number.mid(sepPos);
+ if (negative)
+ number = u'-' + number;
+ return number;
+ }
+
+ template<typename T>
QVariant applyScale(T val, int scale) const
{
if (scale >= 0)
@@ -420,28 +448,36 @@ protected:
return QVariant(qint64(val * pow(10.0, scale)));
case QSql::LowPrecisionDouble:
return QVariant(double(val * pow(10.0, scale)));
- case QSql::HighPrecision: {
- const bool negative = val < 0;
- QString number;
- if constexpr (std::is_signed_v<T> || negative)
- number = QString::number(qAbs(val));
- else
- number = QString::number(val);
- auto len = number.size();
- scale *= -1;
- if (scale >= len) {
- number = QString(scale - len + 1, u'0') + number;
- len = number.size();
- }
- const auto sepPos = len - scale;
- number = number.left(sepPos) + u'.' + number.mid(sepPos);
- if (negative)
- number = u'-' + number;
- return QVariant(number);
- }
+ case QSql::HighPrecision:
+ return QVariant(numberToHighPrecision(val, scale));
}
return QVariant(val);
}
+
+ template<typename T>
+ void setWithScale(const QVariant &val, int scale, char *data)
+ {
+ auto ptr = reinterpret_cast<T *>(data);
+ if (scale < 0) {
+ double d = floor(0.5 + val.toDouble() * pow(10.0, scale * -1));
+#ifdef IBASE_INT128_SUPPORTED
+ if constexpr (std::is_same_v<qinternalint128, T>) {
+ quint64 lower = quint64(d);
+ quint64 tmp = quint64(std::numeric_limits<quint32>::max()) + 1;
+ d /= tmp;
+ d /= tmp;
+ quint64 higher = quint64(d);
+ qinternalint128 result = higher;
+ result <<= 64;
+ result += lower;
+ *ptr = static_cast<T>(result);
+ } else
+#endif
+ *ptr = static_cast<T>(d);
+ }
+ else
+ *ptr = val.value<T>();
+ }
};
class QIBaseResultPrivate: public QSqlCachedResultPrivate
@@ -1102,32 +1138,25 @@ bool QIBaseResult::exec()
}
switch(d->inda->sqlvar[para].sqltype & ~1) {
case SQL_INT64:
- if (d->inda->sqlvar[para].sqlscale < 0)
- *((qint64*)d->inda->sqlvar[para].sqldata) =
- (qint64)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
- else
- *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
+ setWithScale<qint64>(val, d->inda->sqlvar[para].sqlscale, d->inda->sqlvar[para].sqldata);
break;
+#ifdef IBASE_INT128_SUPPORTED
+ case SQL_INT128:
+ setWithScale<qinternalint128>(val, d->inda->sqlvar[para].sqlscale,
+ d->inda->sqlvar[para].sqldata);
+ break;
+#endif
case SQL_LONG:
- if (d->inda->sqlvar[para].sqllen == 4) {
- if (d->inda->sqlvar[para].sqlscale < 0)
- *((qint32*)d->inda->sqlvar[para].sqldata) =
- (qint32)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
- else
- *((qint32*)d->inda->sqlvar[para].sqldata) = (qint32)val.toInt();
- } else {
- *((qint64*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
- }
+ if (d->inda->sqlvar[para].sqllen == 4)
+ setWithScale<qint32>(val, d->inda->sqlvar[para].sqlscale, d->inda->sqlvar[para].sqldata);
+ else
+ setWithScale<qint64>(val, 0, d->inda->sqlvar[para].sqldata);
break;
case SQL_SHORT:
- if (d->inda->sqlvar[para].sqlscale < 0)
- *((short*)d->inda->sqlvar[para].sqldata) =
- (short)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
- else
- *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
+ setWithScale<qint16>(val, d->inda->sqlvar[para].sqlscale, d->inda->sqlvar[para].sqldata);
break;
case SQL_FLOAT:
- *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
+ *((float*)d->inda->sqlvar[para].sqldata) = val.toFloat();
break;
case SQL_DOUBLE:
*((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
@@ -1282,6 +1311,17 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
row[idx] = applyScale(val, scale);
break;
}
+#ifdef IBASE_INT128_SUPPORTED
+ case SQL_INT128: {
+ Q_ASSERT(d->sqlda->sqlvar[i].sqllen == sizeof(qinternalint128));
+ const qinternalint128 val128 = qFromUnaligned<qinternalint128>(buf);
+ const auto scale = d->sqlda->sqlvar[i].sqlscale;
+ row[idx] = numberToHighPrecision(val128, scale);
+ if (numericalPrecisionPolicy() != QSql::HighPrecision)
+ row[idx] = applyScale(row[idx].toDouble(), 0);
+ break;
+ }
+#endif
case SQL_LONG:
if (d->sqlda->sqlvar[i].sqllen == 4) {
const auto val = *(qint32 *)buf;
diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp
index 784f491681..63663c74aa 100644
--- a/src/plugins/styles/modernwindows/qwindows11style.cpp
+++ b/src/plugins/styles/modernwindows/qwindows11style.cpp
@@ -9,12 +9,15 @@
#include <qstyleoption.h>
#include <qpainter.h>
#include <QGraphicsDropShadowEffect>
+#include <QLatin1StringView>
#include <QtWidgets/qcombobox.h>
#include <QtWidgets/qcommandlinkbutton.h>
#include <QtWidgets/qgraphicsview.h>
#include <QtWidgets/qlistview.h>
#include <QtWidgets/qmenu.h>
+#if QT_CONFIG(mdiarea)
#include <QtWidgets/qmdiarea.h>
+#endif
#include <QtWidgets/qtextedit.h>
#include <QtWidgets/qtreeview.h>
@@ -25,6 +28,7 @@ QT_BEGIN_NAMESPACE
const static int topLevelRoundingRadius = 8; //Radius for toplevel items like popups for round corners
const static int secondLevelRoundingRadius = 4; //Radius for second level items like hovered menu item round corners
+constexpr QLatin1StringView originalWidthProperty("_q_windows11_style_original_width");
enum WINUI3Color {
subtleHighlightColor, //Subtle highlight based on alpha used for hovered elements
@@ -134,6 +138,8 @@ static void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbut
*/
QWindows11Style::QWindows11Style() : QWindowsVistaStyle(*new QWindows11StylePrivate)
{
+ highContrastTheme = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Unknown;
+ colorSchemeIndex = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
}
/*!
@@ -142,6 +148,8 @@ QWindows11Style::QWindows11Style() : QWindowsVistaStyle(*new QWindows11StylePriv
*/
QWindows11Style::QWindows11Style(QWindows11StylePrivate &dd) : QWindowsVistaStyle(dd)
{
+ highContrastTheme = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Unknown;
+ colorSchemeIndex = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
}
/*!
@@ -1738,6 +1746,10 @@ int QWindows11Style::styleHint(StyleHint hint, const QStyleOption *opt,
return 0;
case QStyle::SH_ItemView_ShowDecorationSelected:
return 1;
+ case QStyle::SH_Slider_AbsoluteSetButtons:
+ return Qt::LeftButton;
+ case QStyle::SH_Slider_PageSetButtons:
+ return 0;
default:
return QWindowsVistaStyle::styleHint(hint, opt, widget, returnData);
}
@@ -2036,6 +2048,13 @@ void QWindows11Style::polish(QWidget* widget)
QLineEdit *le = cb->lineEdit();
le->setFrame(false);
}
+ } else if (widget->inherits("QAbstractSpinBox")) {
+ const int minWidth = 2 * 24 + 40;
+ const int originalWidth = widget->size().width();
+ if (originalWidth < minWidth) {
+ widget->resize(minWidth, widget->size().height());
+ widget->setProperty(originalWidthProperty.constData(), originalWidth);
+ }
} else if (widget->inherits("QAbstractButton") || widget->inherits("QToolButton")) {
widget->setAutoFillBackground(false);
} else if (qobject_cast<QGraphicsView *>(widget) && !qobject_cast<QTextEdit *>(widget)) {
@@ -2043,7 +2062,11 @@ void QWindows11Style::polish(QWidget* widget)
pal.setColor(QPalette::Base, pal.window().color());
widget->setPalette(pal);
} else if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
- scrollarea && !qobject_cast<QMdiArea *>(widget)) {
+ scrollarea
+#if QT_CONFIG(mdiarea)
+ && !qobject_cast<QMdiArea *>(widget)
+#endif
+ ) {
QPalette pal = scrollarea->viewport()->palette();
const QPalette originalPalette = pal;
pal.setColor(scrollarea->viewport()->backgroundRole(), Qt::transparent);
@@ -2062,11 +2085,22 @@ void QWindows11Style::unpolish(QWidget *widget)
{
QWindowsVistaStyle::unpolish(widget);
if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget);
- scrollarea && !qobject_cast<QMdiArea *>(widget)) {
+ scrollarea
+#if QT_CONFIG(mdiarea)
+ && !qobject_cast<QMdiArea *>(widget)
+#endif
+ ) {
const QPalette pal = scrollarea->viewport()->property("_q_original_background_palette").value<QPalette>();
scrollarea->viewport()->setPalette(pal);
scrollarea->viewport()->setProperty("_q_original_background_palette", QVariant());
}
+ if (widget->inherits("QAbstractSpinBox")) {
+ const QVariant originalWidth = widget->property(originalWidthProperty.constData());
+ if (originalWidth.isValid()) {
+ widget->resize(originalWidth.toInt(), widget->size().height());
+ widget->setProperty(originalWidthProperty.constData(), QVariant());
+ }
+ }
}
/*