diff options
Diffstat (limited to 'src')
53 files changed, 858 insertions, 510 deletions
diff --git a/src/3rdparty/iaccessible2/REUSE.toml b/src/3rdparty/iaccessible2/REUSE.toml index f4d00d7f4f1..24da6e46400 100644 --- a/src/3rdparty/iaccessible2/REUSE.toml +++ b/src/3rdparty/iaccessible2/REUSE.toml @@ -5,5 +5,5 @@ path = ["**"] precedence = "closest" SPDX-FileCopyrightText = ["Copyright (c) 2000, 2006 Sun Microsystems, Inc.", "Copyright (c) 2006 IBM Corporation", - "Copyright (c) 2007, 2010, 2012, 2013 Linux Foundation\nIAccessible2 is a trademark of the Linux Foundation. The IAccessible2 mark may be used in accordance with the Linux Foundation Trademark Policy to indicate compliance with the IAccessible2 specification."] + "Copyright (c) 2007, 2010, 2012, 2013 Linux Foundation IAccessible2 is a trademark of the Linux Foundation. The IAccessible2 mark may be used in accordance with the Linux Foundation Trademark Policy to indicate compliance with the IAccessible2 specification."] SPDX-License-Identifier = "BSD-3-Clause" diff --git a/src/3rdparty/libpsl/REUSE.toml b/src/3rdparty/libpsl/REUSE.toml index 6c911a98dfe..7b63d4ed725 100644 --- a/src/3rdparty/libpsl/REUSE.toml +++ b/src/3rdparty/libpsl/REUSE.toml @@ -10,6 +10,6 @@ SPDX-License-Identifier = "BSD-3-Clause" [[annotations]] path = ["psl_data.cpp"] precedence = "closest" -SPDX-FileCopyrightText = ["The list was originally provided by Jo Hermans <[email protected]>.\nIt is now maintained on github (https://github.com/publicsuffix/list)."] +SPDX-FileCopyrightText = ["The list was originally provided by Jo Hermans <[email protected]>.It is now maintained on github (https://github.com/publicsuffix/list)."] SPDX-License-Identifier = "MPL-2.0" diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt index b073dbea0ea..e0a675ab463 100644 --- a/src/android/jar/CMakeLists.txt +++ b/src/android/jar/CMakeLists.txt @@ -53,6 +53,7 @@ set(java_sources src/org/qtproject/qt/android/BackgroundActionsTracker.java src/org/qtproject/qt/android/QtApkFileEngine.java src/org/qtproject/qt/android/QtContentFileEngine.java + src/org/qtproject/qt/android/QtWindowInsetsController.java ) qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java index aca9e3d3c11..78763e65905 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java @@ -102,9 +102,11 @@ public class QtActivityBase extends Activity requestWindowFeature(Window.FEATURE_ACTION_BAR); if (!m_isCustomThemeSet) { - setTheme(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? - android.R.style.Theme_DeviceDefault_DayNight : - android.R.style.Theme_Holo_Light); + @SuppressWarnings("deprecation") + int themeId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + ? android.R.style.Theme_DeviceDefault_DayNight + : android.R.style.Theme_Holo_Light; + setTheme(themeId); } if (QtNative.getStateDetails().isStarted) { @@ -168,7 +170,7 @@ public class QtActivityBase extends Activity m_delegate.displayManager().registerDisplayListener(); QtWindow.updateWindows(); // Suspending the app clears the immersive mode, so we need to set it again. - m_delegate.displayManager().reinstateFullScreen(); + QtWindowInsetsController.restoreFullScreenVisibility(this); } } @@ -311,9 +313,7 @@ public class QtActivityBase extends Activity return; QtNative.setStarted(savedInstanceState.getBoolean("Started")); - boolean isFullScreen = savedInstanceState.getBoolean("isFullScreen"); - boolean expandedToCutout = savedInstanceState.getBoolean("expandedToCutout"); - m_delegate.displayManager().setSystemUiVisibility(isFullScreen, expandedToCutout); + QtWindowInsetsController.restoreFullScreenVisibility(this); // FIXME restore all surfaces } @@ -329,8 +329,6 @@ public class QtActivityBase extends Activity protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putBoolean("isFullScreen", m_delegate.displayManager().isFullScreen()); - outState.putBoolean("expandedToCutout", m_delegate.displayManager().expandedToCutout()); outState.putBoolean("Started", QtNative.getStateDetails().isStarted); } @@ -339,7 +337,7 @@ public class QtActivityBase extends Activity { super.onWindowFocusChanged(hasFocus); if (hasFocus) - m_delegate.displayManager().reinstateFullScreen(); + QtWindowInsetsController.restoreFullScreenVisibility(this); } @Override diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java index 118845efcec..aa2964c3ae9 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java @@ -86,21 +86,6 @@ class QtActivityDelegate extends QtActivityDelegateBase } @Override - public void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) - { - if (m_layout == null) - return; - - QtNative.runAction(() -> { - if (m_layout != null) { - m_displayManager.setSystemUiVisibility(isFullScreen, expandedToCutout); - QtWindow.updateWindows(); - } - }); - } - - - @Override final public void onAppStateDetailsChanged(QtNative.ApplicationStateDetails details) { if (details.isStarted) registerBackends(); @@ -138,9 +123,9 @@ class QtActivityDelegate extends QtActivityDelegateBase int orientation = m_activity.getResources().getConfiguration().orientation; setUpSplashScreen(orientation); m_activity.registerForContextMenu(m_layout); - m_activity.setContentView(m_layout, - new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); + ViewGroup.LayoutParams rootParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + m_activity.setContentView(m_layout, rootParams); handleUiModeChange(); diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java index 35f519ca9a4..7f6163f164d 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java @@ -46,7 +46,9 @@ abstract class QtActivityDelegateBase m_activity = activity; QtNative.setActivity(m_activity); m_displayManager = new QtDisplayManager(m_activity); - m_inputDelegate = new QtInputDelegate(m_displayManager::reinstateFullScreen); + m_inputDelegate = new QtInputDelegate(() -> { + QtWindowInsetsController.restoreFullScreenVisibility(m_activity); + }); m_accessibilityDelegate = new QtAccessibilityDelegate(); } @@ -105,20 +107,20 @@ abstract class QtActivityDelegateBase Configuration config = resources.getConfiguration(); int uiMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK; - if (m_displayManager.decorFitsSystemWindows()) { + if (QtWindowInsetsController.decorFitsSystemWindows(m_activity)) { Window window = m_activity.getWindow(); - QtDisplayManager.enableSystemBarsBackgroundDrawing(window); - int status = QtDisplayManager.getThemeDefaultStatusBarColor(m_activity); - QtDisplayManager.setStatusBarColor(window, status); - int nav = QtDisplayManager.getThemeDefaultNavigationBarColor(m_activity); - QtDisplayManager.setNavigationBarColor(window, nav); + QtWindowInsetsController.enableSystemBarsBackgroundDrawing(window); + int status = QtWindowInsetsController.getThemeDefaultStatusBarColor(m_activity); + QtWindowInsetsController.setStatusBarColor(window, status); + int nav = QtWindowInsetsController.getThemeDefaultNavigationBarColor(m_activity); + QtWindowInsetsController.setNavigationBarColor(window, nav); } // Don't override color scheme if the app has it set explicitly. if (canOverrideColorSchemeHint()) { boolean isLight = uiMode == Configuration.UI_MODE_NIGHT_NO; - QtDisplayManager.setStatusBarColorHint(m_activity, isLight); - QtDisplayManager.setNavigationBarColorHint(m_activity, isLight); + QtWindowInsetsController.setStatusBarColorHint(m_activity, isLight); + QtWindowInsetsController.setNavigationBarColorHint(m_activity, isLight); } switch (uiMode) { diff --git a/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java index 8736dfab771..2cabb951813 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java @@ -7,7 +7,6 @@ import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.content.res.TypedArray; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Build; @@ -15,22 +14,13 @@ import android.util.DisplayMetrics; import android.util.Size; import android.view.Display; import android.view.Surface; -import android.view.View; -import android.view.WindowInsets; import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; import android.view.WindowMetrics; -import android.view.WindowInsetsController; -import android.view.Window; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import android.graphics.Color; -import android.util.TypedValue; -import android.content.res.Resources.Theme; - import android.util.Log; class QtDisplayManager @@ -47,9 +37,6 @@ class QtDisplayManager static native void handleScreenDensityChanged(double density); // screen methods - private boolean m_isFullScreen = false; - private boolean m_expandedToCutout = false; - private static int m_previousRotation = -1; private final DisplayManager.DisplayListener m_displayListener; @@ -136,264 +123,6 @@ class QtDisplayManager } @SuppressWarnings("deprecation") - void setSystemUiVisibilityPreAndroidR(View decorView) - { - int systemUiVisibility; - - if (m_isFullScreen || m_expandedToCutout) { - systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - if (m_isFullScreen) { - systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - } - } else { - systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; - } - - decorView.setSystemUiVisibility(systemUiVisibility); - } - - void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) - { - if (m_isFullScreen == isFullScreen && m_expandedToCutout == expandedToCutout) - return; - - m_isFullScreen = isFullScreen; - m_expandedToCutout = expandedToCutout; - - Window window = m_activity.getWindow(); - View decorView = window.getDecorView(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - int cutoutMode; - if (m_isFullScreen || m_expandedToCutout) { - window.setDecorFitsSystemWindows(false); - cutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - } else { - window.setDecorFitsSystemWindows(true); - cutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; - } - LayoutParams layoutParams = window.getAttributes(); - layoutParams.layoutInDisplayCutoutMode = cutoutMode; - window.setAttributes(layoutParams); - - final WindowInsetsController insetsControl = window.getInsetsController(); - if (insetsControl != null) { - int sysBarsBehavior; - if (m_isFullScreen) { - insetsControl.hide(WindowInsets.Type.systemBars()); - sysBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - } else { - insetsControl.show(WindowInsets.Type.systemBars()); - sysBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT; - } - insetsControl.setSystemBarsBehavior(sysBarsBehavior); - } - } else { - setSystemUiVisibilityPreAndroidR(decorView); - } - - if (!isFullScreen && !edgeToEdgeEnabled(m_activity)) { - // These are needed to operate on system bar colors - window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - - // Handle transparent status and navigation bars - if (m_expandedToCutout) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - window.setStatusBarColor(Color.TRANSPARENT); - window.setNavigationBarColor(Color.TRANSPARENT); - } else { - // Android 9 and prior doesn't add the semi-transparent bars - // to avoid low contrast system icons, so try to mimick it - // by taking the current color and only increase the opacity. - int statusBarColor = window.getStatusBarColor(); - int transparentStatusBar = statusBarColor & 0x00FFFFFF; - window.setStatusBarColor(transparentStatusBar); - - int navigationBarColor = window.getNavigationBarColor(); - int semiTransparentNavigationBar = navigationBarColor & 0x7FFFFFFF; - window.setNavigationBarColor(semiTransparentNavigationBar); - } - } else { - // Restore theme's system bars colors - Theme theme = m_activity.getTheme(); - TypedValue typedValue = new TypedValue(); - - theme.resolveAttribute(android.R.attr.statusBarColor, typedValue, true); - int defaultStatusBarColor = typedValue.data; - window.setStatusBarColor(defaultStatusBarColor); - - theme.resolveAttribute(android.R.attr.navigationBarColor, typedValue, true); - int defaultNavigationBarColor = typedValue.data; - window.setNavigationBarColor(defaultNavigationBarColor); - } - } - - decorView.post(() -> decorView.requestApplyInsets()); - } - - private static boolean edgeToEdgeEnabled(Activity activity) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) - return true; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) - return false; - int[] attrs = new int[] { android.R.attr.windowOptOutEdgeToEdgeEnforcement }; - TypedArray ta = activity.getTheme().obtainStyledAttributes(attrs); - try { - return !ta.getBoolean(0, false); - } finally { - ta.recycle(); - } - } - - boolean isFullScreen() - { - return m_isFullScreen; - } - - boolean expandedToCutout() - { - return m_expandedToCutout; - } - - boolean decorFitsSystemWindows() - { - return !isFullScreen() && !expandedToCutout(); - } - - void reinstateFullScreen() - { - if (m_isFullScreen) { - m_isFullScreen = false; - setSystemUiVisibility(true, m_expandedToCutout); - } - } - - /* - * Convenience method to call deprecated API prior to Android R (30). - */ - @SuppressWarnings ("deprecation") - private static void setSystemUiVisibility(View decorView, int flags) - { - decorView.setSystemUiVisibility(flags); - } - - /* - * Set the status bar color scheme hint so that the system decides how to color the icons. - */ - @UsedFromNativeCode - static void setStatusBarColorHint(Activity activity, boolean isLight) - { - Window window = activity.getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - WindowInsetsController controller = window.getInsetsController(); - if (controller != null) { - int lightStatusBarMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - int appearance = isLight ? lightStatusBarMask : 0; - controller.setSystemBarsAppearance(appearance, lightStatusBarMask); - } - } else { - @SuppressWarnings("deprecation") - int currentFlags = window.getDecorView().getSystemUiVisibility(); - @SuppressWarnings("deprecation") - int lightStatusBarMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - int appearance = isLight - ? currentFlags | lightStatusBarMask - : currentFlags & ~lightStatusBarMask; - setSystemUiVisibility(window.getDecorView(), appearance); - } - } - - /* - * Set the navigation bar color scheme hint so that the system decides how to color the icons. - */ - @UsedFromNativeCode - static void setNavigationBarColorHint(Activity activity, boolean isLight) - { - Window window = activity.getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - WindowInsetsController controller = window.getInsetsController(); - if (controller != null) { - int lightNavigationBarMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; - int appearance = isLight ? lightNavigationBarMask : 0; - controller.setSystemBarsAppearance(appearance, lightNavigationBarMask); - } - } else { - @SuppressWarnings("deprecation") - int currentFlags = window.getDecorView().getSystemUiVisibility(); - @SuppressWarnings("deprecation") - int lightNavigationBarMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - int appearance = isLight - ? currentFlags | lightNavigationBarMask - : currentFlags & ~lightNavigationBarMask; - setSystemUiVisibility(window.getDecorView(), appearance); - } - } - - static int resolveColorAttribute(Activity activity, int attribute) - { - Theme theme = activity.getTheme(); - Resources resources = activity.getResources(); - TypedValue tv = new TypedValue(); - - if (theme.resolveAttribute(attribute, tv, true)) { - if (tv.resourceId != 0) - return resources.getColor(tv.resourceId, theme); - if (tv.type >= TypedValue.TYPE_FIRST_COLOR_INT && tv.type <= TypedValue.TYPE_LAST_COLOR_INT) - return tv.data; - } - - return -1; - } - - @SuppressWarnings("deprecation") - static int getThemeDefaultStatusBarColor(Activity activity) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return -1; - return resolveColorAttribute(activity, android.R.attr.statusBarColor); - } - - @SuppressWarnings("deprecation") - static int getThemeDefaultNavigationBarColor(Activity activity) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return -1; - return resolveColorAttribute(activity, android.R.attr.navigationBarColor); - } - - static void enableSystemBarsBackgroundDrawing(Window window) - { - // These are needed to operate on system bar colors - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - @SuppressWarnings("deprecation") - final int translucentFlags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; - window.clearFlags(translucentFlags); - } - - @SuppressWarnings("deprecation") - static void setStatusBarColor(Window window, int color) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return; - window.setStatusBarColor(color); - } - - @SuppressWarnings("deprecation") - static void setNavigationBarColor(Window window, int color) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return; - window.setNavigationBarColor(color); - } - - @SuppressWarnings("deprecation") static Display getDisplay(Context context) { Activity activity = (Activity) context; diff --git a/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java index ca271c5dd62..e0c6c00a8fd 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java @@ -9,6 +9,7 @@ import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.ResultReceiver; import android.text.method.MetaKeyKeyListener; import android.util.DisplayMetrics; @@ -166,7 +167,7 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt } else { if (m_imm == null) return; - m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler()) { + m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler(Looper.getMainLooper())) { @Override @SuppressWarnings("fallthrough") protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -278,7 +279,7 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt activity.getWindow().getInsetsController().hide(Type.ime()); } else { m_imm.hideSoftInputFromWindow(m_currentEditText.getWindowToken(), 0, - new ResultReceiver(new Handler()) { + new ResultReceiver(new Handler(Looper.getMainLooper())) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { switch (resultCode) { diff --git a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java index 8fd0213eb91..6d7c0624272 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java @@ -345,7 +345,7 @@ abstract class QtLoader { if (metadata == null || !metadata.containsKey(key)) return ""; - return String.valueOf(metadata.get(key)); + return String.valueOf(metadata.getString(key)); } @SuppressLint("DiscouragedApi") diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java index 080bb787a2d..7e4632f6792 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java @@ -361,8 +361,11 @@ public class QtNative if (m_stateDetails.isStarted) return; + getQtThread().run(() -> initAndroidQpaPlugin()); final String qtParams = mainLib + " " + params; getQtThread().post(() -> { startQtNativeApplication(qtParams); }); + waitForServiceSetup(); + setStarted(true); } @UsedFromNativeCode @@ -428,7 +431,9 @@ public class QtNative } // application methods + static native boolean initAndroidQpaPlugin(); static native void startQtNativeApplication(String params); + static native void waitForServiceSetup(); static native void terminateQtNativeApplication(); static native boolean updateNativeActivity(); // application methods diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindowInsetsController.java b/src/android/jar/src/org/qtproject/qt/android/QtWindowInsetsController.java new file mode 100644 index 00000000000..a3d0a0df8e5 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindowInsetsController.java @@ -0,0 +1,358 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +package org.qtproject.qt.android; + +import android.app.Activity; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.os.Build; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowInsetsController; +import android.view.Window; + +import android.graphics.Color; +import android.util.TypedValue; +import android.content.res.Resources.Theme; + +class QtWindowInsetsController +{ + /* + * Convenience method to call deprecated API prior to Android R (30). + */ + @SuppressWarnings ("deprecation") + private static void setDecorFitsSystemWindows(Window window, boolean enable) + { + final int sdk = Build.VERSION.SDK_INT; + if (sdk < Build.VERSION_CODES.R || sdk > Build.VERSION_CODES.VANILLA_ICE_CREAM) + return; + window.setDecorFitsSystemWindows(enable); + } + + private static void useCutoutShortEdges(Window window, boolean enabled) + { + if (window == null) + return; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + WindowManager.LayoutParams layoutParams = window.getAttributes(); + layoutParams.layoutInDisplayCutoutMode = enabled + ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + : WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; + window.setAttributes(layoutParams); + } + } + + @UsedFromNativeCode + static void showNormal(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + setDecorFitsSystemWindows(window, true); + WindowInsetsController ctrl = window.getInsetsController(); + if (ctrl != null) { + ctrl.show(WindowInsets.Type.systemBars()); + ctrl.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT); + } + } else { + @SuppressWarnings("deprecation") + int flags = View.SYSTEM_UI_FLAG_VISIBLE; // clear all flags + setSystemUiVisibility(decor, flags); + } + + setTransparentSystemBars(activity, false); + useCutoutShortEdges(window, false); + + decor.post(() -> decor.requestApplyInsets()); + } + + /* + * Make system bars transparent for Andorid versions prior to Android 15. + */ + @SuppressWarnings("deprecation") + private static void setTransparentSystemBars(Activity activity, boolean transparent) + { + Window window = activity.getWindow(); + if (window == null) + return; + + if (edgeToEdgeEnabled(activity)) + return; + + // These are needed to operate on system bar colors + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + + if (transparent) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + window.setStatusBarColor(Color.TRANSPARENT); + window.setNavigationBarColor(Color.TRANSPARENT); + } else { + // Android 9 and prior doesn't add the semi-transparent bars + // to avoid low contrast system icons, so try to mimick it + // by taking the current color and only increase the opacity. + int statusBarColor = window.getStatusBarColor(); + int transparentStatusBar = statusBarColor & 0x00FFFFFF; + window.setStatusBarColor(transparentStatusBar); + + int navigationBarColor = window.getNavigationBarColor(); + int semiTransparentNavigationBar = navigationBarColor & 0x7FFFFFFF; + window.setNavigationBarColor(semiTransparentNavigationBar); + } + } else { + // Restore theme's system bars colors + int defaultStatusBarColor = getThemeDefaultStatusBarColor(activity); + window.setStatusBarColor(defaultStatusBarColor); + + int defaultNavigationBarColor = getThemeDefaultNavigationBarColor(activity); + window.setNavigationBarColor(defaultNavigationBarColor); + } + } + + @UsedFromNativeCode + static void showExpanded(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + setDecorFitsSystemWindows(window, false); + WindowInsetsController ctrl = window.getInsetsController(); + if (ctrl != null) { + ctrl.show(WindowInsets.Type.systemBars()); + ctrl.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT); + } + } else { + @SuppressWarnings("deprecation") + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + setSystemUiVisibility(decor, flags); + } + + setTransparentSystemBars(activity, true); + useCutoutShortEdges(window, true); + + decor.post(() -> decor.requestApplyInsets()); + } + + @UsedFromNativeCode + public static void showFullScreen(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + setDecorFitsSystemWindows(window, false); + WindowInsetsController ctrl = window.getInsetsController(); + if (ctrl != null) { + ctrl.hide(WindowInsets.Type.systemBars()); + ctrl.setSystemBarsBehavior( + WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + } else { + @SuppressWarnings("deprecation") + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + setSystemUiVisibility(decor, flags); + } + + useCutoutShortEdges(window, true); + + decor.post(() -> decor.requestApplyInsets()); + } + + private static boolean edgeToEdgeEnabled(Activity activity) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) + return true; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) + return false; + int[] attrs = new int[] { android.R.attr.windowOptOutEdgeToEdgeEnforcement }; + TypedArray ta = activity.getTheme().obtainStyledAttributes(attrs); + try { + return !ta.getBoolean(0, false); + } finally { + ta.recycle(); + } + } + + static boolean isFullScreen(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return false; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets(); + if (insets != null) + return !insets.isVisible(WindowInsets.Type.statusBars()); + } else { + @SuppressWarnings("deprecation") + int flags = decor.getSystemUiVisibility(); + @SuppressWarnings("deprecation") + int immersiveMask = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + return (flags & immersiveMask) == immersiveMask; + } + + return false; + } + + static boolean isExpandedClientArea(Activity activity) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return edgeToEdgeEnabled(activity); + + @SuppressWarnings("deprecation") + int statusBarColor = activity.getWindow().getStatusBarColor(); + // If the status bar is not fully opaque assume we have expanded client + // area and we're drawing under it. + int statusBarAlpha = statusBarColor >>> 24; + return statusBarAlpha != 0xFF; + } + + static boolean decorFitsSystemWindows(Activity activity) + { + return !isFullScreen(activity) && !isExpandedClientArea(activity); + } + + static void restoreFullScreenVisibility(Activity activity) + { + if (isFullScreen(activity)) + showFullScreen(activity); + } + + /* + * Convenience method to call deprecated API prior to Android R (30). + */ + @SuppressWarnings ("deprecation") + private static void setSystemUiVisibility(View decorView, int flags) + { + decorView.setSystemUiVisibility(flags); + } + + /* + * Set the status bar color scheme hint so that the system decides how to color the icons. + */ + @UsedFromNativeCode + static void setStatusBarColorHint(Activity activity, boolean isLight) + { + Window window = activity.getWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowInsetsController controller = window.getInsetsController(); + if (controller != null) { + int lightStatusBarMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + int appearance = isLight ? lightStatusBarMask : 0; + controller.setSystemBarsAppearance(appearance, lightStatusBarMask); + } + } else { + @SuppressWarnings("deprecation") + int currentFlags = window.getDecorView().getSystemUiVisibility(); + @SuppressWarnings("deprecation") + int lightStatusBarMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + int appearance = isLight + ? currentFlags | lightStatusBarMask + : currentFlags & ~lightStatusBarMask; + setSystemUiVisibility(window.getDecorView(), appearance); + } + } + + /* + * Set the navigation bar color scheme hint so that the system decides how to color the icons. + */ + @UsedFromNativeCode + static void setNavigationBarColorHint(Activity activity, boolean isLight) + { + Window window = activity.getWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowInsetsController controller = window.getInsetsController(); + if (controller != null) { + int lightNavigationBarMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; + int appearance = isLight ? lightNavigationBarMask : 0; + controller.setSystemBarsAppearance(appearance, lightNavigationBarMask); + } + } else { + @SuppressWarnings("deprecation") + int currentFlags = window.getDecorView().getSystemUiVisibility(); + @SuppressWarnings("deprecation") + int lightNavigationBarMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + int appearance = isLight + ? currentFlags | lightNavigationBarMask + : currentFlags & ~lightNavigationBarMask; + setSystemUiVisibility(window.getDecorView(), appearance); + } + } + + private static int resolveColorAttribute(Activity activity, int attribute) + { + Theme theme = activity.getTheme(); + Resources resources = activity.getResources(); + TypedValue tv = new TypedValue(); + + if (theme.resolveAttribute(attribute, tv, true)) { + if (tv.resourceId != 0) + return resources.getColor(tv.resourceId, theme); + if (tv.type >= TypedValue.TYPE_FIRST_COLOR_INT && tv.type <= TypedValue.TYPE_LAST_COLOR_INT) + return tv.data; + } + + return -1; + } + + @SuppressWarnings("deprecation") + static int getThemeDefaultStatusBarColor(Activity activity) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return -1; + return resolveColorAttribute(activity, android.R.attr.statusBarColor); + } + + @SuppressWarnings("deprecation") + static int getThemeDefaultNavigationBarColor(Activity activity) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return -1; + return resolveColorAttribute(activity, android.R.attr.navigationBarColor); + } + + static void enableSystemBarsBackgroundDrawing(Window window) + { + // These are needed to operate on system bar colors + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + @SuppressWarnings("deprecation") + final int translucentFlags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + window.clearFlags(translucentFlags); + } + + @SuppressWarnings("deprecation") + static void setStatusBarColor(Window window, int color) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return; + window.setStatusBarColor(color); + } + + @SuppressWarnings("deprecation") + static void setNavigationBarColor(Window window, int color) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return; + window.setNavigationBarColor(color); + } +} diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java b/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java index d42b3e6821e..1cd36f06f5c 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java @@ -7,5 +7,4 @@ interface QtWindowInterface { default void removeTopLevelWindow(final int id) { } default void bringChildToFront(final int id) { } default void bringChildToBack(int id) { } - default void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) { } } diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h index bc2bbf9e53e..723a462bae1 100644 --- a/src/corelib/global/qnumeric.h +++ b/src/corelib/global/qnumeric.h @@ -20,6 +20,18 @@ #include <limits> #include <QtCore/q20type_traits.h> +#if __has_include(<stdckdint.h>) +// This was added by C23 and C++26, defining __STDC_VERSION_STDCKDINT_H__ when +// usable. Its provided functions are in the global namespace even in C++ (not +// std::) because they are sometimes macros. +# include <QtCore/qstdlibdetection.h> +# if defined(Q_CC_GNU_ONLY) && (defined(Q_STL_LIBCPP) || Q_CC_GNU_ONLY < 1500) +// broken - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121811 +# else +# include <stdckdint.h> +# endif +#endif + // min() and max() may be #defined by windows.h if that is included before, but we need them // for std::numeric_limits below. You should not use the min() and max() macros, so we just #undef. #ifdef min @@ -263,7 +275,10 @@ constexpr inline typename std::enable_if_t<std::is_unsigned_v<T>, bool> qAddOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v<T, char>, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_add(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_add_overflow(v1, v2, r); #else if (q20::is_constant_evaluated()) @@ -285,7 +300,7 @@ qAddOverflow(T v1, T v2, T *r) } # endif // defined(Q_HAVE_ADDCARRY) return QtPrivate::qAddOverflowGeneric(v1, v2, r); -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template <typename T> @@ -293,7 +308,10 @@ constexpr inline typename std::enable_if_t<std::is_signed_v<T>, bool> qAddOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v<T, char>, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_add(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_add_overflow(v1, v2, r); #else // Here's how we calculate the overflow: @@ -314,7 +332,7 @@ qAddOverflow(T v1, T v2, T *r) // (x ^ z) & (y ^ z) is negative if x and z have different signs // AND y and z have different signs return ((v1 ^ *r) & (v2 ^ *r)) < 0; -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template <typename T> @@ -322,13 +340,16 @@ constexpr inline typename std::enable_if_t<std::is_unsigned_v<T>, bool> qSubOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v<T, char>, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_sub(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_sub_overflow(v1, v2, r); #else // unsigned subtractions are well-defined *r = v1 - v2; return v1 < v2; -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template <typename T> @@ -336,7 +357,10 @@ constexpr inline typename std::enable_if_t<std::is_signed_v<T>, bool> qSubOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v<T, char>, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_sub(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) return __builtin_sub_overflow(v1, v2, r); #else // See above for explanation. This is the same with some signs reversed. @@ -347,7 +371,7 @@ qSubOverflow(T v1, T v2, T *r) *r = T(U(v1) - U(v2)); return ((v1 ^ *r) & (~v2 ^ *r)) < 0; -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } template <typename T> @@ -355,7 +379,10 @@ constexpr inline typename std::enable_if_t<std::is_unsigned_v<T> || std::is_signed_v<T>, bool> qMulOverflow(T v1, T v2, T *r) { -#if defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) + static_assert(!std::is_same_v<T, char>, "Template must be an integral other than plain 'char'"); +#if defined(__STDC_VERSION_STDCKDINT_H__) + return ckd_mul(r, v1, v2); +#elif defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) # if defined(Q_INTRINSIC_MUL_OVERFLOW64) return __builtin_mul_overflow(v1, v2, r); # else @@ -388,7 +415,7 @@ qMulOverflow(T v1, T v2, T *r) # endif // defined(Q_INTRINSIC_MUL_OVERFLOW64) return QtPrivate::qMulOverflowGeneric(v1, v2, r); -#endif // defined(Q_NUMERIC_USE_GCC_OVERFLOW_BUILTINS) +#endif } #undef Q_HAVE_ADDCARRY @@ -421,6 +448,8 @@ template <auto V2, typename T> constexpr bool qSubOverflow(T v1, T *r) template <typename T, T V2> constexpr bool qMulOverflow(T v1, std::integral_constant<T, V2>, T *r) { + static_assert(!std::is_same_v<T, char>, "Template must be an integral other than plain 'char'"); + // Runtime detection for anything smaller than or equal to a register // width, as most architectures' multiplication instructions actually // produce a result twice as wide as the input registers, allowing us to diff --git a/src/corelib/kernel/qwinregistry.cpp b/src/corelib/kernel/qwinregistry.cpp index 9740edbf299..fb315cacb7e 100644 --- a/src/corelib/kernel/qwinregistry.cpp +++ b/src/corelib/kernel/qwinregistry.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #define UMDF_USING_NTSTATUS // Avoid ntstatus redefinitions diff --git a/src/corelib/kernel/qwinregistry_p.h b/src/corelib/kernel/qwinregistry_p.h index 5643a35363b..fd6d6dc853f 100644 --- a/src/corelib/kernel/qwinregistry_p.h +++ b/src/corelib/kernel/qwinregistry_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:trivial-impl-only #ifndef QWINREGISTRY_H #define QWINREGISTRY_H @@ -41,12 +42,17 @@ public: ~QWinRegistryKey(); QWinRegistryKey(QWinRegistryKey &&other) noexcept +#if 1 // QTBUG-140725 + = delete; + void operator=(QWinRegistryKey &&) = delete; +#else : m_key(std::exchange(other.m_key, nullptr)) {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QWinRegistryKey) void swap(QWinRegistryKey &other) noexcept { qt_ptr_swap(m_key, other.m_key); } +#endif [[nodiscard]] bool isValid() const { return m_key != nullptr; } diff --git a/src/corelib/serialization/qcborarray.cpp b/src/corelib/serialization/qcborarray.cpp index 9a566b999b4..de6edcb75e6 100644 --- a/src/corelib/serialization/qcborarray.cpp +++ b/src/corelib/serialization/qcborarray.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2018 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qcborarray.h" #include "qcborvalue_p.h" diff --git a/src/corelib/serialization/qcborcommon_p.h b/src/corelib/serialization/qcborcommon_p.h index b80451be4bc..66c95ee887f 100644 --- a/src/corelib/serialization/qcborcommon_p.h +++ b/src/corelib/serialization/qcborcommon_p.h @@ -1,6 +1,7 @@ // Copyright (C) 2018 Intel Corporation. // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #ifndef QCBORCOMMON_P_H #define QCBORCOMMON_P_H diff --git a/src/corelib/serialization/qcbormap.cpp b/src/corelib/serialization/qcbormap.cpp index 32d6d563ac4..9c17e07b70b 100644 --- a/src/corelib/serialization/qcbormap.cpp +++ b/src/corelib/serialization/qcbormap.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2018 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qcbormap.h" #include "qcborvalue_p.h" diff --git a/src/corelib/serialization/qcborstream.h b/src/corelib/serialization/qcborstream.h index 7850d266639..e51296f3cca 100644 --- a/src/corelib/serialization/qcborstream.h +++ b/src/corelib/serialization/qcborstream.h @@ -1,6 +1,7 @@ // Copyright (C) 2019 The Qt Company Ltd. // Copyright (C) 2018 Intel Corporation. // 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:header-decls-only #ifndef QCBORSTREAM_H #define QCBORSTREAM_H diff --git a/src/corelib/serialization/qjsonparser_p.h b/src/corelib/serialization/qjsonparser_p.h index 6b70af38152..8951bb3129b 100644 --- a/src/corelib/serialization/qjsonparser_p.h +++ b/src/corelib/serialization/qjsonparser_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:header-decls-only #ifndef QJSONPARSER_P_H #define QJSONPARSER_P_H diff --git a/src/corelib/serialization/qjsonwriter_p.h b/src/corelib/serialization/qjsonwriter_p.h index 446ed906e21..10b89f3c106 100644 --- a/src/corelib/serialization/qjsonwriter_p.h +++ b/src/corelib/serialization/qjsonwriter_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:header-decls-only #ifndef QJSONWRITER_P_H #define QJSONWRITER_P_H diff --git a/src/gui/accessible/linux/qspiaccessiblebridge.cpp b/src/gui/accessible/linux/qspiaccessiblebridge.cpp index 1ee1a435ca5..11b3bc57471 100644 --- a/src/gui/accessible/linux/qspiaccessiblebridge.cpp +++ b/src/gui/accessible/linux/qspiaccessiblebridge.cpp @@ -184,6 +184,12 @@ static RoleMapping map[] = { //: Role of an accessible object { QAccessible::CheckBox, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, //: Role of an accessible object +#if ATSPI_ROLE_COUNT >= 132 + { QAccessible::Switch, ATSPI_ROLE_SWITCH, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "switch") }, +#else + { QAccessible::Switch, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, +#endif + //: Role of an accessible object { QAccessible::RadioButton, ATSPI_ROLE_RADIO_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "radio button") }, //: Role of an accessible object { QAccessible::ComboBox, ATSPI_ROLE_COMBO_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "combo box") }, diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index 97f7eb94e2a..eeb06c535b8 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -342,6 +342,7 @@ Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core"); \value Splitter A splitter distributing available space between its child widgets. \value StaticText Static text, such as labels for other widgets. \value StatusBar A status bar. + \value [since 6.11] Switch A switch that can be toggled on or off. \value Table A table representing data in a grid of rows and columns. \value Terminal A terminal or command line interface. \value TitleBar The title bar caption of a window. diff --git a/src/gui/accessible/qaccessible_base.h b/src/gui/accessible/qaccessible_base.h index 0a1a305b76d..31b97880ffc 100644 --- a/src/gui/accessible/qaccessible_base.h +++ b/src/gui/accessible/qaccessible_base.h @@ -267,6 +267,7 @@ public: WebDocument = 0x00000084, Section = 0x00000085, Notification = 0x00000086, + Switch = 0x00000087, // IAccessible2 roles // IA2_ROLE_CANVAS = 0x401, // An object that can be drawn into and to manage events from the objects drawn into it diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp index 5c723cdb289..9edb0dff4fd 100644 --- a/src/gui/accessible/qaccessibleobject.cpp +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -119,14 +119,14 @@ QWindow *QAccessibleApplication::window() const return nullptr; } -// all toplevel windows except popups and the desktop +// all toplevel windows except popups static QObjectList topLevelObjects() { QObjectList list; const QWindowList tlw(QGuiApplication::topLevelWindows()); for (int i = 0; i < tlw.size(); ++i) { QWindow *w = tlw.at(i); - if (w->type() != Qt::Popup && w->type()) { + if (w->type() != Qt::Popup) { if (QAccessibleInterface *root = w->accessibleRoot()) { if (root->object()) list.append(root->object()); diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 8e21c92f9b3..8f29fb503b0 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -352,8 +352,8 @@ QIconTheme::QIconTheme(const QString &themeName) QFile themeIndex; const QStringList iconDirs = QIcon::themeSearchPaths(); - for ( int i = 0 ; i < iconDirs.size() ; ++i) { - QDir iconDir(iconDirs[i]); + for (const auto &dirName : iconDirs) { + QDir iconDir(dirName); QString themeDir = iconDir.path() + u'/' + themeName; QFileInfo themeDirInfo(themeDir); @@ -479,7 +479,7 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, const QString pngIconName = iconNameFallback + ".png"_L1; // Add all relevant files - for (int i = 0; i < contentDirs.size(); ++i) { + for (qsizetype i = 0; i < contentDirs.size(); ++i) { QList<QIconDirInfo> subDirs = theme.keyList(); // Try to reduce the amount of subDirs by looking in the GTK+ cache in order to save @@ -504,8 +504,7 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, } QString contentDir = contentDirs.at(i) + u'/'; - for (int j = 0; j < subDirs.size() ; ++j) { - const QIconDirInfo &dirInfo = subDirs.at(j); + for (const auto &dirInfo : std::as_const(subDirs)) { if (searchingGenericFallback && (dirInfo.context == QIconDirInfo::Applications || dirInfo.context == QIconDirInfo::MimeTypes)) @@ -544,9 +543,9 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, << "skipping visited" << visited; // Search recursively through inherited themes - for (int i = 0 ; i < parents.size() ; ++i) { + for (const auto &parent : parents) { - const QString parentTheme = parents.at(i).trimmed(); + const QString parentTheme = parent.trimmed(); if (!visited.contains(parentTheme)) // guard against recursion info = findIconHelper(parentTheme, iconName, visited, QIconLoader::NoFallBack); diff --git a/src/gui/kernel/qplatformwindow_p.h b/src/gui/kernel/qplatformwindow_p.h index c446ac760c0..24c0fd7c431 100644 --- a/src/gui/kernel/qplatformwindow_p.h +++ b/src/gui/kernel/qplatformwindow_p.h @@ -125,6 +125,14 @@ struct Q_GUI_EXPORT QWaylandWindow : public QObject public: QT_DECLARE_NATIVE_INTERFACE(QWaylandWindow, 1, QWindow) + enum WindowType { + Default, + ToolTip, + ComboBox, + Menu, + SubMenu, + }; + virtual wl_surface *surface() const = 0; virtual void setCustomMargins(const QMargins &margins) = 0; virtual void requestXdgActivationToken(uint serial) = 0; @@ -136,6 +144,10 @@ public: return role ? *role : nullptr; } virtual void setSessionRestoreId(const QString &role) = 0; + + virtual void setExtendedWindowType(WindowType windowType) = 0; + virtual void setParentControlGeometry(const QRect &parentAnchor) = 0; + Q_SIGNALS: void surfaceCreated(); void surfaceDestroyed(); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 3b64a8ecf73..a3f9f069b69 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1527,14 +1527,14 @@ void QPainterPrivate::initFrom(const QPaintDevice *device) Q_Q(QPainter); device->initPainter(q); +} - if (extended) { - extended->penChanged(); - } else if (engine) { - engine->setDirty(QPaintEngine::DirtyPen); - engine->setDirty(QPaintEngine::DirtyBrush); - engine->setDirty(QPaintEngine::DirtyFont); - } +void QPainterPrivate::setEngineDirtyFlags(QSpan<const QPaintEngine::DirtyFlags> flags) +{ + if (!engine) + return; + for (const QPaintEngine::DirtyFlags f : flags) + engine->setDirty(f); } /*! @@ -1801,14 +1801,16 @@ bool QPainter::begin(QPaintDevice *pd) d->engine->setActive(begun); } - // Copy painter properties from original paint device, - // required for QPixmap::grabWidget() - if (d->original_device->devType() == QInternal::Widget) { + switch (d->original_device->devType()) { + case QInternal::Widget: d->initFrom(d->original_device); - } else { + break; + + default: d->state->layoutDirection = Qt::LayoutDirectionAuto; // make sure we have a font compatible with the paintdevice d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device()); + break; } QRect systemRect = d->engine->systemRect(); @@ -1834,6 +1836,15 @@ bool QPainter::begin(QPaintDevice *pd) d->state->emulationSpecifier = 0; + switch (d->original_device->devType()) { + case QInternal::Widget: + // for widgets we've aleady initialized the painter above + break; + default: + d->initFrom(d->original_device); + break; + } + return true; } diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index dd4653a5788..a6f93134ca0 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -15,6 +15,7 @@ // We mean it. // +#include <QtCore/qspan.h> #include <QtCore/qvarlengtharray.h> #include <QtGui/private/qtguiglobal_p.h> #include "QtGui/qbrush.h" @@ -242,6 +243,8 @@ public: std::unique_ptr<QEmulationPaintEngine> emulationEngine; QPaintEngineEx *extended = nullptr; QBrush colorBrush; // for fill with solid color + + Q_GUI_EXPORT void setEngineDirtyFlags(QSpan<const QPaintEngine::DirtyFlags>); }; Q_GUI_EXPORT void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation); 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/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/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}, diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 9cfd877d945..64af8c10fc1 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -68,6 +68,8 @@ bool Moc::parseClassHead(ClassDef *def) const QByteArrayView lex = lexemView(); if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") name = lexem(); + else + def->isFinal = true; } def->qualified += name; @@ -85,6 +87,8 @@ bool Moc::parseClassHead(ClassDef *def) const QByteArrayView lex = lexemView(); if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") return false; + else + def->isFinal = true; } if (test(COLON)) { @@ -2055,6 +2059,8 @@ QJsonObject ClassDef::toJson() const cls["className"_L1] = QString::fromUtf8(classname.constData()); cls["qualifiedClassName"_L1] = QString::fromUtf8(qualified.constData()); cls["lineNumber"_L1] = lineNumber; + if (isFinal) + cls["final"_L1] = true; QJsonArray classInfos; for (const auto &info: std::as_const(classInfoList)) { diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index bf55aa7c44f..aafa80d2164 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -207,6 +207,7 @@ struct ClassDef : BaseDef { bool hasQGadget = false; bool hasQNamespace = false; bool requireCompleteMethodTypes = false; + bool isFinal = false; QJsonObject toJson() const; }; diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 6fcfcf1b1ef..53ce4dd8211 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1432,8 +1432,8 @@ void QApplicationPrivate::notifyWindowIconChanged() // in case there are any plain QWindows in this QApplication-using // application, also send the notification to them - for (int i = 0; i < windowList.size(); ++i) - QCoreApplication::sendEvent(windowList.at(i), &ev); + for (QWindow *w : std::as_const(windowList)) + QCoreApplication::sendEvent(w, &ev); } /*! @@ -1774,9 +1774,9 @@ void QApplicationPrivate::notifyLayoutDirectionChange() // in case there are any plain QWindows in this QApplication-using // application, also send the notification to them - for (int i = 0; i < windowList.size(); ++i) { + for (QWindow *w: std::as_const(windowList)) { QEvent ev(QEvent::ApplicationLayoutDirectionChange); - QCoreApplication::sendEvent(windowList.at(i), &ev); + QCoreApplication::sendEvent(w, &ev); } } @@ -1863,14 +1863,12 @@ void QApplicationPrivate::setActiveWindow(QWidget* act) QEvent windowActivate(QEvent::WindowActivate); QEvent windowDeactivate(QEvent::WindowDeactivate); - for (int i = 0; i < toBeActivated.size(); ++i) { - QWidget *w = toBeActivated.at(i); + for (QWidget *w : std::as_const(toBeActivated)) { QApplication::sendSpontaneousEvent(w, &windowActivate); QApplication::sendSpontaneousEvent(w, &activationChange); } - for(int i = 0; i < toBeDeactivated.size(); ++i) { - QWidget *w = toBeDeactivated.at(i); + for (QWidget *w : std::as_const(toBeDeactivated)) { QApplication::sendSpontaneousEvent(w, &windowDeactivate); QApplication::sendSpontaneousEvent(w, &activationChange); } @@ -2082,8 +2080,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con } QEvent leaveEvent(QEvent::Leave); - for (int i = 0; i < leaveList.size(); ++i) { - auto *w = leaveList.at(i); + for (QWidget *w : std::as_const(leaveList)) { if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, nullptr)) { QCoreApplication::sendEvent(w, &leaveEvent); if (w->testAttribute(Qt::WA_Hover) && @@ -2125,8 +2122,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con // Whenever we leave an alien widget on X11/QPA, we need to reset its nativeParentWidget()'s cursor. // This is not required on Windows as the cursor is reset on every single mouse move. QWidget *parentOfLeavingCursor = nullptr; - for (int i = 0; i < leaveList.size(); ++i) { - auto *w = leaveList.at(i); + for (QWidget *w : std::as_const(leaveList)) { if (!isAlien(w)) break; if (w->testAttribute(Qt::WA_SetCursor)) { @@ -3085,7 +3081,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) const QPoint offset = w->pos(); w = w->parentWidget(); QMutableTouchEvent::setTarget(touchEvent, w); - for (int i = 0; i < touchEvent->pointCount(); ++i) { + for (qsizetype cnt = touchEvent->pointCount(), i = 0; i < cnt; ++i) { auto &pt = touchEvent->point(i); QMutableEventPoint::setPosition(pt, pt.position() + offset); } @@ -3164,8 +3160,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) res = d->notify_helper(w, &ge); gestureEvent->m_spont = false; eventAccepted = ge.isAccepted(); - for (int i = 0; i < gestures.size(); ++i) { - QGesture *g = gestures.at(i); + for (QGesture *g : std::as_const(gestures)) { // Ignore res [event return value] because handling of multiple gestures // packed into a single QEvent depends on not consuming the event if (eventAccepted || ge.isAccepted(g)) { @@ -3708,7 +3703,7 @@ bool QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEven { bool containsPress = false; - for (int i = 0; i < touchEvent->pointCount(); ++i) { + for (qsizetype cnt = touchEvent->pointCount(), i = 0; i < cnt; ++i) { auto &pt = touchEvent->point(i); QMutableEventPoint::setPosition(pt, widget->mapFromGlobal(pt.globalPosition())); @@ -3768,7 +3763,7 @@ void QApplicationPrivate::activateImplicitTouchGrab(QWidget *widget, QTouchEvent // If the widget dispatched the event further (see QGraphicsProxyWidget), then // there might already be an implicit grabber. Don't override that. A widget that // has partially recognized a gesture needs to grab all points. - for (int i = 0; i < touchEvent->pointCount(); ++i) { + for (qsizetype cnt = touchEvent->pointCount(), i = 0; i < cnt; ++i) { auto &ep = touchEvent->point(i); if (!QMutableEventPoint::target(ep) && (ep.isAccepted() || grabMode == GrabAllPoints)) QMutableEventPoint::setTarget(ep, widget); diff --git a/src/widgets/kernel/qtooltip.cpp b/src/widgets/kernel/qtooltip.cpp index 9e6aaf4b95f..d989feb7f91 100644 --- a/src/widgets/kernel/qtooltip.cpp +++ b/src/widgets/kernel/qtooltip.cpp @@ -20,6 +20,8 @@ #if QT_CONFIG(style_stylesheet) #include <private/qstylesheetstyle_p.h> #endif +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #include <qlabel.h> #include <QtWidgets/private/qlabel_p.h> @@ -386,6 +388,17 @@ void QTipLabel::placeTip(const QPoint &pos, QWidget *w) p += offset; +#if QT_CONFIG(wayland) + create(); + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(windowHandle()->handle())) { + // based on the existing code below, by default position at 'p' stored at the bottom right of our rect + // then flip to the other arbitrary 4x24 space if constrained + const QRect controlGeometry(QRect(p.x() - 4, p.y() - 24, 4, 24)); + waylandWindow->setParentControlGeometry(controlGeometry); + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ToolTip); + } +#endif + QRect screenRect = screen->geometry(); if (p.x() + this->width() > screenRect.x() + screenRect.width()) p.rx() -= 4 + this->width(); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 36446c3e5c4..9499c88af12 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -13144,11 +13144,22 @@ int QWidget::metric(PaintDeviceMetric m) const void QWidget::initPainter(QPainter *painter) const { const QPalette &pal = palette(); - painter->d_func()->state->pen = QPen(pal.brush(foregroundRole()), 1); - painter->d_func()->state->bgBrush = pal.brush(backgroundRole()); + QPainterPrivate *painterPrivate = QPainterPrivate::get(painter); + + painterPrivate->state->pen = QPen(pal.brush(foregroundRole()), 1); + painterPrivate->state->bgBrush = pal.brush(backgroundRole()); QFont f(font(), this); - painter->d_func()->state->deviceFont = f; - painter->d_func()->state->font = f; + painterPrivate->state->deviceFont = f; + painterPrivate->state->font = f; + + painterPrivate->setEngineDirtyFlags({ + QPaintEngine::DirtyPen, + QPaintEngine::DirtyBrush, + QPaintEngine::DirtyFont, + }); + + if (painterPrivate->extended) + painterPrivate->extended->penChanged(); } /*! diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 737ccb0e807..622f1548756 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -591,6 +591,10 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) } } + // Event delivery above might have destroyed this object. See QTBUG-138419. + if (self.isNull()) + return; + if (QApplication::activePopupWidget() != activePopupWidget && QApplicationPrivate::replayMousePress && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) { diff --git a/src/widgets/util/qcompleter.cpp b/src/widgets/util/qcompleter.cpp index 220f600ea41..735b574d293 100644 --- a/src/widgets/util/qcompleter.cpp +++ b/src/widgets/util/qcompleter.cpp @@ -122,6 +122,8 @@ #include "QtGui/qevent.h" #include <private/qapplication_p.h> #include <private/qwidget_p.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #if QT_CONFIG(lineedit) #include "QtWidgets/qlineedit.h" #endif @@ -925,10 +927,15 @@ void QCompleterPrivate::showPopup(const QRect& rect) popup->setGeometry(pos.x(), pos.y(), w, h); if (!popup->isVisible()) { - // Make sure popup has a transient parent set, Wayland needs it. QTBUG-130474 - popup->winId(); // force creation of windowHandle - popup->windowHandle()->setTransientParent(widget->window()->windowHandle()); - +#if QT_CONFIG(wayland) + popup->createWinId(); + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(popup->windowHandle()->handle())) { + popup->windowHandle()->setTransientParent(widget->window()->windowHandle()); + const QRect controlGeometry = QRect(widget->mapTo(widget->topLevelWidget(), QPoint(0,0)), widget->size()); + waylandWindow->setParentControlGeometry(controlGeometry); + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ComboBox); + } +#endif popup->show(); } } diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 6f25b8bde67..2f51b83a49d 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -7,6 +7,9 @@ #include <qstylepainter.h> #include <qpa/qplatformtheme.h> #include <qpa/qplatformmenu.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> + #include <qlineedit.h> #include <qapplication.h> #include <qlistview.h> @@ -2868,6 +2871,17 @@ void QComboBox::showPopup() container->hide(); } } + +#if QT_CONFIG(wayland) + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(container->windowHandle()->handle())) { + const QRect popup(style->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxListBoxPopup, this)); + const QRect controlGeometry = QRect(mapTo(window(), popup.topLeft()), popup.size()); + waylandWindow->setParentControlGeometry(controlGeometry); + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ComboBox); + } +#endif + container->show(); if (!neededHorizontalScrollBar && needHorizontalScrollBar()) { listRect.adjust(0, 0, 0, sb->height()); diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 5cda8f33f4c..7d4228709be 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -40,6 +40,8 @@ #include <private/qaction_p.h> #include <private/qguiapplication_p.h> #include <qpa/qplatformtheme.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #include <private/qstyle_p.h> QT_BEGIN_NAMESPACE @@ -2535,6 +2537,23 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po } popupScreen = QGuiApplication::screenAt(pos); q->setGeometry(QRect(pos, size)); + +#if QT_CONFIG(wayland) + q->create(); + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) { + if (causedButton) { + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu); + waylandWindow->setParentControlGeometry(causedButton->geometry()); + } else if (caused) { + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu); + waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction)); + } else if (auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) { + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu); + waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action)); + } + } +#endif + #if QT_CONFIG(effects) int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll; int vGuess = QEffects::DownScroll; diff --git a/src/widgets/widgets/qtoolbutton.cpp b/src/widgets/widgets/qtoolbutton.cpp index e6f7edc1ac1..01150cd39b2 100644 --- a/src/widgets/widgets/qtoolbutton.cpp +++ b/src/widgets/widgets/qtoolbutton.cpp @@ -58,7 +58,8 @@ public: Qt::ArrowType arrowType; Qt::ToolButtonStyle toolButtonStyle; QToolButton::ToolButtonPopupMode popupMode; - enum { NoButtonPressed=0, MenuButtonPressed=1, ToolButtonPressed=2 }; + uint popupModeSetByUser : 1; // true if popupMode was set through setPopupMode + enum { NoButtonPressed = 0, MenuButtonPressed = 1, ToolButtonPressed = 2 }; uint buttonPressed : 2; uint menuButtonDown : 1; uint autoRaise : 1; @@ -178,6 +179,7 @@ void QToolButtonPrivate::init() arrowType = Qt::NoArrow; menuButtonDown = false; popupMode = QToolButton::DelayedPopup; + popupModeSetByUser = false; buttonPressed = QToolButtonPrivate::NoButtonPressed; toolButtonStyle = Qt::ToolButtonIconOnly; @@ -827,7 +829,7 @@ void QToolButtonPrivate::onMenuTriggered(QAction *action) void QToolButtonPrivate::onDefaultActionChanged() { Q_Q(QToolButton); - if (defaultAction && defaultAction->menu()) + if (defaultAction && defaultAction->menu() && !popupModeSetByUser) q->setPopupMode(QToolButton::MenuButtonPopup); } @@ -864,6 +866,7 @@ void QToolButtonPrivate::onDefaultActionChanged() void QToolButton::setPopupMode(ToolButtonPopupMode mode) { Q_D(QToolButton); + d->popupModeSetByUser = true; d->popupMode = mode; } @@ -922,10 +925,11 @@ void QToolButton::setDefaultAction(QAction *action) { Q_D(QToolButton); #if QT_CONFIG(menu) - if (d->defaultAction) { + if (d->defaultAction && d->defaultAction != action) { QObjectPrivate::disconnect(d->defaultAction, &QAction::changed, d, &QToolButtonPrivate::onDefaultActionChanged); } + const bool hadMenu = d->hasMenu(); #endif d->defaultAction = action; if (!action) @@ -949,15 +953,18 @@ void QToolButton::setDefaultAction(QAction *action) setWhatsThis(action->whatsThis()); #endif #if QT_CONFIG(menu) - if (action->menu()) { + if (!hadMenu && !d->popupModeSetByUser) { // ### Qt7 Fixme // new 'default' popup mode defined introduced by tool bar. We // should have changed QToolButton's default instead. Do that // in 4.2. - setPopupMode(QToolButton::MenuButtonPopup); + if (action->menu()) { + setPopupMode(QToolButton::MenuButtonPopup); + } else { + QObjectPrivate::connect(d->defaultAction, &QAction::changed, d, + &QToolButtonPrivate::onDefaultActionChanged); + } } - QObjectPrivate::connect(d->defaultAction, &QAction::changed, d, - &QToolButtonPrivate::onDefaultActionChanged); #endif setCheckable(action->isCheckable()); setChecked(action->isChecked()); |