diff options
Diffstat (limited to 'src/plugins/platforms')
33 files changed, 431 insertions, 217 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..2989b4d6df3 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; @@ -160,6 +161,7 @@ static void populateRoleMap()      roleMap[QAccessible::Graphic] = NSAccessibilityImageRole;      roleMap[QAccessible::Tree] = NSAccessibilityOutlineRole;      roleMap[QAccessible::BlockQuote] = NSAccessibilityGroupRole; +    roleMap[QAccessible::LayeredPane] = NSAccessibilityGroupRole;  }  /* @@ -204,6 +206,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 +332,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/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index 144c0519155..238f2ea2307 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -714,6 +714,25 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of          iface->setText(QAccessible::Description, QString::fromNSString(label));  } +- (NSAccessibilityOrientation)accessibilityOrientation { +    QAccessibleInterface *iface = self.qtInterface; +    if (!iface) +        return NSAccessibilityOrientationUnknown; + +    NSAccessibilityOrientation nsOrientation = NSAccessibilityOrientationUnknown; +    if (QAccessibleAttributesInterface *attributesIface = iface->attributesInterface()) { +        const QVariant orientationVariant = +                attributesIface->attributeValue(QAccessible::Attribute::Orientation); +        if (orientationVariant.isValid()) { +            Q_ASSERT(orientationVariant.canConvert<Qt::Orientation>()); +            const Qt::Orientation orientation = orientationVariant.value<Qt::Orientation>(); +            nsOrientation = orientation == Qt::Horizontal ? NSAccessibilityOrientationHorizontal +                                                          : NSAccessibilityOrientationVertical; +        } +    } +    return nsOrientation; +} +  - (id) accessibilityValue {      if (QAccessibleInterface *iface = self.qtInterface) {          // VoiceOver asks for the value attribute for all elements. Return nil 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..a87c33c8346 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: @@ -389,6 +401,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac          case QAccessible::PageTabList:{              element = document.call<emscripten::val>("createElement", std::string("div"));              setAttribute(element, "role", "tablist"); +            setHtmlElementOrientation(element, iface);              m_elements[iface] = element; @@ -402,7 +415,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 +424,8 @@ 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"); +            setHtmlElementOrientation(element, iface); +            addEventListener(iface, element, "change");          } break;          case QAccessible::StaticText: { @@ -425,7 +439,8 @@ 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"); +            setHtmlElementOrientation(element, iface); +            addEventListener(iface, element, "click");          }break;          case QAccessible::MenuItem:          case QAccessible::ButtonMenu: { @@ -433,7 +448,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: { @@ -441,6 +456,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac              element = document.call<emscripten::val>("createElement", std::string("div"));              setAttribute(element, "role", "menubar");              setAttribute(element, "title", text.toStdString()); +            setHtmlElementOrientation(element, iface);              m_elements[iface] = element;              for (int i = 0; i < iface->childCount(); ++i) { @@ -460,7 +476,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 +547,7 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface)  {      emscripten::val element = getHtmlElement(iface);      emscripten::val container = getElementContainer(iface); +      if (container.isUndefined() || element.isUndefined())          return; @@ -543,21 +560,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) { @@ -648,6 +665,21 @@ void QWasmAccessibility::setHtmlElementDisabled(QAccessibleInterface *iface)      setAttribute(element, "aria-disabled", iface->state().disabled);  } +void QWasmAccessibility::setHtmlElementOrientation(emscripten::val element, QAccessibleInterface *iface) +{ +    Q_ASSERT(iface); +    if (QAccessibleAttributesInterface *attributesIface = iface->attributesInterface()) { +        const QVariant orientationVariant = +                attributesIface->attributeValue(QAccessible::Attribute::Orientation); +        if (orientationVariant.isValid()) { +            Q_ASSERT(orientationVariant.canConvert<Qt::Orientation>()); +            const Qt::Orientation orientation = orientationVariant.value<Qt::Orientation>(); +            const std::string value = orientation == Qt::Horizontal ? "horizontal" : "vertical"; +            setAttribute(element, "aria-orientation", value); +        } +    } +} +  void QWasmAccessibility::handleStaticTextUpdate(QAccessibleEvent *event)  {      switch (event->type()) { @@ -701,22 +733,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 +779,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 +1210,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..ddbfec918d6 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.h +++ b/src/plugins/platforms/wasm/qwasmaccessibility.h @@ -77,10 +77,12 @@ private:      void setHtmlElementTextNameLE(QAccessibleInterface *iface);      void setHtmlElementFocus(QAccessibleInterface *iface);      void setHtmlElementDisabled(QAccessibleInterface *iface); +    void setHtmlElementOrientation(emscripten::val element, QAccessibleInterface *iface);      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 +117,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/plugins/shellintegration/xdg-shell/qwaylandxdgtopleveliconv1.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgtopleveliconv1.cpp index 84ecc289c59..9aa8af39090 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgtopleveliconv1.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgtopleveliconv1.cpp @@ -2,7 +2,6 @@  // Copyright (C) 2024 David Edmundson <[email protected]>  // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#include "qwaylandshmbackingstore_p.h"  #include "qwaylandxdgtopleveliconv1_p.h"  #include <QtWaylandClient/private/qwaylanddisplay_p.h> diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index 6ce43714a35..34b32da289c 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -729,11 +729,11 @@ public:  void QWaylandInputDevice::Pointer::pointer_leave(uint32_t serial, struct wl_surface *surface)  { +    Q_UNUSED(serial); +      invalidateFocus();      mButtons = Qt::NoButton; -    mParent->mSerial = serial; -      // The event may arrive after destroying the window, indicated by      // a null surface.      if (!surface) 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/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 53548622dfc..482810d5b7e 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -553,12 +553,14 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)      const bool orientationChanged = m_data.orientation != newData.orientation;      const bool primaryChanged = (newData.flags & QWindowsScreenData::PrimaryScreen)              && !(m_data.flags & QWindowsScreenData::PrimaryScreen); +    const bool refreshRateChanged = m_data.refreshRateHz != newData.refreshRateHz;      m_data.dpi = newData.dpi;      m_data.orientation = newData.orientation;      m_data.geometry = newData.geometry;      m_data.availableGeometry = newData.availableGeometry;      m_data.flags = (m_data.flags & ~QWindowsScreenData::PrimaryScreen)              | (newData.flags & QWindowsScreenData::PrimaryScreen); +    m_data.refreshRateHz = newData.refreshRateHz;      if (dpiChanged) {          QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), @@ -573,6 +575,9 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)      }      if (primaryChanged)          QWindowSystemInterface::handlePrimaryScreenChanged(this); + +    if (refreshRateChanged) +        QWindowSystemInterface::handleScreenRefreshRateChange(screen(), newData.refreshRateHz);  }  HMONITOR QWindowsScreen::handle() const 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..b7bf97f5c4b 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) @@ -653,6 +654,21 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR          *pRetVal = QComVariant{ name }.release();          break;      } +    case UIA_OrientationPropertyId: { +        OrientationType orientationType = OrientationType_None; +        if (QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface()) { +            const QVariant orientationVariant = +                    attributesIface->attributeValue(QAccessible::Attribute::Orientation); +            if (orientationVariant.isValid()) { +                Q_ASSERT(orientationVariant.canConvert<Qt::Orientation>()); +                const Qt::Orientation orientation = orientationVariant.value<Qt::Orientation>(); +                orientationType = orientation == Qt::Horizontal ? OrientationType_Horizontal +                                                                : OrientationType_Vertical; +            } +        } +        *pRetVal = QComVariant{ long(orientationType) }.release(); +        break; +    }      case UIA_StyleIdAttributeId:          setStyle(accessible, pRetVal);          break; diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp index 4146a56b226..835499d3554 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}, @@ -153,6 +154,7 @@ long roleToControlTypeId(QAccessible::Role role)          {QAccessible::WebDocument, UIA_DocumentControlTypeId},          {QAccessible::Heading, UIA_TextControlTypeId},          {QAccessible::BlockQuote, UIA_GroupControlTypeId}, +        {QAccessible::LayeredPane, UIA_PaneControlTypeId},      };      long controlType = mapping.value(role, UIA_CustomControlTypeId); diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp index 5f712d33d56..e609b84ad5f 100644 --- a/src/plugins/platforms/xcb/qxcbatom.cpp +++ b/src/plugins/platforms/xcb/qxcbatom.cpp @@ -9,6 +9,7 @@  #include <string.h>  #include <algorithm> +#include <cstdlib>  static const char *xcb_atomnames = {      // window-manager <-> client protocols | 
