diff options
Diffstat (limited to 'src/plugins/platforms')
21 files changed, 276 insertions, 139 deletions
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index a1edf49da12..028ae6d682a 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: 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/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..96c4bfa06f1 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), @@ -260,10 +261,17 @@ void QAndroidPlatformWindow::updateSystemUiVisibility() 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 (m_windowState & 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/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/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/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..35e804531bc 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp +++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp @@ -363,6 +363,17 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac addEventListener(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(element, "change"); + } break; + case QAccessible::RadioButton: { element = document.call<emscripten::val>("createElement", std::string("input")); setAttribute(element, "type", "radio"); @@ -743,6 +754,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 +1185,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..2b4716d64e7 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); 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/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}, |