diff options
Diffstat (limited to 'src/plugins/platforms')
28 files changed, 367 insertions, 214 deletions
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index a1edf49da12..6154f4121d2 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/androidjniaccessibility.cpp @@ -470,7 +470,6 @@ namespace QtAndroidAccessibility case QAccessible::Role::Link: { if (state.checkable) - // There is also a android.widget.Switch for which we have no match. return QStringLiteral("android.widget.ToggleButton"); return QStringLiteral("android.widget.Button"); } @@ -478,6 +477,8 @@ namespace QtAndroidAccessibility // As of android/accessibility/utils/Role.java::getRole a CheckBox // is NOT android.widget.CheckBox return QStringLiteral("android.widget.CompoundButton"); + case QAccessible::Role::Switch: + return QStringLiteral("android.widget.Switch"); case QAccessible::Role::Clock: return QStringLiteral("android.widget.TextClock"); case QAccessible::Role::ComboBox: @@ -725,9 +726,18 @@ namespace QtAndroidAccessibility break; } + float min = info.minValue.toFloat(); + float max = info.maxValue.toFloat(); + float current = info.currentValue.toFloat(); + if (info.role == QAccessible::ProgressBar) { + rangeType = 2; // RANGE_TYPE_PERCENT + current = 100 * (current - min) / (max - min); + min = 0.0f; + max = 100.0f; + } + QJniObject rangeInfo("android/view/accessibility/AccessibilityNodeInfo$RangeInfo", - "(IFFF)V", rangeType, info.minValue.toFloat(), - info.maxValue.toFloat(), info.currentValue.toFloat()); + "(IFFF)V", rangeType, min, max, current); if (rangeInfo.isValid()) { env->CallVoidMethod(node, m_setRangeInfoMethodID, rangeInfo.object()); diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 8c5335d9c31..9059fab757b 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -359,20 +359,41 @@ namespace QtAndroid static bool initJavaReferences(QJniEnvironment &env); -static void initializeBackends() +static bool initAndroidQpaPlugin(JNIEnv *jenv, jobject object) { + Q_UNUSED(jenv) + Q_UNUSED(object) + + // Init all the Java refs, if they haven't already been initialized. They get initialized + // when the library is loaded, but in case Qt is terminated, they are cleared, and in case + // Qt is then started again JNI_OnLoad will not be called again, since the library is already + // loaded - in that case we need to init again here, hence the check. + // TODO QTBUG-130614 QtCore also inits some Java references in qjnihelpers - we probably + // want to reset those, too. + QJniEnvironment qEnv; + if (!qEnv.isValid()) { + qCritical() << "Failed to initialize the JNI Environment"; + return false; + } + + if (!initJavaReferences(qEnv)) + return false; + + m_androidPlatformIntegration = nullptr; + // File engine handler instantiation registers the handler m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler(); m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler(); m_androidApkFileEngineHandler = new QAndroidApkFileEngineHandler(); m_backendRegister = new AndroidBackendRegister(); -} -static bool initCleanupHandshakeSemaphores() -{ - return sem_init(&m_exitSemaphore, 0, 0) != -1 - && sem_init(&m_stopQtSemaphore, 0, 0) != -1; + if (sem_init(&m_exitSemaphore, 0, 0) == -1 && sem_init(&m_stopQtSemaphore, 0, 0) == -1) { + qCritical() << "Failed to init Qt application cleanup semaphores"; + return false; + } + + return true; } static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring paramsString) @@ -391,23 +412,6 @@ static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring param vm->AttachCurrentThread(&env, &args); } - // Init all the Java refs, if they haven't already been initialized. They get initialized - // when the library is loaded, but in case Qt is terminated, they are cleared, and in case - // Qt is then started again JNI_OnLoad will not be called again, since the library is already - // loaded - in that case we need to init again here, hence the check. - // TODO QTBUG-130614 QtCore also inits some Java references in qjnihelpers - we probably - // want to reset those, too. - QJniEnvironment qEnv; - if (!qEnv.isValid()) { - qCritical() << "Failed to initialize the JNI Environment"; - return; - } - if (!initJavaReferences(qEnv)) - return; - - m_androidPlatformIntegration = nullptr; - initializeBackends(); - const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString()); const int argc = argsList.size(); QVarLengthArray<char *> argv(argc + 1); @@ -444,11 +448,6 @@ static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring param return; } - if (!initCleanupHandshakeSemaphores()) { - qCritical() << "Failed to init Qt application cleanup semaphores"; - return; - } - // Register type for invokeMethod() calls. qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation"); @@ -458,13 +457,6 @@ static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring param startQtAndroidPluginCalled.fetchAndAddRelease(1); - QtNative::callStaticMethod("setStarted", true); - - // The service must wait until the QCoreApplication starts, - // otherwise onBind will be called too early. - if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication()) - QtAndroidPrivate::waitForServiceSetup(); - const int ret = m_main(argc, argv.data()); qInfo() << "main() returned" << ret; @@ -540,6 +532,15 @@ static void clearJavaReferences(JNIEnv *env) } } +static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/) +{ + Q_UNUSED(env); + // The service must wait until the QCoreApplication starts otherwise onBind will be + // called too early + if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication()) + QtAndroidPrivate::waitForServiceSetup(); +} + static void terminateQtNativeApplication(JNIEnv *env, jclass /*clazz*/) { // QAndroidEventDispatcherStopper is stopped when the user uses the task manager @@ -730,8 +731,10 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent) } static JNINativeMethod methods[] = { + { "initAndroidQpaPlugin", "()Z", (void *)initAndroidQpaPlugin }, { "startQtNativeApplication", "(Ljava/lang/String;)V", (void *)startQtNativeApplication }, { "terminateQtNativeApplication", "()V", (void *)terminateQtNativeApplication }, + { "waitForServiceSetup", "()V", (void *)waitForServiceSetup }, { "updateApplicationState", "(I)V", (void *)updateApplicationState }, { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult }, { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent }, diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index ecbbddb2e36..1d755139b46 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -82,9 +82,6 @@ static bool hasValidFocusObject() if (!m_androidInputContext) return false; - if (!m_androidInputContext->isInputPanelVisible()) - return false; - const auto focusObject = m_androidInputContext->focusObject(); if (!focusObject) return false; diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index c8555cdc659..f64742ff133 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -291,7 +291,7 @@ void QAndroidPlatformScreen::topVisibleWindowChanged() if (w && w->handle()) { QAndroidPlatformWindow *platformWindow = static_cast<QAndroidPlatformWindow *>(w->handle()); if (platformWindow) { - platformWindow->updateSystemUiVisibility(); + platformWindow->updateSystemUiVisibility(w->windowStates(), w->flags()); platformWindow->updateFocusedEditText(); } } diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp index 822a5357107..0d8673aac03 100644 --- a/src/plugins/platforms/android/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp @@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus") -Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager") +Q_DECLARE_JNI_CLASS(QtWindowInsetsController, "org/qtproject/qt/android/QtWindowInsetsController") using namespace Qt::StringLiterals; @@ -448,9 +448,9 @@ void QAndroidPlatformTheme::requestColorScheme(Qt::ColorScheme scheme) const auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>(); iface->runOnAndroidMainThread([=]() { bool isLight = scheme == Qt::ColorScheme::Light; - QtJniTypes::QtDisplayManager::callStaticMethod("setStatusBarColorHint", + QtJniTypes::QtWindowInsetsController::callStaticMethod("setStatusBarColorHint", iface->context().object<QtJniTypes::Activity>(), isLight); - QtJniTypes::QtDisplayManager::callStaticMethod("setNavigationBarColorHint", + QtJniTypes::QtWindowInsetsController::callStaticMethod("setNavigationBarColorHint", iface->context().object<QtJniTypes::Activity>(), isLight); }); } diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp index 937839ace0c..c4245998772 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp @@ -22,6 +22,7 @@ Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface Q_DECLARE_JNI_CLASS(QtInputConnectionListener, "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener") Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtWindowInterface") +Q_DECLARE_JNI_CLASS(QtWindowInsetsController, "org/qtproject/qt/android/QtWindowInsetsController") QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) : QPlatformWindow(window), m_nativeQtWindow(nullptr), @@ -55,15 +56,12 @@ void QAndroidPlatformWindow::initialize() isForeignWindow(), m_nativeParentQtWindow, listener); m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId"); - m_windowFlags = Qt::Widget; - m_windowState = Qt::WindowNoState; // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save // the fact that it's a raster window for now m_isRaster = window->surfaceType() == QSurface::RasterSurface; - setWindowState(window->windowStates()); // the following is in relation to the virtual geometry - const bool forceMaximize = m_windowState & (Qt::WindowMaximized | Qt::WindowFullScreen); + const bool forceMaximize = window->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen); const QRect nativeScreenGeometry = platformScreen()->availableGeometry(); if (forceMaximize) { setGeometry(nativeScreenGeometry); @@ -122,7 +120,7 @@ void QAndroidPlatformWindow::raise() QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason); return; } - updateSystemUiVisibility(); + updateSystemUiVisibility(window()->windowStates(), window()->flags()); platformScreen()->raise(this); } @@ -166,13 +164,13 @@ void QAndroidPlatformWindow::setVisible(bool visible) if (!visible && window() == qGuiApp->focusWindow()) { platformScreen()->topVisibleWindowChanged(); } else { - updateSystemUiVisibility(); - if ((m_windowState & Qt::WindowFullScreen) - || (window()->flags() & Qt::ExpandedClientAreaHint)) { + const Qt::WindowStates states = window()->windowStates(); + const Qt::WindowFlags flags = window()->flags(); + updateSystemUiVisibility(states, flags); + if (states & Qt::WindowFullScreen || flags & Qt::ExpandedClientAreaHint) setGeometry(platformScreen()->geometry()); - } else if (m_windowState & Qt::WindowMaximized) { + else if (states & Qt::WindowMaximized) setGeometry(platformScreen()->availableGeometry()); - } requestActivateWindow(); } } @@ -187,27 +185,18 @@ void QAndroidPlatformWindow::setVisible(bool visible) void QAndroidPlatformWindow::setWindowState(Qt::WindowStates state) { - if (m_windowState == state) - return; - QPlatformWindow::setWindowState(state); - m_windowState = state; if (window()->isVisible()) - updateSystemUiVisibility(); + updateSystemUiVisibility(state, window()->flags()); } void QAndroidPlatformWindow::setWindowFlags(Qt::WindowFlags flags) { - if (m_windowFlags == flags) - return; + QPlatformWindow::setWindowFlags(flags); - m_windowFlags = flags; -} - -Qt::WindowFlags QAndroidPlatformWindow::windowFlags() const -{ - return m_windowFlags; + if (window()->isVisible()) + updateSystemUiVisibility(window()->windowStates(), flags); } void QAndroidPlatformWindow::setParent(const QPlatformWindow *window) @@ -255,15 +244,21 @@ void QAndroidPlatformWindow::requestActivateWindow() raise(); } -void QAndroidPlatformWindow::updateSystemUiVisibility() +void QAndroidPlatformWindow::updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags) { - const int flags = window()->flags(); const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; if (!isNonRegularWindow) { - const bool isFullScreen = (m_windowState & Qt::WindowFullScreen); - const bool expandedToCutout = (flags & Qt::ExpandedClientAreaHint); - QtAndroid::backendRegister()->callInterface<QtJniTypes::QtWindowInterface, void>( - "setSystemUiVisibility", isFullScreen, expandedToCutout); + auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>(); + iface->runOnAndroidMainThread([=]() { + using namespace QtJniTypes; + auto activity = iface->context().object<Activity>(); + if (states & Qt::WindowFullScreen) + QtWindowInsetsController::callStaticMethod("showFullScreen", activity); + else if (flags & Qt::ExpandedClientAreaHint) + QtWindowInsetsController::callStaticMethod("showExpanded", activity); + else + QtWindowInsetsController::callStaticMethod("showNormal", activity); + }); } } diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h index 07f4e12b35c..826a8d30ade 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.h +++ b/src/plugins/platforms/android/qandroidplatformwindow.h @@ -43,7 +43,6 @@ public: void setWindowState(Qt::WindowStates state) override; void setWindowFlags(Qt::WindowFlags flags) override; - Qt::WindowFlags windowFlags() const; void setParent(const QPlatformWindow *window) override; WId winId() const override; @@ -58,7 +57,7 @@ public: void propagateSizeHints() override; void requestActivateWindow() override; - void updateSystemUiVisibility(); + void updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags); void updateFocusedEditText(); inline bool isRaster() const { return m_isRaster; } bool isExposed() const override; @@ -82,8 +81,6 @@ protected: bool isEmbeddingContainer() const; virtual void clearSurface() {} - Qt::WindowFlags m_windowFlags; - Qt::WindowStates m_windowState; bool m_isRaster; int m_nativeViewId = -1; diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 69ee3638ac6..08c9f5d5ba2 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -132,6 +132,7 @@ static void populateRoleMap() roleMap[QAccessible::ComboBox] = NSAccessibilityComboBoxRole; roleMap[QAccessible::RadioButton] = NSAccessibilityRadioButtonRole; roleMap[QAccessible::CheckBox] = NSAccessibilityCheckBoxRole; + roleMap[QAccessible::Switch] = NSAccessibilityCheckBoxRole; roleMap[QAccessible::StaticText] = NSAccessibilityStaticTextRole; roleMap[QAccessible::Table] = NSAccessibilityTableRole; roleMap[QAccessible::StatusBar] = NSAccessibilityStaticTextRole; @@ -204,6 +205,8 @@ NSString *macSubrole(QAccessibleInterface *interface) return NSAccessibilitySecureTextFieldSubrole; if (interface->role() == QAccessible::PageTab) return NSAccessibilityTabButtonSubrole; + if (interface->role() == QAccessible::Switch) + return NSAccessibilitySwitchSubrole; return nil; } @@ -328,8 +331,11 @@ NSString *getTranslatedAction(const QString &qtAction) QString translateAction(NSString *nsAction, QAccessibleInterface *interface) { if ([nsAction compare: NSAccessibilityPressAction] == NSOrderedSame) { - if (interface->role() == QAccessible::CheckBox || interface->role() == QAccessible::RadioButton) + if (interface->role() == QAccessible::CheckBox + || interface->role() == QAccessible::RadioButton + || interface->role() == QAccessible::Switch) { return QAccessibleActionInterface::toggleAction(); + } return QAccessibleActionInterface::pressAction(); } else if ([nsAction compare: NSAccessibilityIncrementAction] == NSOrderedSame) return QAccessibleActionInterface::increaseAction(); diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index d4c5d0f0425..e0ef6cec794 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -161,7 +161,8 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; bool selectable = (m_options->acceptMode() == QFileDialogOptions::AcceptSave) || [self panel:m_panel shouldEnableURL:url]; - m_panel.nameFieldStringValue = selectable ? info.fileName().toNSString() : @""; + if (!openpanel_cast(m_panel)) + m_panel.nameFieldStringValue = selectable ? info.fileName().toNSString() : @""; [self updateProperties]; diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm index aab01a7b439..e9ef769ec4b 100644 --- a/src/plugins/platforms/cocoa/qnsview_keys.mm +++ b/src/plugins/platforms/cocoa/qnsview_keys.mm @@ -114,6 +114,9 @@ static bool sendAsShortcut(const KeyEvent &keyEvent, QWindow *window) qCDebug(lcQpaKeys) << "Interpreting key event for focus object" << focusObject; m_currentlyInterpretedKeyEvent = nsevent; + // Asking the input context to handle the event will involve both + // the current input method, as well as NSKeyBindingManager, which + // may result in action callbacks to doCommandBySelector. if (![self.inputContext handleEvent:nsevent]) { qCDebug(lcQpaKeys) << "Input context did not consume event"; m_sendKeyEvent = true; diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h index 60b9bc8fc02..8fdcf88293e 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QIOSIMAGEPICKERCONTROLLER_H +#define QIOSIMAGEPICKERCONTROLLER_H + #import <UIKit/UIKit.h> #include "../../qiosfiledialog.h" @@ -9,3 +12,5 @@ @interface QIOSImagePickerController : UIImagePickerController <UIImagePickerControllerDelegate, UINavigationControllerDelegate> - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; @end + +#endif // QIOSIMAGEPICKERCONTROLLER_H diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h index 289c3ee3258..a227312c5b0 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QIOSDOCUMENTPICKERCONTROLLER_H +#define QIOSDOCUMENTPICKERCONTROLLER_H + #import <UIKit/UIKit.h> #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h> @@ -12,3 +15,5 @@ UIAdaptivePresentationControllerDelegate> - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; @end + +#endif // QIOSDOCUMENTPICKERCONTROLLER_H diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm index 4e019b69cc4..6ca6554f673 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm @@ -7,8 +7,10 @@ #include "qiosdocumentpickercontroller.h" +#include <QtCore/qpointer.h> + @implementation QIOSDocumentPickerController { - QIOSFileDialog *m_fileDialog; + QPointer<QIOSFileDialog> m_fileDialog; } - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog @@ -61,6 +63,9 @@ { Q_UNUSED(controller); + if (!m_fileDialog) + return; + QList<QUrl> files; for (NSURL* url in urls) files.append(QUrl::fromNSURL(url)); @@ -71,12 +76,18 @@ - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { + if (!m_fileDialog) + return; + Q_UNUSED(controller); emit m_fileDialog->reject(); } - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController { + if (!m_fileDialog) + return; + Q_UNUSED(presentationController); // "Called on the delegate when the user has taken action to dismiss the diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.h b/src/plugins/platforms/ios/qiosplatformaccessibility.h index 04cddf00f4e..1ccc5bd089a 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.h +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.h @@ -23,7 +23,7 @@ public: private: QMacNotificationObserver m_focusObserver; - QMacAccessibilityElement *m_focusElement; + QT_MANGLE_NAMESPACE(QMacAccessibilityElement) *m_focusElement; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.mm b/src/plugins/platforms/ios/qiosplatformaccessibility.mm index 26f48468c45..a63c75757e4 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.mm +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.mm @@ -12,6 +12,8 @@ #include "qioswindow.h" #include "quiaccessibilityelement.h" +QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + QIOSPlatformAccessibility::QIOSPlatformAccessibility() { m_focusObserver = QMacNotificationObserver( diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h index addfae3d748..7d73ed9821a 100644 --- a/src/plugins/platforms/ios/qiostextresponder.h +++ b/src/plugins/platforms/ios/qiostextresponder.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QIOSTEXTRESPONDER_H +#define QIOSTEXTRESPONDER_H + #import <UIKit/UIKit.h> #include <QtCore/qstring.h> @@ -50,3 +53,5 @@ QT_END_NAMESPACE @property(nonatomic, assign) id<UITextInputDelegate> inputDelegate; @end + +#endif // QIOSTEXTRESPONDER_H diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 23a4c506c24..5024013ba77 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -10,9 +10,6 @@ #import <UIKit/UIKit.h> -class QIOSContext; -class QIOSWindow; - @class QUIView; QT_BEGIN_NAMESPACE @@ -84,7 +81,7 @@ private: QDebug operator<<(QDebug debug, const QIOSWindow *window); #endif -QT_MANGLE_NAMESPACE(QUIView) *quiview_cast(UIView *view); +QUIView *quiview_cast(UIView *view); QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index d10fe40840a..0d6952bddf0 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -115,6 +115,9 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); || iface->role() == QAccessible::RadioButton) return @""; + if (iface->role() == QAccessible::Switch) + return state.checked ? @"1" : @"0"; + return state.checked ? QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_CHECKED).toNSString() : QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_UNCHECKED).toNSString(); @@ -169,6 +172,8 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); || accessibleRole == QAccessible::RadioButton) { if (state.checked) traits |= UIAccessibilityTraitSelected; + } else if (accessibleRole == QAccessible::Switch) { + traits |= UIAccessibilityTraitToggleButton; } else if (accessibleRole == QAccessible::EditableText) { static auto defaultTextFieldTraits = []{ auto *textField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease]; diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h index 84726216021..12ae3646ad9 100644 --- a/src/plugins/platforms/ios/quiview.h +++ b/src/plugins/platforms/ios/quiview.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QUIVIEW_H +#define QUIVIEW_H + #import <UIKit/UIKit.h> #include <qhash.h> @@ -39,3 +42,5 @@ QT_END_NAMESPACE @interface QUIMetalView : QUIView @end #endif + +#endif // QUIVIEW_H diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp index 1cf81453fe7..5fa79482217 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp +++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp @@ -323,8 +323,9 @@ void QWasmAccessibility::setProperty(emscripten::val element, const std::string } -void QWasmAccessibility::addEventListener(emscripten::val element, const char *eventType) +void QWasmAccessibility::addEventListener(QAccessibleInterface *iface, emscripten::val element, const char *eventType) { + element.set("data-qta11yinterface", reinterpret_cast<size_t>(iface)); element.call<void>("addEventListener", emscripten::val(eventType), QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_eventHandlerIndex), true); @@ -352,7 +353,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac case QAccessible::Button: { element = document.call<emscripten::val>("createElement", std::string("button")); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); } break; case QAccessible::CheckBox: { @@ -360,7 +361,18 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac setAttribute(element, "type", "checkbox"); setAttribute(element, "checked", iface->state().checked); setProperty(element, "indeterminate", iface->state().checkStateMixed); - addEventListener(element, "change"); + addEventListener(iface, element, "change"); + } break; + + case QAccessible::Switch: { + element = document.call<emscripten::val>("createElement", std::string("button")); + setAttribute(element, "type", "button"); + setAttribute(element, "role", "switch"); + if (iface->state().checked) + setAttribute(element, "aria-checked", "true"); + else + setAttribute(element, "aria-checked", "false"); + addEventListener(iface, element, "change"); } break; case QAccessible::RadioButton: { @@ -368,7 +380,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac setAttribute(element, "type", "radio"); setAttribute(element, "checked", iface->state().checked); setProperty(element, "name", "buttonGroup"); - addEventListener(element, "change"); + addEventListener(iface, element, "change"); } break; case QAccessible::SpinBox: @@ -402,7 +414,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("button")); setAttribute(element, "role", "tab"); setAttribute(element, "title", text.toStdString()); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); } break; case QAccessible::ScrollBar: { @@ -411,7 +423,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("div")); setAttribute(element, "role", "scrollbar"); setAttribute(element, "aria-valuenow", valueString); - addEventListener(element, "change"); + addEventListener(iface, element, "change"); } break; case QAccessible::StaticText: { @@ -425,7 +437,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("div")); setAttribute(element, "role", "toolbar"); setAttribute(element, "title", text.toStdString()); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); }break; case QAccessible::MenuItem: case QAccessible::ButtonMenu: { @@ -433,7 +445,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("button")); setAttribute(element, "role", "menuitem"); setAttribute(element, "title", text.toStdString()); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); }break; case QAccessible::MenuBar: case QAccessible::PopupMenu: { @@ -460,7 +472,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("div")); } - addEventListener(element, "focus"); + addEventListener(iface, element, "focus"); return element; }(); @@ -531,6 +543,7 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface) { emscripten::val element = getHtmlElement(iface); emscripten::val container = getElementContainer(iface); + if (container.isUndefined() || element.isUndefined()) return; @@ -543,21 +556,21 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface) emscripten::val next = emscripten::val::undefined(); const int thisIndex = iface->parent()->indexOfChild(iface); - Q_ASSERT(thisIndex >= 0 && thisIndex < iface->parent()->childCount()); - for (int i = thisIndex + 1; i < iface->parent()->childCount(); ++i) { - const auto elementI = getHtmlElement(iface->parent()->child(i)); - if (!elementI.isUndefined() && - elementI["parentElement"] == container) { - next = elementI; - break; + if (thisIndex >= 0) { + Q_ASSERT(thisIndex < iface->parent()->childCount()); + for (int i = thisIndex + 1; i < iface->parent()->childCount(); ++i) { + const auto elementI = getHtmlElement(iface->parent()->child(i)); + if (!elementI.isUndefined() && + elementI["parentElement"] == container) { + next = elementI; + break; + } } + if (next.isUndefined()) + container.call<void>("appendChild", element); + else + container.call<void>("insertBefore", element, next); } - if (next.isUndefined()) { - container.call<void>("appendChild", element); - } else { - container.call<void>("insertBefore", element, next); - } - const auto activeElementAfter = emscripten::val::take_ownership( getActiveElement_js(emscripten::val::undefined().as_handle())); if (activeElementBefore != activeElementAfter) { @@ -701,22 +714,26 @@ void QWasmAccessibility::handleLineEditUpdate(QAccessibleEvent *event) void QWasmAccessibility::handleEventFromHtmlElement(const emscripten::val event) { - QAccessibleInterface *iface = m_elements.key(event["target"]); + if (event["target"].isNull() || event["target"].isUndefined()) + return; - if (iface == nullptr) { + if (event["target"]["data-qta11yinterface"].isNull() || event["target"]["data-qta11yinterface"].isUndefined()) return; - } else { - QString eventType = QString::fromStdString(event["type"].as<std::string>()); - const auto& actionNames = QAccessibleBridgeUtils::effectiveActionNames(iface); - - if (eventType == "focus") { - if (actionNames.contains(QAccessibleActionInterface::setFocusAction())) - iface->actionInterface()->doAction(QAccessibleActionInterface::setFocusAction()); - } else if (actionNames.contains(QAccessibleActionInterface::pressAction())) { - iface->actionInterface()->doAction(QAccessibleActionInterface::pressAction()); - } else if (actionNames.contains(QAccessibleActionInterface::toggleAction())) { - iface->actionInterface()->doAction(QAccessibleActionInterface::toggleAction()); - } + + auto iface = reinterpret_cast<QAccessibleInterface *>(event["target"]["data-qta11yinterface"].as<size_t>()); + if (m_elements.find(iface) == m_elements.end()) + return; + + const QString eventType = QString::fromStdString(event["type"].as<std::string>()); + const auto& actionNames = QAccessibleBridgeUtils::effectiveActionNames(iface); + + if (eventType == "focus") { + if (actionNames.contains(QAccessibleActionInterface::setFocusAction())) + iface->actionInterface()->doAction(QAccessibleActionInterface::setFocusAction()); + } else if (actionNames.contains(QAccessibleActionInterface::pressAction())) { + iface->actionInterface()->doAction(QAccessibleActionInterface::pressAction()); + } else if (actionNames.contains(QAccessibleActionInterface::toggleAction())) { + iface->actionInterface()->doAction(QAccessibleActionInterface::toggleAction()); } } @@ -743,6 +760,28 @@ void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event) break; } } + +void QWasmAccessibility::handleSwitchUpdate(QAccessibleEvent *event) +{ + switch (event->type()) { + case QAccessible::Focus: + case QAccessible::NameChanged: { + setHtmlElementTextName(event->accessibleInterface()); + } break; + case QAccessible::StateChanged: { + QAccessibleInterface *accessible = event->accessibleInterface(); + const emscripten::val element = getHtmlElement(accessible); + if (accessible->state().checked) + setAttribute(element, "aria-checked", "true"); + else + setAttribute(element, "aria-checked", "false"); + } break; + default: + qCDebug(lcQpaAccessibility) << "TODO: implement handleSwitchUpdate for event" << event->type(); + break; + } +} + void QWasmAccessibility::handleToolUpdate(QAccessibleEvent *event) { QAccessibleInterface *iface = event->accessibleInterface(); @@ -1152,6 +1191,9 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::CheckBox: handleCheckBoxUpdate(event); break; + case QAccessible::Switch: + handleSwitchUpdate(event); + break; case QAccessible::EditableText: handleLineEditUpdate(event); break; diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.h b/src/plugins/platforms/wasm/qwasmaccessibility.h index ee2be18e5b7..26f3e0e9afe 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.h +++ b/src/plugins/platforms/wasm/qwasmaccessibility.h @@ -81,6 +81,7 @@ private: void handleStaticTextUpdate(QAccessibleEvent *event); void handleButtonUpdate(QAccessibleEvent *event); void handleCheckBoxUpdate(QAccessibleEvent *event); + void handleSwitchUpdate(QAccessibleEvent *event); void handleDialogUpdate(QAccessibleEvent *event); void handleMenuUpdate(QAccessibleEvent *event); void handleToolUpdate(QAccessibleEvent *event); @@ -115,7 +116,7 @@ private: void setProperty(emscripten::val element, const std::string &attr, const char *val); void setProperty(emscripten::val element, const std::string &attr, bool val); - void addEventListener(emscripten::val element, const char *eventType); + void addEventListener(QAccessibleInterface *, emscripten::val element, const char *eventType); private: static QWasmAccessibility *s_instance; diff --git a/src/plugins/platforms/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp index 614d5bd25a3..18a457198f1 100644 --- a/src/plugins/platforms/wasm/qwasminputcontext.cpp +++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp @@ -225,8 +225,8 @@ void QWasmInputContext::updateGeometry() qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "propagating inputItemRectangle:" << inputItemRectangle; m_inputElement["style"].set("left", std::to_string(inputItemRectangle.x()) + "px"); m_inputElement["style"].set("top", std::to_string(inputItemRectangle.y()) + "px"); - m_inputElement["style"].set("width", std::to_string(inputItemRectangle.width()) + "px"); - m_inputElement["style"].set("height", std::to_string(inputItemRectangle.height()) + "px"); + m_inputElement["style"].set("width", "1px"); + m_inputElement["style"].set("height", "1px"); } } diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index a1a173f0182..17422bd606d 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -201,7 +201,7 @@ void QWaylandXdgSurface::Toplevel::requestWindowFlags(Qt::WindowFlags flags) delete m_decoration; m_decoration = nullptr; } else { - m_decoration->unsetMode(); + m_decoration->requestMode(QWaylandXdgToplevelDecorationV1::mode_server_side); } } } @@ -697,108 +697,135 @@ void QWaylandXdgSurface::setWindowPosition(const QPoint &position) window()->updateExposure(); } +static QtWayland::xdg_positioner::gravity gravityFromEdge(Qt::Edges edges) +{ + switch (edges) { + case Qt::Edges(): + return QtWayland::xdg_positioner::gravity_none; + case Qt::TopEdge: + return QtWayland::xdg_positioner::gravity_top; + case Qt::TopEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::gravity_top_right; + case Qt::RightEdge: + return QtWayland::xdg_positioner::gravity_right; + case Qt::BottomEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::gravity_bottom_right; + case Qt::BottomEdge: + return QtWayland::xdg_positioner::gravity_bottom; + case Qt::BottomEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::gravity_bottom_left; + case Qt::LeftEdge: + return QtWayland::xdg_positioner::gravity_left; + case Qt::TopEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::gravity_top_left; + } + qCWarning(lcQpaWayland) << "Cannot map positioner gravity " << edges; + return QtWayland::xdg_positioner::gravity_none; +} + +static QtWayland::xdg_positioner::anchor anchorFromEdge(Qt::Edges edges) +{ + switch (edges) { + case Qt::Edges(): + return QtWayland::xdg_positioner::anchor_none; + case Qt::TopEdge: + return QtWayland::xdg_positioner::anchor_top; + case Qt::TopEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::anchor_top_right; + case Qt::RightEdge: + return QtWayland::xdg_positioner::anchor_right; + case Qt::BottomEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::anchor_bottom_right; + case Qt::BottomEdge: + return QtWayland::xdg_positioner::anchor_bottom; + case Qt::BottomEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::anchor_bottom_left; + case Qt::LeftEdge: + return QtWayland::xdg_positioner::anchor_left; + case Qt::TopEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::anchor_top_left; + } + qCWarning(lcQpaWayland) << "Cannot map positioner anchor" << edges; + return QtWayland::xdg_positioner::anchor_none; +} + std::unique_ptr<QWaylandXdgSurface::Positioner> QWaylandXdgSurface::createPositioner(QWaylandWindow *parent) { std::unique_ptr<Positioner> positioner(new Positioner(m_shell)); - // set_popup expects a position relative to the parent - QRect windowGeometry = m_window->windowContentGeometry(); - QMargins windowMargins = m_window->windowContentMargins() - m_window->clientSideMargins(); - QMargins parentMargins = parent->windowContentMargins() - parent->clientSideMargins(); - // These property overrides may be removed when public API becomes available - QRect placementAnchor = m_window->window()->property("_q_waylandPopupAnchorRect").toRect(); - if (!placementAnchor.isValid()) { - placementAnchor = QRect(m_window->geometry().topLeft() - parent->geometry().topLeft(), QSize(1,1)); - } - placementAnchor.translate(windowMargins.left(), windowMargins.top()); - placementAnchor.translate(-parentMargins.left(), -parentMargins.top()); + // Default case, map the guessed global position to a relative position + QRect placementAnchor = QRect(m_window->geometry().topLeft() - parent->geometry().topLeft(), QSize(1,1)); + Qt::Edges anchor = Qt::TopEdge | Qt::RightEdge; + Qt::Edges gravity = Qt::BottomEdge | Qt::RightEdge; + uint32_t constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_slide_x | QtWayland::xdg_positioner::constraint_adjustment_slide_y; - uint32_t anchor = QtWayland::xdg_positioner::anchor_top_left; + // Override from window type + if (m_window->parentControlGeometry().isValid()) + placementAnchor = m_window->parentControlGeometry(); + + switch (m_window->extendedWindowType()) { + case QNativeInterface::Private::QWaylandWindow::Menu: + case QNativeInterface::Private::QWaylandWindow::WindowType::ComboBox: + anchor = Qt::BottomEdge | Qt::LeftEdge; + gravity = Qt::BottomEdge | Qt::RightEdge; + constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_slide_x | + QtWayland::xdg_positioner::constraint_adjustment_flip_y | QtWayland::xdg_positioner::constraint_adjustment_slide_y; + break; + case QNativeInterface::Private::QWaylandWindow::SubMenu: + anchor = Qt::TopEdge | Qt::RightEdge; + gravity = Qt::BottomEdge | Qt::RightEdge; + constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_flip_x | + QtWayland::xdg_positioner::constraint_adjustment_slide_y; + break; + case QNativeInterface::Private::QWaylandWindow::ToolTip: + anchor = Qt::BottomEdge | Qt::RightEdge; + gravity = Qt::BottomEdge | Qt::RightEdge; + constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_flip_x | QtWayland::xdg_positioner::constraint_adjustment_slide_x | + QtWayland::xdg_positioner::constraint_adjustment_flip_y | QtWayland::xdg_positioner::constraint_adjustment_slide_y; + break; + default: + break; + } + + if (qApp->layoutDirection() == Qt::RightToLeft) { + if (anchor & (Qt::RightEdge | Qt::LeftEdge)) + anchor ^= (Qt::RightEdge | Qt::LeftEdge); + if (gravity & (Qt::RightEdge | Qt::LeftEdge)) + gravity ^= (Qt::RightEdge | Qt::LeftEdge); + } + + // Override with properties fauxAPI + const QVariant placementAnchorVariant = m_window->window()->property("_q_waylandPopupAnchorRect"); + if (placementAnchorVariant.isValid()) + placementAnchor = placementAnchorVariant.toRect(); const QVariant anchorVariant = m_window->window()->property("_q_waylandPopupAnchor"); - if (anchorVariant.isValid()) { - switch (anchorVariant.value<Qt::Edges>()) { - case Qt::Edges(): - anchor = QtWayland::xdg_positioner::anchor_none; - break; - case Qt::TopEdge: - anchor = QtWayland::xdg_positioner::anchor_top; - break; - case Qt::TopEdge | Qt::RightEdge: - anchor = QtWayland::xdg_positioner::anchor_top_right; - break; - case Qt::RightEdge: - anchor = QtWayland::xdg_positioner::anchor_right; - break; - case Qt::BottomEdge | Qt::RightEdge: - anchor = QtWayland::xdg_positioner::anchor_bottom_right; - break; - case Qt::BottomEdge: - anchor = QtWayland::xdg_positioner::anchor_bottom; - break; - case Qt::BottomEdge | Qt::LeftEdge: - anchor = QtWayland::xdg_positioner::anchor_bottom_left; - break; - case Qt::LeftEdge: - anchor = QtWayland::xdg_positioner::anchor_left; - break; - case Qt::TopEdge | Qt::LeftEdge: - anchor = QtWayland::xdg_positioner::anchor_top_left; - break; - } - } - - uint32_t gravity = QtWayland::xdg_positioner::gravity_bottom_right; + if (anchorVariant.isValid()) + anchor = anchorVariant.value<Qt::Edges>(); const QVariant popupGravityVariant = m_window->window()->property("_q_waylandPopupGravity"); - if (popupGravityVariant.isValid()) { - switch (popupGravityVariant.value<Qt::Edges>()) { - case Qt::Edges(): - gravity = QtWayland::xdg_positioner::gravity_none; - break; - case Qt::TopEdge: - gravity = QtWayland::xdg_positioner::gravity_top; - break; - case Qt::TopEdge | Qt::RightEdge: - gravity = QtWayland::xdg_positioner::gravity_top_right; - break; - case Qt::RightEdge: - gravity = QtWayland::xdg_positioner::gravity_right; - break; - case Qt::BottomEdge | Qt::RightEdge: - gravity = QtWayland::xdg_positioner::gravity_bottom_right; - break; - case Qt::BottomEdge: - gravity = QtWayland::xdg_positioner::gravity_bottom; - break; - case Qt::BottomEdge | Qt::LeftEdge: - gravity = QtWayland::xdg_positioner::gravity_bottom_left; - break; - case Qt::LeftEdge: - gravity = QtWayland::xdg_positioner::gravity_left; - break; - case Qt::TopEdge | Qt::LeftEdge: - gravity = QtWayland::xdg_positioner::gravity_top_left; - break; - } - } - - uint32_t constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_slide_x | QtWayland::xdg_positioner::constraint_adjustment_slide_y; + if (popupGravityVariant.isValid()) + gravity = popupGravityVariant.value<Qt::Edges>(); const QVariant constraintAdjustmentVariant = m_window->window()->property("_q_waylandPopupConstraintAdjustment"); - if (constraintAdjustmentVariant.isValid()) { + if (constraintAdjustmentVariant.isValid()) constraintAdjustment = constraintAdjustmentVariant.toUInt(); - } + + // set_popup expects a position relative to the parent + QRect windowGeometry = m_window->windowContentGeometry(); + QMargins windowMargins = m_window->windowContentMargins() - m_window->clientSideMargins(); + QMargins parentMargins = parent->windowContentMargins() - parent->clientSideMargins(); + placementAnchor.translate(windowMargins.left(), windowMargins.top()); + placementAnchor.translate(-parentMargins.left(), -parentMargins.top()); positioner->set_anchor_rect(placementAnchor.x(), placementAnchor.y(), placementAnchor.width(), placementAnchor.height()); - positioner->set_anchor(anchor); - positioner->set_gravity(gravity); + positioner->set_anchor(anchorFromEdge(anchor)); + positioner->set_gravity(gravityFromEdge(gravity)); positioner->set_size(windowGeometry.width(), windowGeometry.height()); positioner->set_constraint_adjustment(constraintAdjustment); return positioner; } - void QWaylandXdgSurface::setIcon(const QIcon &icon) { if (!m_shell->m_topLevelIconManager || !m_toplevel) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 7c300843518..be527b08f4d 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -481,8 +481,9 @@ void QWaylandWindow::setGeometry(const QRect &r) if (mShellSurface && !mInResizeFromApplyConfigure) { const QRect frameGeometry = r.marginsAdded(clientSideMargins()).marginsRemoved(windowContentMargins()); - if (qt_window_private(window())->positionAutomatic) + if (qt_window_private(window())->positionAutomatic || m_popupInfo.parentControlGeometry.isValid()) mShellSurface->setWindowSize(frameGeometry.size()); + else mShellSurface->setWindowGeometry(frameGeometry); } @@ -1945,6 +1946,27 @@ QString QWaylandWindow::sessionRestoreId() const return mSessionRestoreId; } +void QWaylandWindow::setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::WindowType windowType) { + m_popupInfo.extendedWindowType = windowType; +} + +QNativeInterface::Private::QWaylandWindow::WindowType QWaylandWindow::extendedWindowType() const +{ + return m_popupInfo.extendedWindowType; +} + +void QWaylandWindow::setParentControlGeometry(const QRect &parentControlGeometry) { + m_popupInfo.parentControlGeometry = parentControlGeometry; + if (mExposed) { + mShellSurface->setWindowPosition(window()->position()); + } +} + +QRect QWaylandWindow::parentControlGeometry() const +{ + return m_popupInfo.parentControlGeometry; +} + } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 854724daf82..d6b24d0569f 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -255,6 +255,11 @@ public: void setSessionRestoreId(const QString &role) override; QString sessionRestoreId() const; + void setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::WindowType) override; + QNativeInterface::Private::QWaylandWindow::WindowType extendedWindowType() const; + void setParentControlGeometry(const QRect &parentAnchor) override; + QRect parentControlGeometry() const; + public Q_SLOTS: void applyConfigure(); @@ -397,6 +402,11 @@ private: void handleFrameCallback(struct ::wl_callback* callback); const QPlatformWindow *lastParent = nullptr; + struct { + QRect parentControlGeometry; + QNativeInterface::Private::QWaylandWindow::WindowType extendedWindowType = QNativeInterface::Private::QWaylandWindow::Default; + } m_popupInfo; + static QWaylandWindow *mMouseGrab; static QWaylandWindow *mTopPopup; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 82a86d6ff3a..01716fba60c 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -4019,9 +4019,11 @@ void QWindowsWindow::requestUpdate() // request or we are waiting for the event loop to process // the Posted event on the GUI thread. if (m_vsyncUpdatePending.testAndSetAcquire(UpdateState::Requested, UpdateState::Posted)) { - QMetaObject::invokeMethod(w, [w] { + QWindowsWindow *oldSelf = this; + QMetaObject::invokeMethod(w, [w, oldSelf] { + // 'oldSelf' is only used for comparison, don't access it directly! auto *self = static_cast<QWindowsWindow *>(w->handle()); - if (self) { + if (self && self == oldSelf) { // The platform window is still alive self->m_vsyncUpdatePending.storeRelease(UpdateState::Ready); self->deliverUpdateRequest(); diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index fc0e053f396..0144786ce5e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -82,8 +82,9 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve { if (QAccessibleInterface *accessible = event->accessibleInterface()) { if (event->changedStates().checked || event->changedStates().checkStateMixed) { - // Notifies states changes in checkboxes. - if (accessible->role() == QAccessible::CheckBox) { + // Notifies states changes in checkboxes and switches. + if (accessible->role() == QAccessible::CheckBox + || accessible->role() == QAccessible::Switch) { if (auto provider = providerForAccessible(accessible)) { long toggleState = ToggleState_Off; if (accessible->state().checked) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp index 4146a56b226..b2675d5b884 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp @@ -132,6 +132,7 @@ long roleToControlTypeId(QAccessible::Role role) {QAccessible::EditableText, UIA_EditControlTypeId}, {QAccessible::Button, UIA_ButtonControlTypeId}, {QAccessible::CheckBox, UIA_CheckBoxControlTypeId}, + {QAccessible::Switch, UIA_ButtonControlTypeId}, {QAccessible::RadioButton, UIA_RadioButtonControlTypeId}, {QAccessible::ComboBox, UIA_ComboBoxControlTypeId}, {QAccessible::ProgressBar, UIA_ProgressBarControlTypeId}, |
