summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bin/qt-cmake.bat.in4
-rwxr-xr-xbin/qt-cmake.in5
-rw-r--r--src/3rdparty/libpng/ANNOUNCE52
-rw-r--r--src/3rdparty/libpng/CHANGES11
-rw-r--r--src/3rdparty/libpng/README2
-rw-r--r--src/3rdparty/libpng/libpng-manual.txt2
-rw-r--r--src/3rdparty/libpng/png.c4
-rw-r--r--src/3rdparty/libpng/png.h14
-rw-r--r--src/3rdparty/libpng/pngconf.h2
-rw-r--r--src/3rdparty/libpng/pnglibconf.h2
-rw-r--r--src/3rdparty/libpng/pngread.c51
-rw-r--r--src/3rdparty/libpng/pngrtran.c1
-rw-r--r--src/3rdparty/libpng/qt_attribution.json4
-rw-r--r--src/corelib/CMakeLists.txt7
-rw-r--r--src/corelib/configure.cmake27
-rw-r--r--src/corelib/doc/snippets/code/doc_src_properties.cpp2
-rw-r--r--src/corelib/doc/src/objectmodel/properties.qdoc16
-rw-r--r--src/corelib/global/qnumeric.h21
-rw-r--r--src/corelib/io/qioring.cpp30
-rw-r--r--src/corelib/io/qioring_linux.cpp13
-rw-r--r--src/corelib/io/qioring_p.h39
-rw-r--r--src/corelib/io/qioring_win.cpp754
-rw-r--r--src/corelib/itemmodels/qrangemodel.cpp1
-rw-r--r--src/corelib/itemmodels/qrangemodel_impl.h209
-rw-r--r--src/corelib/itemmodels/qrangemodeladapter.qdoc8
-rw-r--r--src/corelib/kernel/qmetaobject.cpp28
-rw-r--r--src/corelib/kernel/qmetaobject.h2
-rw-r--r--src/corelib/kernel/qtmocconstants.h3
-rw-r--r--src/corelib/mimetypes/qmimeprovider.cpp7
-rw-r--r--src/corelib/tools/qflatmap_p.h15
-rw-r--r--src/corelib/tools/qmargins.h15
-rw-r--r--src/corelib/tools/qpoint.h7
-rw-r--r--src/corelib/tools/qsize.h5
-rw-r--r--src/gui/accessible/linux/qspi_struct_marshallers.cpp19
-rw-r--r--src/gui/accessible/linux/qspi_struct_marshallers_p.h16
-rw-r--r--src/gui/accessible/qaccessiblecache.cpp23
-rw-r--r--src/gui/image/qabstractfileiconprovider.cpp2
-rw-r--r--src/gui/math3d/qquaternion.cpp2
-rw-r--r--src/gui/math3d/qvectornd.cpp3
-rw-r--r--src/gui/text/qrawfont.cpp2
-rw-r--r--src/gui/text/qrawfont.h2
-rw-r--r--src/gui/text/qtextengine.cpp2
-rw-r--r--src/plugins/platforms/cocoa/qnsview_drawing.mm3
-rw-r--r--src/plugins/platforms/wasm/qwasmdrag.cpp16
-rw-r--r--src/plugins/platforms/wasm/qwasmdrag.h2
-rw-r--r--src/plugins/platforms/wayland/qwaylandwindow.cpp1
-rw-r--r--src/plugins/platforms/wayland/qwaylandwindow_p.h1
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp7
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp2
-rw-r--r--src/tools/moc/generator.cpp4
-rw-r--r--src/tools/moc/moc.cpp24
-rw-r--r--src/tools/moc/moc.h2
-rw-r--r--src/widgets/accessible/itemviews.cpp31
-rw-r--r--src/widgets/itemviews/qabstractitemview.cpp41
-rw-r--r--src/widgets/itemviews/qabstractitemview_p.h12
-rw-r--r--src/widgets/itemviews/qlistview.cpp16
-rw-r--r--src/widgets/itemviews/qlistview_p.h4
-rw-r--r--src/widgets/itemviews/qtableview.cpp6
-rw-r--r--src/widgets/itemviews/qtableview_p.h5
-rw-r--r--src/widgets/itemviews/qtreeview.cpp10
-rw-r--r--src/widgets/itemviews/qtreeview_p.h4
-rw-r--r--tests/auto/corelib/io/CMakeLists.txt2
-rw-r--r--tests/auto/corelib/io/qioring/tst_qioring.cpp15
-rw-r--r--tests/auto/corelib/io/qurl/tst_qurl.cpp2
-rw-r--r--tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp15
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp30
-rw-r--r--tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp77
-rw-r--r--tests/auto/other/qaccessibility/tst_qaccessibility.cpp16
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp20
69 files changed, 1487 insertions, 315 deletions
diff --git a/bin/qt-cmake.bat.in b/bin/qt-cmake.bat.in
index 5d831ebce93..934f79c2599 100644
--- a/bin/qt-cmake.bat.in
+++ b/bin/qt-cmake.bat.in
@@ -7,5 +7,5 @@ set script_dir_path=%~dp0
set cmake_path=@CMAKE_COMMAND@
if not exist "%cmake_path%" set cmake_path=cmake
-set toolchain_path=%script_dir_path%\@__GlobalConfig_relative_path_from_bin_dir_to_cmake_config_dir@\qt.toolchain.cmake
-"%cmake_path%" -DCMAKE_TOOLCHAIN_FILE="%toolchain_path%" @__qt_cmake_extra@ %*
+set CMAKE_TOOLCHAIN_FILE=%script_dir_path%\@__GlobalConfig_relative_path_from_bin_dir_to_cmake_config_dir@\qt.toolchain.cmake
+"%cmake_path%" @__qt_cmake_extra@ %*
diff --git a/bin/qt-cmake.in b/bin/qt-cmake.in
index 363c490960c..5203e49f878 100755
--- a/bin/qt-cmake.in
+++ b/bin/qt-cmake.in
@@ -16,4 +16,7 @@ toolchain_path="$script_dir_path/@__GlobalConfig_relative_path_from_bin_dir_to_c
@extra_qt_cmake_code@
# Find the qt toolchain relative to the absolute bin dir path where the script is located.
-exec "$cmake_path" -DCMAKE_TOOLCHAIN_FILE="$toolchain_path" @__qt_cmake_extra@ "$@"
+CMAKE_TOOLCHAIN_FILE="$toolchain_path"
+export CMAKE_TOOLCHAIN_FILE
+
+exec "$cmake_path" @__qt_cmake_extra@ "$@"
diff --git a/src/3rdparty/libpng/ANNOUNCE b/src/3rdparty/libpng/ANNOUNCE
index ae0b6ccc13b..10dee70d834 100644
--- a/src/3rdparty/libpng/ANNOUNCE
+++ b/src/3rdparty/libpng/ANNOUNCE
@@ -1,5 +1,5 @@
-libpng 1.6.51 - November 21, 2025
-=================================
+libpng 1.6.52 - December 3, 2025
+================================
This is a public release of libpng, intended for use in production code.
@@ -7,15 +7,12 @@ This is a public release of libpng, intended for use in production code.
Files available for download
----------------------------
-Source files with LF line endings (for Unix/Linux):
+Source files:
- * libpng-1.6.51.tar.xz (LZMA-compressed, recommended)
- * libpng-1.6.51.tar.gz (deflate-compressed)
-
-Source files with CRLF line endings (for Windows):
-
- * lpng1651.7z (LZMA-compressed, recommended)
- * lpng1651.zip (deflate-compressed)
+ * libpng-1.6.52.tar.xz (LZMA-compressed, recommended)
+ * libpng-1.6.52.tar.gz (deflate-compressed)
+ * lpng1652.7z (LZMA-compressed)
+ * lpng1652.zip (deflate-compressed)
Other information:
@@ -25,33 +22,18 @@ Other information:
* TRADEMARK.md
-Changes from version 1.6.50 to version 1.6.51
+Changes from version 1.6.51 to version 1.6.52
---------------------------------------------
- * Fixed CVE-2025-64505 (moderate severity):
- Heap buffer overflow in `png_do_quantize` via malformed palette index.
- (Reported by Samsung; analyzed by Fabio Gritti.)
- * Fixed CVE-2025-64506 (moderate severity):
- Heap buffer over-read in `png_write_image_8bit` with 8-bit input and
- `convert_to_8bit` enabled.
- (Reported by Samsung and <weijinjinnihao@users.noreply.github.com>;
- analyzed by Fabio Gritti.)
- * Fixed CVE-2025-64720 (high severity):
- Buffer overflow in `png_image_read_composite` via incorrect palette
- premultiplication.
- (Reported by Samsung; analyzed by John Bowler.)
- * Fixed CVE-2025-65018 (high severity):
- Heap buffer overflow in `png_combine_row` triggered via
- `png_image_finish_read`.
- (Reported by <yosiimich@users.noreply.github.com>.)
- * Fixed a memory leak in `png_set_quantize`.
- (Reported by Samsung; analyzed by Fabio Gritti.)
- * Removed the experimental and incomplete ERROR_NUMBERS code.
- (Contributed by Tobias Stoeckmann.)
- * Improved the RISC-V vector extension support; required RVV 1.0 or newer.
- (Contributed by Filip Wasil.)
- * Added GitHub Actions workflows for automated testing.
- * Performed various refactorings and cleanups.
+ * Fixed CVE-2025-66293 (high severity):
+ Out-of-bounds read in `png_image_read_composite`.
+ (Reported by flyfish101 <flyfish101@users.noreply.github.com>.)
+ * Fixed the Paeth filter handling in the RISC-V RVV implementation.
+ (Reported by Filip Wasil; fixed by Liang Junzhao.)
+ * Improved the performance of the RISC-V RVV implementation.
+ (Contributed by Liang Junzhao.)
+ * Added allocation failure fuzzing to oss-fuzz.
+ (Contributed by Philippe Antoine.)
Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
diff --git a/src/3rdparty/libpng/CHANGES b/src/3rdparty/libpng/CHANGES
index 2478fd0fc08..f8ad74bbdf3 100644
--- a/src/3rdparty/libpng/CHANGES
+++ b/src/3rdparty/libpng/CHANGES
@@ -6304,6 +6304,17 @@ Version 1.6.51 [November 21, 2025]
Added GitHub Actions workflows for automated testing.
Performed various refactorings and cleanups.
+Version 1.6.52 [December 3, 2025]
+ Fixed CVE-2025-66293 (high severity):
+ Out-of-bounds read in `png_image_read_composite`.
+ (Reported by flyfish101 <flyfish101@users.noreply.github.com>.)
+ Fixed the Paeth filter handling in the RISC-V RVV implementation.
+ (Reported by Filip Wasil; fixed by Liang Junzhao.)
+ Improved the performance of the RISC-V RVV implementation.
+ (Contributed by Liang Junzhao.)
+ Added allocation failure fuzzing to oss-fuzz.
+ (Contributed by Philippe Antoine.)
+
Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
Subscription is required; visit
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
diff --git a/src/3rdparty/libpng/README b/src/3rdparty/libpng/README
index 5ea329ee3da..87e5f8b177e 100644
--- a/src/3rdparty/libpng/README
+++ b/src/3rdparty/libpng/README
@@ -1,4 +1,4 @@
-README for libpng version 1.6.51
+README for libpng version 1.6.52
================================
See the note about version numbers near the top of `png.h`.
diff --git a/src/3rdparty/libpng/libpng-manual.txt b/src/3rdparty/libpng/libpng-manual.txt
index f342c18e814..f284d987ba6 100644
--- a/src/3rdparty/libpng/libpng-manual.txt
+++ b/src/3rdparty/libpng/libpng-manual.txt
@@ -9,7 +9,7 @@ libpng-manual.txt - A description on how to use and modify libpng
Based on:
- libpng version 1.6.36, December 2018, through 1.6.51 - November 2025
+ libpng version 1.6.36, December 2018, through 1.6.52 - December 2025
Updated and distributed by Cosmin Truta
Copyright (c) 2018-2025 Cosmin Truta
diff --git a/src/3rdparty/libpng/png.c b/src/3rdparty/libpng/png.c
index 380c4c19e6a..11b65d1f13e 100644
--- a/src/3rdparty/libpng/png.c
+++ b/src/3rdparty/libpng/png.c
@@ -13,7 +13,7 @@
#include "pngpriv.h"
/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_51 Your_png_h_is_not_version_1_6_51;
+typedef png_libpng_version_1_6_52 Your_png_h_is_not_version_1_6_52;
/* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
* corresponding macro definitions. This causes a compile time failure if
@@ -817,7 +817,7 @@ png_get_copyright(png_const_structrp png_ptr)
return PNG_STRING_COPYRIGHT
#else
return PNG_STRING_NEWLINE \
- "libpng version 1.6.51" PNG_STRING_NEWLINE \
+ "libpng version 1.6.52" PNG_STRING_NEWLINE \
"Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \
"Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
PNG_STRING_NEWLINE \
diff --git a/src/3rdparty/libpng/png.h b/src/3rdparty/libpng/png.h
index fb93d2242b5..bceb9aa45d7 100644
--- a/src/3rdparty/libpng/png.h
+++ b/src/3rdparty/libpng/png.h
@@ -1,6 +1,6 @@
/* png.h - header file for PNG reference library
*
- * libpng version 1.6.51
+ * libpng version 1.6.52
*
* Copyright (c) 2018-2025 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
@@ -14,7 +14,7 @@
* libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
* libpng versions 0.97, January 1998, through 1.6.35, July 2018:
* Glenn Randers-Pehrson
- * libpng versions 1.6.36, December 2018, through 1.6.51, November 2025:
+ * libpng versions 1.6.36, December 2018, through 1.6.52, December 2025:
* Cosmin Truta
* See also "Contributing Authors", below.
*/
@@ -238,7 +238,7 @@
* ...
* 1.5.30 15 10530 15.so.15.30[.0]
* ...
- * 1.6.51 16 10651 16.so.16.51[.0]
+ * 1.6.52 16 10651 16.so.16.52[.0]
*
* Henceforth the source version will match the shared-library major and
* minor numbers; the shared-library major version number will be used for
@@ -274,7 +274,7 @@
*/
/* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.51"
+#define PNG_LIBPNG_VER_STRING "1.6.52"
#define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
/* The versions of shared library builds should stay in sync, going forward */
@@ -285,7 +285,7 @@
/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
#define PNG_LIBPNG_VER_MAJOR 1
#define PNG_LIBPNG_VER_MINOR 6
-#define PNG_LIBPNG_VER_RELEASE 51
+#define PNG_LIBPNG_VER_RELEASE 52
/* This should be zero for a public release, or non-zero for a
* development version.
@@ -316,7 +316,7 @@
* From version 1.0.1 it is:
* XXYYZZ, where XX=major, YY=minor, ZZ=release
*/
-#define PNG_LIBPNG_VER 10651 /* 1.6.51 */
+#define PNG_LIBPNG_VER 10652 /* 1.6.52 */
/* Library configuration: these options cannot be changed after
* the library has been built.
@@ -426,7 +426,7 @@ extern "C" {
/* This triggers a compiler error in png.c, if png.c and png.h
* do not agree upon the version number.
*/
-typedef char* png_libpng_version_1_6_51;
+typedef char* png_libpng_version_1_6_52;
/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info.
*
diff --git a/src/3rdparty/libpng/pngconf.h b/src/3rdparty/libpng/pngconf.h
index 981df68d87a..76b5c20bdff 100644
--- a/src/3rdparty/libpng/pngconf.h
+++ b/src/3rdparty/libpng/pngconf.h
@@ -1,6 +1,6 @@
/* pngconf.h - machine-configurable file for libpng
*
- * libpng version 1.6.51
+ * libpng version 1.6.52
*
* Copyright (c) 2018-2025 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
diff --git a/src/3rdparty/libpng/pnglibconf.h b/src/3rdparty/libpng/pnglibconf.h
index 00432d6c033..f4a993441f7 100644
--- a/src/3rdparty/libpng/pnglibconf.h
+++ b/src/3rdparty/libpng/pnglibconf.h
@@ -1,6 +1,6 @@
/* pnglibconf.h - library build configuration */
-/* libpng version 1.6.51 */
+/* libpng version 1.6.52 */
/* Copyright (c) 2018-2025 Cosmin Truta */
/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
diff --git a/src/3rdparty/libpng/pngread.c b/src/3rdparty/libpng/pngread.c
index 79917daaaf9..f8ca2b7e31d 100644
--- a/src/3rdparty/libpng/pngread.c
+++ b/src/3rdparty/libpng/pngread.c
@@ -3207,6 +3207,7 @@ png_image_read_composite(png_voidp argument)
ptrdiff_t step_row = display->row_bytes;
unsigned int channels =
(image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
+ int optimize_alpha = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;
int pass;
for (pass = 0; pass < passes; ++pass)
@@ -3263,20 +3264,44 @@ png_image_read_composite(png_voidp argument)
if (alpha < 255) /* else just use component */
{
- /* This is PNG_OPTIMIZED_ALPHA, the component value
- * is a linear 8-bit value. Combine this with the
- * current outrow[c] value which is sRGB encoded.
- * Arithmetic here is 16-bits to preserve the output
- * values correctly.
- */
- component *= 257*255; /* =65535 */
- component += (255-alpha)*png_sRGB_table[outrow[c]];
+ if (optimize_alpha != 0)
+ {
+ /* This is PNG_OPTIMIZED_ALPHA, the component value
+ * is a linear 8-bit value. Combine this with the
+ * current outrow[c] value which is sRGB encoded.
+ * Arithmetic here is 16-bits to preserve the output
+ * values correctly.
+ */
+ component *= 257*255; /* =65535 */
+ component += (255-alpha)*png_sRGB_table[outrow[c]];
- /* So 'component' is scaled by 255*65535 and is
- * therefore appropriate for the sRGB to linear
- * conversion table.
- */
- component = PNG_sRGB_FROM_LINEAR(component);
+ /* Clamp to the valid range to defend against
+ * unforeseen cases where the data might be sRGB
+ * instead of linear premultiplied.
+ * (Belt-and-suspenders for GitHub Issue #764.)
+ */
+ if (component > 255*65535)
+ component = 255*65535;
+
+ /* So 'component' is scaled by 255*65535 and is
+ * therefore appropriate for the sRGB-to-linear
+ * conversion table.
+ */
+ component = PNG_sRGB_FROM_LINEAR(component);
+ }
+ else
+ {
+ /* Compositing was already done on the palette
+ * entries. The data is sRGB premultiplied on black.
+ * Composite with the background in sRGB space.
+ * This is not gamma-correct, but matches what was
+ * done to the palette.
+ */
+ png_uint_32 background = outrow[c];
+ component += ((255-alpha) * background + 127) / 255;
+ if (component > 255)
+ component = 255;
+ }
}
outrow[c] = (png_byte)component;
diff --git a/src/3rdparty/libpng/pngrtran.c b/src/3rdparty/libpng/pngrtran.c
index 2f520225515..507d11381ec 100644
--- a/src/3rdparty/libpng/pngrtran.c
+++ b/src/3rdparty/libpng/pngrtran.c
@@ -1843,6 +1843,7 @@ png_init_read_transformations(png_structrp png_ptr)
* transformations elsewhere.
*/
png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
+ png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
} /* color_type == PNG_COLOR_TYPE_PALETTE */
/* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
diff --git a/src/3rdparty/libpng/qt_attribution.json b/src/3rdparty/libpng/qt_attribution.json
index fe8ba663881..1f942f8f564 100644
--- a/src/3rdparty/libpng/qt_attribution.json
+++ b/src/3rdparty/libpng/qt_attribution.json
@@ -7,8 +7,8 @@
"Description": "libpng is the official PNG reference library.",
"Homepage": "http://www.libpng.org/pub/png/libpng.html",
- "Version": "1.6.51",
- "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.51.tar.xz",
+ "Version": "1.6.52",
+ "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.52.tar.xz",
"PURL": "pkg:github/pnggroup/libpng@v$<VERSION>",
"CPE": "cpe:2.3:a:libpng:libpng:$<VERSION>:*:*:*:*:*:*:*",
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index dec68c5f9f4..ea8cf7b9c8e 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -583,7 +583,7 @@ if(QT_FEATURE_async_io)
SOURCES
io/qrandomaccessasyncfile_darwin.mm
)
- elseif(LINUX AND QT_FEATURE_liburing)
+ elseif((LINUX AND QT_FEATURE_liburing) OR (WIN32 AND QT_FEATURE_windows_ioring))
qt_internal_extend_target(Core
SOURCES
io/qrandomaccessasyncfile_qioring.cpp
@@ -763,6 +763,11 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_liburing
uring
)
+qt_internal_extend_target(Core CONDITION QT_FEATURE_windows_ioring
+ SOURCES
+ io/qioring.cpp io/qioring_win.cpp io/qioring_p.h
+)
+
# Workaround for QTBUG-101411
# Remove if QCC (gcc version 8.3.0) for QNX 7.1.0 is no longer supported
qt_internal_extend_target(Core CONDITION QCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "8.3.0")
diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake
index c1d15c75054..7216f2920fe 100644
--- a/src/corelib/configure.cmake
+++ b/src/corelib/configure.cmake
@@ -605,6 +605,27 @@ int main(void)
"
)
+qt_config_compile_test(windows_ioring
+ LABEL "Windows SDK: IORing"
+ CODE
+"#include <windows.h>
+#include <ioringapi.h>
+
+int main(void)
+{
+ /* BEGIN TEST: */
+ IORING_CREATE_FLAGS flags;
+ memset(&flags, 0, sizeof(flags));
+ HIORING ioRingHandle = nullptr;
+ HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, 1, 1, &ioRingHandle);
+ if (hr == IORING_E_SUBMISSION_QUEUE_FULL) // not valid, but test that this #define exists
+ return 0;
+ /* END TEST: */
+ return 0;
+}
+"
+)
+
# cpp_winrt
qt_config_compile_test(cpp_winrt
LABEL "cpp/winrt"
@@ -785,6 +806,11 @@ qt_feature("winsdkicu" PRIVATE
CONDITION TEST_winsdkicu
DISABLE QT_FEATURE_icu
)
+qt_feature("windows_ioring" PRIVATE
+ LABEL "Windows I/O Ring"
+ AUTODETECT WIN32 AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 10.0.22000
+ CONDITION TEST_windows_ioring
+)
qt_feature("inotify" PUBLIC PRIVATE
LABEL "inotify"
CONDITION TEST_inotify OR TEST_fsnotify
@@ -1272,6 +1298,7 @@ qt_configure_add_summary_entry(ARGS "glib")
qt_configure_add_summary_entry(ARGS "icu")
qt_configure_add_summary_entry(ARGS "jemalloc")
qt_configure_add_summary_entry(ARGS "liburing")
+qt_configure_add_summary_entry(ARGS "windows_ioring")
qt_configure_add_summary_entry(ARGS "timezone_tzdb")
qt_configure_add_summary_entry(ARGS "system-libb2")
qt_configure_add_summary_entry(ARGS "mimetype-database")
diff --git a/src/corelib/doc/snippets/code/doc_src_properties.cpp b/src/corelib/doc/snippets/code/doc_src_properties.cpp
index eafa7acda3b..07f574c2de2 100644
--- a/src/corelib/doc/snippets/code/doc_src_properties.cpp
+++ b/src/corelib/doc/snippets/code/doc_src_properties.cpp
@@ -16,6 +16,8 @@ Q_PROPERTY(type name
[BINDABLE bindableProperty]
[CONSTANT]
[FINAL]
+ [VIRTUAL]
+ [OVERRIDE]
[REQUIRED])
//! [0]
diff --git a/src/corelib/doc/src/objectmodel/properties.qdoc b/src/corelib/doc/src/objectmodel/properties.qdoc
index 0e66c8445c2..71e14222763 100644
--- a/src/corelib/doc/src/objectmodel/properties.qdoc
+++ b/src/corelib/doc/src/objectmodel/properties.qdoc
@@ -128,10 +128,18 @@
constant value may be different for different instances of the object. A
constant property cannot have a WRITE method or a NOTIFY signal.
- \li The presence of the \c FINAL attribute indicates that the property
- will not be overridden by a derived class. This can be used for performance
- optimizations in some cases, but is not enforced by moc. Care must be taken
- never to override a \c FINAL property.
+ \li \c FINAL, \c VIRTUAL, \c OVERRIDE modifiers mirror the semantics of their C++ and
+ \l {Override Semantics}{QML counterparts}, allowing to make property overriding explicit at the
+ meta-object level.
+
+ \note At present, these modifiers are not enforced by moc.
+ They are recognized syntactically and are primarily used for QML runtime enforcement and tooling
+ diagnostics. Future versions may introduce stricter compile-time validation and warnings for
+ invalid overrides across modules.
+
+ \note If you want to change accessing behaviour for a property, use the
+ polymorphism provided by C++.
+
\li The presence of the \c REQUIRED attribute indicates that the property
should be set by a user of the class. This is not enforced by moc, and is
diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h
index 48e736ff124..4568d089590 100644
--- a/src/corelib/global/qnumeric.h
+++ b/src/corelib/global/qnumeric.h
@@ -627,6 +627,27 @@ QT_WARNING_DISABLE_FLOAT_COMPARE
QT_WARNING_POP
+namespace QtPrivate {
+/*
+ A version of qFuzzyCompare that works for all values (qFuzzyCompare()
+ requires that neither argument is numerically 0).
+
+ It's private because we need a fix for the many qFuzzyCompare() uses that
+ ignore the precondition, even for older branches.
+
+ See QTBUG-142020 for discussion of a longer-term solution.
+*/
+template <typename T, typename S>
+[[nodiscard]] constexpr bool fuzzyCompare(const T &lhs, const S &rhs) noexcept
+{
+ static_assert(noexcept(qIsNull(lhs) && qIsNull(rhs) && qFuzzyIsNull(lhs - rhs) && qFuzzyCompare(lhs, rhs)),
+ "The operations qIsNull(), qFuzzyIsNull() and qFuzzyCompare() must be noexcept"
+ "for both argument types!");
+ return qIsNull(lhs) || qIsNull(rhs) ? qFuzzyIsNull(lhs - rhs) : qFuzzyCompare(lhs, rhs);
+}
+} // namespace QtPrivate
+
+
inline int qIntCast(double f) { return int(f); }
inline int qIntCast(float f) { return int(f); }
diff --git a/src/corelib/io/qioring.cpp b/src/corelib/io/qioring.cpp
index 28849b49b04..2eb013e24fc 100644
--- a/src/corelib/io/qioring.cpp
+++ b/src/corelib/io/qioring.cpp
@@ -8,6 +8,20 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQIORing, "qt.core.ioring", QtCriticalMsg)
+QIORing *QIORing::sharedInstance()
+{
+ thread_local QIORing instance;
+ if (!instance.initializeIORing())
+ return nullptr;
+ return &instance;
+}
+
+QIORing::QIORing(quint32 submissionQueueSize, quint32 completionQueueSize)
+ : sqEntries(submissionQueueSize), cqEntries(completionQueueSize)
+{
+ // Destructor in respective _<platform>.cpp
+}
+
auto QIORing::queueRequestInternal(GenericRequestType &request) -> QueuedRequestStatus
{
if (!ensureInitialized() || preparingRequests) { // preparingRequests protects against recursing
@@ -65,12 +79,20 @@ template <typename T>
constexpr bool HasResultMember = qxp::is_detected_v<DetectResult, T>;
}
+void QIORing::setFileErrorResult(QIORing::GenericRequestType &req, QFileDevice::FileError error)
+{
+ invokeOnOp(req, [error](auto *concreteRequest) {
+ if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>)
+ setFileErrorResult(*concreteRequest, error);
+ });
+}
+
void QIORing::finishRequestWithError(QIORing::GenericRequestType &req, QFileDevice::FileError error)
{
- invokeOnOp(req, [error](auto *req) {
- if constexpr (QtPrivate::HasResultMember<decltype(*req)>)
- req->result.template emplace<QFileDevice::FileError>(error);
- invokeCallback(*req);
+ invokeOnOp(req, [error](auto *concreteRequest) {
+ if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>)
+ setFileErrorResult(*concreteRequest, error);
+ invokeCallback(*concreteRequest);
});
}
diff --git a/src/corelib/io/qioring_linux.cpp b/src/corelib/io/qioring_linux.cpp
index b296b916c81..2b5865f3c2d 100644
--- a/src/corelib/io/qioring_linux.cpp
+++ b/src/corelib/io/qioring_linux.cpp
@@ -35,19 +35,6 @@ static io_uring_op toUringOp(QIORing::Operation op);
static void prepareFileReadWrite(io_uring_sqe *sqe, const QIORingRequestOffsetFdBase &request,
const void *address, qsizetype size);
-
-QIORing *QIORing::sharedInstance()
-{
- thread_local QIORing instance;
- if (!instance.initializeIORing())
- return nullptr;
- return &instance;
-}
-
-QIORing::QIORing(quint32 submissionQueueSize, quint32 completionQueueSize)
- : sqEntries(submissionQueueSize), cqEntries(completionQueueSize)
-{
-}
QIORing::~QIORing()
{
if (eventDescriptor != -1)
diff --git a/src/corelib/io/qioring_p.h b/src/corelib/io/qioring_p.h
index d4c4308122e..0db832bc6bf 100644
--- a/src/corelib/io/qioring_p.h
+++ b/src/corelib/io/qioring_p.h
@@ -22,7 +22,6 @@
#include <QtCore/qspan.h>
#include <QtCore/qhash.h>
#include <QtCore/qfiledevice.h>
-#include <QtCore/qwineventnotifier.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qdeadlinetimer.h>
@@ -30,10 +29,15 @@
# include <QtCore/qsocketnotifier.h>
struct io_uring_sqe;
struct io_uring_cqe;
+#elif defined(Q_OS_WIN)
+# include <QtCore/qwineventnotifier.h>
+# include <qt_windows.h>
+# include <ioringapi.h>
#endif
#include <algorithm>
#include <filesystem>
+#include <QtCore/qxpfunctional.h>
#include <variant>
#include <optional>
#include <type_traits>
@@ -162,6 +166,12 @@ private:
template <typename Fun>
static auto invokeOnOp(GenericRequestType &req, Fun fn);
+ template <Operation Op>
+ static void setFileErrorResult(QIORingRequest<Op> &req, QFileDevice::FileError error)
+ {
+ req.result.template emplace<QFileDevice::FileError>(error);
+ }
+ static void setFileErrorResult(GenericRequestType &req, QFileDevice::FileError error);
static void finishRequestWithError(GenericRequestType &req, QFileDevice::FileError error);
static bool verifyFd(GenericRequestType &req);
@@ -205,6 +215,28 @@ private:
ReadWriteStatus handleReadCompletion(const io_uring_cqe *cqe, GenericRequestType *request);
template <Operation Op>
ReadWriteStatus handleWriteCompletion(const io_uring_cqe *cqe, GenericRequestType *request);
+#elif defined(Q_OS_WIN)
+ // We use UINT32 because that's the type used for size parameters in their API.
+ static constexpr qsizetype MaxReadWriteLen = std::numeric_limits<UINT32>::max();
+ std::optional<QWinEventNotifier> notifier;
+ HIORING ioRingHandle = nullptr;
+ HANDLE eventHandle = INVALID_HANDLE_VALUE;
+
+ bool initialized = false;
+ bool queueWasFull = false;
+ [[nodiscard]]
+ RequestPrepResult prepareRequest(GenericRequestType &request);
+ QIORing::ReadWriteStatus handleReadCompletion(
+ HRESULT result, quintptr information, QSpan<std::byte> *destinations, void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResult);
+ template <Operation Op>
+ ReadWriteStatus handleReadCompletion(const IORING_CQE *cqe, GenericRequestType *request);
+ ReadWriteStatus handleWriteCompletion(
+ HRESULT result, quintptr information, const QSpan<const std::byte> *sources,
+ void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResult);
+ template <Operation Op>
+ ReadWriteStatus handleWriteCompletion(const IORING_CQE *cqe, GenericRequestType *request);
#endif
};
@@ -243,6 +275,7 @@ struct QIORingRequestBase : Base
template <>
struct QIORingResult<QtPrivate::Operation::Open>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
template <>
@@ -260,6 +293,7 @@ template <>
struct QIORingRequest<QtPrivate::Operation::Close> final
: QIORingRequestBase<QtPrivate::Operation::Close, QIORingRequestEmptyBase>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
@@ -318,6 +352,7 @@ struct QIORingResult<QtPrivate::Operation::Flush> final
template <>
struct QIORingRequest<QtPrivate::Operation::Flush> final : QIORingRequestBase<QtPrivate::Operation::Flush, QIORingRequestEmptyBase>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
@@ -330,6 +365,7 @@ template <>
struct QIORingRequest<QtPrivate::Operation::Stat> final
: QIORingRequestBase<QtPrivate::Operation::Stat, QIORingRequestEmptyBase>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
@@ -473,6 +509,7 @@ namespace QtPrivate {
// The 'extra' struct for Read/Write operations that must be split up
struct ReadWriteExtra
{
+ qint64 totalProcessed = 0;
qsizetype spanIndex = 0;
qsizetype spanOffset = 0;
qsizetype numSpans = 1;
diff --git a/src/corelib/io/qioring_win.cpp b/src/corelib/io/qioring_win.cpp
new file mode 100644
index 00000000000..42c51f428d6
--- /dev/null
+++ b/src/corelib/io/qioring_win.cpp
@@ -0,0 +1,754 @@
+// 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
+// Qt-Security score:significant reason:default
+
+#include "qioring_p.h"
+
+QT_REQUIRE_CONFIG(windows_ioring);
+
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qscopedvaluerollback.h>
+
+#include <qt_windows.h>
+#include <ioringapi.h>
+
+#include <QtCore/q26numeric.h>
+
+QT_BEGIN_NAMESPACE
+
+// We don't really build for 32-bit windows anymore, but this code is definitely wrong if someone
+// does.
+static_assert(sizeof(qsizetype) > sizeof(UINT32),
+ "This code is written with assuming 64-bit Windows.");
+
+using namespace Qt::StringLiterals;
+
+static HRESULT buildReadOperation(HIORING ioRingHandle, qintptr fd, QSpan<std::byte> destination,
+ quint64 offset, quintptr userData)
+{
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef((HANDLE(fd)));
+ const IORING_BUFFER_REF bufferRef(destination.data());
+ const auto maxSize = q26::saturate_cast<UINT32>(destination.size());
+ Q_ASSERT(maxSize == destination.size());
+ return BuildIoRingReadFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, userData,
+ IOSQE_FLAGS_NONE);
+}
+
+static HRESULT buildWriteOperation(HIORING ioRingHandle, qintptr fd, QSpan<const std::byte> source,
+ quint64 offset, quintptr userData)
+{
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef((HANDLE(fd)));
+ const IORING_BUFFER_REF bufferRef(const_cast<std::byte *>(source.data()));
+ const auto maxSize = q26::saturate_cast<UINT32>(source.size());
+ Q_ASSERT(maxSize == source.size());
+ // @todo: FILE_WRITE_FLAGS can be set to write-through, could be used for Unbuffered mode.
+ return BuildIoRingWriteFile(ioRingHandle, fileRef, bufferRef, maxSize, offset,
+ FILE_WRITE_FLAGS_NONE, userData, IOSQE_FLAGS_NONE);
+}
+
+QIORing::~QIORing()
+{
+ if (initialized) {
+ CloseHandle(eventHandle);
+ CloseIoRing(ioRingHandle);
+ }
+}
+
+bool QIORing::initializeIORing()
+{
+ if (initialized)
+ return true;
+
+ IORING_CAPABILITIES capabilities;
+ QueryIoRingCapabilities(&capabilities);
+ if (capabilities.MaxVersion < IORING_VERSION_3) // 3 adds write, flush and drain
+ return false;
+ if ((capabilities.FeatureFlags & IORING_FEATURE_SET_COMPLETION_EVENT) == 0)
+ return false; // We currently require the SET_COMPLETION_EVENT feature
+
+ qCDebug(lcQIORing) << "Creating QIORing, requesting space for" << sqEntries
+ << "submission queue entries, and" << cqEntries
+ << "completion queue entries";
+
+ IORING_CREATE_FLAGS flags;
+ memset(&flags, 0, sizeof(flags));
+ HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, sqEntries, cqEntries, &ioRingHandle);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "failed to initialize QIORing");
+ return false;
+ }
+ auto earlyExitCleanup = qScopeGuard([this]() {
+ if (eventHandle != INVALID_HANDLE_VALUE)
+ CloseHandle(eventHandle);
+ CloseIoRing(ioRingHandle);
+ });
+ eventHandle = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ if (eventHandle == INVALID_HANDLE_VALUE) {
+ qErrnoWarning("Failed to create event handle");
+ return false;
+ }
+ notifier.emplace(eventHandle);
+ hr = SetIoRingCompletionEvent(ioRingHandle, eventHandle);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to assign the event handle to QIORing");
+ return false;
+ }
+ IORING_INFO info;
+ if (SUCCEEDED(GetIoRingInfo(ioRingHandle, &info))) {
+ sqEntries = info.SubmissionQueueSize;
+ cqEntries = info.CompletionQueueSize;
+ qCDebug(lcQIORing) << "QIORing configured with capacity for" << sqEntries
+ << "submissions, and" << cqEntries << "completions.";
+ }
+ QObject::connect(std::addressof(*notifier), &QWinEventNotifier::activated,
+ std::addressof(*notifier), [this]() { completionReady(); });
+ initialized = true;
+ earlyExitCleanup.dismiss();
+ return true;
+}
+
+QIORing::ReadWriteStatus QIORing::handleReadCompletion(
+ HRESULT result, quintptr information, QSpan<std::byte> *destinations, void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResultFn)
+{
+ if (FAILED(result)) {
+ if (result == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
+ return ReadWriteStatus::Finished;
+
+ if (result == E_ABORT)
+ setResultFn(QFileDevice::AbortError);
+ else
+ setResultFn(QFileDevice::ReadError);
+ } else if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) {
+ const qsizetype bytesRead = q26::saturate_cast<decltype(MaxReadWriteLen)>(information);
+ qCDebug(lcQIORing) << "Partial read of" << bytesRead << "bytes completed";
+ extra->totalProcessed = setResultFn(bytesRead);
+ extra->spanOffset += bytesRead;
+ qCDebug(lcQIORing) << "Read operation progress: span" << extra->spanIndex << "offset"
+ << extra->spanOffset << "of" << destinations[extra->spanIndex].size()
+ << "bytes. Total read:" << extra->totalProcessed << "bytes";
+ // The while loop is in case there is an empty span, we skip over it:
+ while (extra->spanOffset == destinations[extra->spanIndex].size()) {
+ // Move to next span
+ if (++extra->spanIndex == extra->numSpans)
+ return ReadWriteStatus::Finished;
+ extra->spanOffset = 0;
+ }
+ return ReadWriteStatus::MoreToDo;
+ } else {
+ setResultFn(q26::saturate_cast<decltype(MaxReadWriteLen)>(information));
+ }
+ return ReadWriteStatus::Finished;
+}
+
+template <QIORing::Operation Op>
+Q_ALWAYS_INLINE QIORing::ReadWriteStatus QIORing::handleReadCompletion(const IORING_CQE *cqe,
+ GenericRequestType *request)
+{
+ static_assert(Op == Operation::Read || Op == Operation::VectoredRead);
+ QIORingRequest<Op> *readRequest = request->requestData<Op>();
+ Q_ASSERT(readRequest);
+ auto *destinations = [&readRequest]() {
+ if constexpr (Op == Operation::Read)
+ return &readRequest->destination;
+ else
+ return &readRequest->destinations[0];
+ }();
+ auto setResult = [readRequest](const std::variant<QFileDevice::FileError, qint64> &result) {
+ if (result.index() == 0) { // error
+ QIORing::setFileErrorResult(*readRequest, *std::get_if<QFileDevice::FileError>(&result));
+ return 0ll;
+ }
+ // else: success
+ auto &readResult = [&readRequest]() -> QIORingResult<Op> & {
+ if (auto *result = std::get_if<QIORingResult<Op>>(&readRequest->result))
+ return *result;
+ return readRequest->result.template emplace<QIORingResult<Op>>();
+ }();
+ qint64 bytesRead = *std::get_if<qint64>(&result);
+ readResult.bytesRead += bytesRead;
+ return readResult.bytesRead;
+ };
+ QIORing::ReadWriteStatus rwstatus = handleReadCompletion(
+ cqe->ResultCode, cqe->Information, destinations, request->getExtra<void>(), setResult);
+ switch (rwstatus) {
+ case ReadWriteStatus::Finished:
+ if (request->getExtra<void>())
+ --ongoingSplitOperations;
+ break;
+ case ReadWriteStatus::MoreToDo: {
+ // Move the request such that it is next in the list to be processed:
+ auto &it = addrItMap[request];
+ const auto where = lastUnqueuedIterator.value_or(pendingRequests.end());
+ pendingRequests.splice(where, pendingRequests, it);
+ it = std::prev(where);
+ lastUnqueuedIterator = it;
+ break;
+ }
+ }
+ return rwstatus;
+}
+
+QIORing::ReadWriteStatus QIORing::handleWriteCompletion(
+ HRESULT result, quintptr information, const QSpan<const std::byte> *sources, void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResultFn)
+{
+ if (FAILED(result)) {
+ if (result == E_ABORT)
+ setResultFn(QFileDevice::AbortError);
+ else
+ setResultFn(QFileDevice::WriteError);
+ } else if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) {
+ const qsizetype bytesWritten = q26::saturate_cast<decltype(MaxReadWriteLen)>(information);
+ qCDebug(lcQIORing) << "Partial write of" << bytesWritten << "bytes completed";
+ extra->totalProcessed = setResultFn(bytesWritten);
+ extra->spanOffset += bytesWritten;
+ qCDebug(lcQIORing) << "Write operation progress: span" << extra->spanIndex << "offset"
+ << extra->spanOffset << "of" << sources[extra->spanIndex].size()
+ << "bytes. Total written:" << extra->totalProcessed << "bytes";
+ // The while loop is in case there is an empty span, we skip over it:
+ while (extra->spanOffset == sources[extra->spanIndex].size()) {
+ // Move to next span
+ if (++extra->spanIndex == extra->numSpans)
+ return ReadWriteStatus::Finished;
+ extra->spanOffset = 0;
+ }
+ return ReadWriteStatus::MoreToDo;
+ } else {
+ setResultFn(q26::saturate_cast<decltype(MaxReadWriteLen)>(information));
+ }
+ return ReadWriteStatus::Finished;
+}
+
+template <QIORing::Operation Op>
+Q_ALWAYS_INLINE QIORing::ReadWriteStatus QIORing::handleWriteCompletion(const IORING_CQE *cqe,
+ GenericRequestType *request)
+{
+ static_assert(Op == Operation::Write || Op == Operation::VectoredWrite);
+ QIORingRequest<Op> *writeRequest = request->requestData<Op>();
+ auto *sources = [&writeRequest]() {
+ if constexpr (Op == Operation::Write)
+ return &writeRequest->source;
+ else
+ return &writeRequest->sources[0];
+ }();
+ auto setResult = [writeRequest](const std::variant<QFileDevice::FileError, qint64> &result) {
+ if (result.index() == 0) { // error
+ QIORing::setFileErrorResult(*writeRequest, *std::get_if<QFileDevice::FileError>(&result));
+ return 0ll;
+ }
+ // else: success
+ auto &writeResult = [&writeRequest]() -> QIORingResult<Op> & {
+ if (auto *result = std::get_if<QIORingResult<Op>>(&writeRequest->result))
+ return *result;
+ return writeRequest->result.template emplace<QIORingResult<Op>>();
+ }();
+ qint64 bytesWritten = *std::get_if<qint64>(&result);
+ writeResult.bytesWritten += bytesWritten;
+ return writeResult.bytesWritten;
+ };
+ QIORing::ReadWriteStatus rwstatus = handleWriteCompletion(
+ cqe->ResultCode, cqe->Information, sources, request->getExtra<void>(), setResult);
+ switch (rwstatus) {
+ case ReadWriteStatus::Finished:
+ if (request->getExtra<void>())
+ --ongoingSplitOperations;
+ break;
+ case ReadWriteStatus::MoreToDo: {
+ // Move the request such that it is next in the list to be processed:
+ auto &it = addrItMap[request];
+ const auto where = lastUnqueuedIterator.value_or(pendingRequests.end());
+ pendingRequests.splice(where, pendingRequests, it);
+ it = std::prev(where);
+ lastUnqueuedIterator = it;
+ break;
+ }
+ }
+ return rwstatus;
+}
+
+void QIORing::completionReady()
+{
+ ResetEvent(eventHandle);
+ IORING_CQE entry;
+ while (PopIoRingCompletion(ioRingHandle, &entry) == S_OK) {
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ auto *request = reinterpret_cast<GenericRequestType *>(entry.UserData);
+ if (!addrItMap.contains(request)) {
+ qCDebug(lcQIORing) << "Got completed entry, but cannot find it in the map. Likely "
+ "deleted, ignoring. UserData pointer:"
+ << request;
+ continue;
+ }
+ qCDebug(lcQIORing) << "Got completed entry. Operation:" << request->operation()
+ << "- UserData pointer:" << request
+ << "- Result:" << qt_error_string(entry.ResultCode) << '('
+ << QByteArray("0x"_ba + QByteArray::number(entry.ResultCode, 16)).data()
+ << ')';
+ switch (request->operation()) {
+ case Operation::Open: // Synchronously finishes
+ Q_UNREACHABLE_RETURN();
+ case Operation::Close: {
+ auto closeRequest = request->takeRequestData<Operation::Close>();
+ // We ignore the result of the flush, we are closing the handle anyway.
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ if (CloseHandle(HANDLE(closeRequest.fd)))
+ closeRequest.result.emplace<QIORingResult<Operation::Close>>();
+ else
+ closeRequest.result.emplace<QFileDevice::FileError>(QFileDevice::OpenError);
+ invokeCallback(closeRequest);
+ break;
+ }
+ case Operation::Read: {
+ const ReadWriteStatus status = handleReadCompletion<Operation::Read>(&entry, request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto readRequest = request->takeRequestData<Operation::Read>();
+ invokeCallback(readRequest);
+ break;
+ }
+ case Operation::Write: {
+ const ReadWriteStatus status = handleWriteCompletion<Operation::Write>(&entry, request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto writeRequest = request->takeRequestData<Operation::Write>();
+ invokeCallback(writeRequest);
+ break;
+ }
+ case Operation::VectoredRead: {
+ const ReadWriteStatus status = handleReadCompletion<Operation::VectoredRead>(&entry,
+ request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto vectoredReadRequest = request->takeRequestData<Operation::VectoredRead>();
+ invokeCallback(vectoredReadRequest);
+ break;
+ }
+ case Operation::VectoredWrite: {
+ const ReadWriteStatus status = handleWriteCompletion<Operation::VectoredWrite>(&entry,
+ request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto vectoredWriteRequest = request->takeRequestData<Operation::VectoredWrite>();
+ invokeCallback(vectoredWriteRequest);
+ break;
+ }
+ case Operation::Flush: {
+ auto flushRequest = request->takeRequestData<Operation::Flush>();
+ if (FAILED(entry.ResultCode)) {
+ qErrnoWarning(entry.ResultCode, "Flush operation failed");
+ // @todo any FlushError?
+ flushRequest.result.emplace<QFileDevice::FileError>(
+ QFileDevice::FileError::WriteError);
+ } else {
+ flushRequest.result.emplace<QIORingResult<Operation::Flush>>();
+ }
+ invokeCallback(flushRequest);
+ break;
+ }
+ case QtPrivate::Operation::Cancel: {
+ auto cancelRequest = request->takeRequestData<Operation::Cancel>();
+ invokeCallback(cancelRequest);
+ break;
+ }
+ case QtPrivate::Operation::Stat:
+ Q_UNREACHABLE_RETURN(); // Completes synchronously
+ break;
+ case Operation::NumOperations:
+ Q_UNREACHABLE_RETURN();
+ break;
+ }
+ auto it = addrItMap.take(request);
+ pendingRequests.erase(it);
+ --inFlightRequests;
+ queueWasFull = false;
+ }
+ prepareRequests();
+ if (unstagedRequests > 0)
+ submitRequests();
+}
+
+bool QIORing::waitForCompletions(QDeadlineTimer deadline)
+{
+ notifier->setEnabled(false);
+ auto reactivateNotifier = qScopeGuard([this]() {
+ notifier->setEnabled(true);
+ });
+
+ while (!deadline.hasExpired()) {
+ DWORD timeout = 0;
+ if (deadline.isForever()) {
+ timeout = INFINITE;
+ } else {
+ timeout = q26::saturate_cast<DWORD>(deadline.remainingTime());
+ if (timeout == INFINITE)
+ --timeout;
+ }
+ if (WaitForSingleObject(eventHandle, timeout) == WAIT_OBJECT_0)
+ return true;
+ }
+ return false;
+}
+
+static HANDLE openFile(const QIORingRequest<QIORing::Operation::Open> &openRequest)
+{
+ DWORD access = 0;
+ if (openRequest.flags.testFlag(QIODevice::ReadOnly))
+ access |= GENERIC_READ;
+ if (openRequest.flags.testFlag(QIODevice::WriteOnly))
+ access |= GENERIC_WRITE;
+
+ DWORD disposition = 0;
+ if (openRequest.flags.testFlag(QIODevice::Append)) {
+ qCWarning(lcQIORing, "Opening file with Append not supported for random access file");
+ return INVALID_HANDLE_VALUE;
+ }
+ if (openRequest.flags.testFlag(QIODevice::NewOnly)) {
+ disposition = CREATE_NEW;
+ } else {
+ // If Write is specified we _may_ create a file.
+ // See qfsfileengine_p.h openModeCanCreate.
+ disposition = openRequest.flags.testFlag(QIODeviceBase::WriteOnly)
+ && !openRequest.flags.testFlags(QIODeviceBase::ExistingOnly)
+ ? OPEN_ALWAYS
+ : OPEN_EXISTING;
+ }
+ const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ const DWORD flagsAndAttribs = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
+ HANDLE h = CreateFile(openRequest.path.native().c_str(), access, shareMode, nullptr,
+ disposition, flagsAndAttribs, nullptr);
+ if (h != INVALID_HANDLE_VALUE && openRequest.flags.testFlag(QIODeviceBase::Truncate)) {
+ FILE_END_OF_FILE_INFO info;
+ memset(&info, 0, sizeof(info));
+ SetFileInformationByHandle(h, FileEndOfFileInfo, &info, sizeof(info));
+ }
+ return h;
+}
+
+bool QIORing::supportsOperation(Operation op)
+{
+ switch (op) {
+ case QtPrivate::Operation::Open:
+ case QtPrivate::Operation::Close:
+ case QtPrivate::Operation::Read:
+ case QtPrivate::Operation::Write:
+ case QtPrivate::Operation::Flush:
+ case QtPrivate::Operation::Cancel:
+ case QtPrivate::Operation::Stat:
+ case QtPrivate::Operation::VectoredRead:
+ case QtPrivate::Operation::VectoredWrite:
+ return true;
+ case QtPrivate::Operation::NumOperations:
+ return false;
+ }
+ return false; // Not unreachable, we could allow more for io_uring
+}
+
+void QIORing::submitRequests()
+{
+ stagePending = false;
+ if (unstagedRequests == 0)
+ return;
+
+ // We perform a miniscule wait - to see if anything already in the queue is already completed -
+ // if we have been told the queue is full. Then we can try queuing more things right away
+ const bool shouldTryWait = std::exchange(queueWasFull, false);
+ const auto submitToRing = [this, &shouldTryWait] {
+ quint32 submittedEntries = 0;
+ HRESULT hr = SubmitIoRing(ioRingHandle, shouldTryWait ? 1 : 0, 1, &submittedEntries);
+ qCDebug(lcQIORing) << "Submitted" << submittedEntries << "requests";
+ unstagedRequests -= submittedEntries;
+ if (FAILED(hr)) {
+ // Too noisy, not a real problem
+ // qErrnoWarning(hr, "Failed to submit QIORing request: %u", submittedEntries);
+ return false;
+ }
+ return submittedEntries > 0;
+ };
+ if (submitToRing() && shouldTryWait) {
+ // We try to prepare some more request and submit more if able
+ prepareRequests();
+ if (unstagedRequests > 0)
+ submitToRing();
+ }
+}
+
+void QIORing::prepareRequests()
+{
+ if (!lastUnqueuedIterator)
+ return;
+ Q_ASSERT(!preparingRequests);
+ QScopedValueRollback<bool> prepareGuard(preparingRequests, true);
+
+ auto it = *lastUnqueuedIterator;
+ lastUnqueuedIterator.reset();
+ const auto end = pendingRequests.end();
+ while (!queueWasFull && it != end) {
+ auto &request = *it;
+ switch (prepareRequest(request)) {
+ case RequestPrepResult::Ok:
+ ++unstagedRequests;
+ ++inFlightRequests;
+ break;
+ case RequestPrepResult::QueueFull:
+ qCDebug(lcQIORing) << "Queue was reported as full, in flight requests:"
+ << inFlightRequests << "submission queue size:" << sqEntries
+ << "completion queue size:" << cqEntries;
+ queueWasFull = true;
+ lastUnqueuedIterator = it;
+ return;
+ case RequestPrepResult::Defer:
+ qCDebug(lcQIORing) << "Request for" << request.operation()
+ << "had to be deferred, will not queue any more requests at the "
+ "moment.";
+ lastUnqueuedIterator = it;
+ return; //
+ case RequestPrepResult::RequestCompleted:
+ // Used for requests that immediately finish. So we erase it:
+ qCDebug(lcQIORing) << "Request for" << request.operation()
+ << "completed synchronously.";
+ addrItMap.remove(&request);
+ it = pendingRequests.erase(it);
+ continue; // Don't increment iterator again
+ }
+ ++it;
+ }
+}
+
+namespace QtPrivate {
+template <typename T>
+using DetectHasFd = decltype(std::declval<const T &>().fd);
+
+template <typename T>
+constexpr bool OperationHasFd_v = qxp::is_detected_v<DetectHasFd, T>;
+} // namespace QtPrivate
+
+auto QIORing::prepareRequest(GenericRequestType &request) -> RequestPrepResult
+{
+ qCDebug(lcQIORing) << "Preparing a request with operation" << request.operation();
+ HRESULT hr = -1;
+
+ if (!verifyFd(request)) {
+ finishRequestWithError(request, QFileDevice::OpenError);
+ return RequestPrepResult::RequestCompleted;
+ }
+
+ switch (request.operation()) {
+ case Operation::Open: {
+ QIORingRequest<Operation::Open> openRequest = request.takeRequestData<Operation::Open>();
+ HANDLE fileDescriptor = openFile(openRequest);
+ if (fileDescriptor == INVALID_HANDLE_VALUE) {
+ openRequest.result.emplace<QFileDevice::FileError>(QFileDevice::FileError::OpenError);
+ } else {
+ auto &result = openRequest.result.emplace<QIORingResult<Operation::Open>>();
+ result.fd = qintptr(fileDescriptor);
+ }
+ invokeCallback(openRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ case Operation::Close: {
+ if (ongoingSplitOperations > 0)
+ return RequestPrepResult::Defer;
+
+ // We need to wait until all previous OPS are done before we close the request.
+ // There is no no-op request in the Windows QIORing, so we issue a flush.
+ auto *closeRequest = request.requestData<Operation::Close>();
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef(HANDLE(closeRequest->fd));
+ hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_MIN_METADATA,
+ quintptr(std::addressof(request)),
+ IOSQE_FLAGS_DRAIN_PRECEDING_OPS);
+ break;
+ }
+ case Operation::Read: {
+ auto *readRequest = request.requestData<Operation::Read>();
+ auto span = readRequest->destination;
+ auto offset = readRequest->offset;
+ if (span.size() > MaxReadWriteLen) {
+ qCDebug(lcQIORing) << "Requested Read of size" << span.size() << "has to be split";
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0)
+ ++ongoingSplitOperations;
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildReadOperation(ioRingHandle, readRequest->fd, span, offset,
+ quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::VectoredRead: {
+ auto *vectoredReadRequest = request.requestData<Operation::VectoredRead>();
+ auto span = vectoredReadRequest->destinations.front();
+ auto offset = vectoredReadRequest->offset;
+ if (Q_LIKELY(vectoredReadRequest->destinations.size() > 1
+ || span.size() > MaxReadWriteLen)) {
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0 && extra->spanIndex == 0)
+ ++ongoingSplitOperations;
+ extra->numSpans = vectoredReadRequest->destinations.size();
+
+ span = vectoredReadRequest->destinations[extra->spanIndex];
+
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildReadOperation(ioRingHandle, vectoredReadRequest->fd, span,
+ offset, quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::Write: {
+ auto *writeRequest = request.requestData<Operation::Write>();
+ auto span = writeRequest->source;
+ auto offset = writeRequest->offset;
+ if (span.size() > MaxReadWriteLen) {
+ qCDebug(lcQIORing) << "Requested Write of size" << span.size() << "has to be split";
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0)
+ ++ongoingSplitOperations;
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildWriteOperation(ioRingHandle, writeRequest->fd, span, offset,
+ quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::VectoredWrite: {
+ auto *vectoredWriteRequest = request.requestData<Operation::VectoredWrite>();
+ auto span = vectoredWriteRequest->sources.front();
+ auto offset = vectoredWriteRequest->offset;
+ if (Q_LIKELY(vectoredWriteRequest->sources.size() > 1
+ || span.size() > MaxReadWriteLen)) {
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0 && extra->spanIndex == 0)
+ ++ongoingSplitOperations;
+ extra->numSpans = vectoredWriteRequest->sources.size();
+
+ span = vectoredWriteRequest->sources[extra->spanIndex];
+
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildWriteOperation(ioRingHandle, vectoredWriteRequest->fd, span,
+ offset, quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::Flush: {
+ if (ongoingSplitOperations > 0)
+ return RequestPrepResult::Defer;
+ auto *flushRequest = request.requestData<Operation::Flush>();
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef(HANDLE(flushRequest->fd));
+ hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_DEFAULT,
+ quintptr(std::addressof(request)),
+ IOSQE_FLAGS_DRAIN_PRECEDING_OPS);
+ break;
+ }
+ case QtPrivate::Operation::Stat: {
+ auto statRequest = request.takeRequestData<Operation::Stat>();
+ FILE_STANDARD_INFO info;
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ if (!GetFileInformationByHandleEx(HANDLE(statRequest.fd), FileStandardInfo, &info,
+ sizeof(info))) {
+ DWORD winErr = GetLastError();
+ QFileDevice::FileError error = QFileDevice::UnspecifiedError;
+ if (winErr == ERROR_FILE_NOT_FOUND || winErr == ERROR_INVALID_HANDLE)
+ error = QFileDevice::OpenError;
+ else if (winErr == ERROR_ACCESS_DENIED)
+ error = QFileDevice::PermissionsError;
+ statRequest.result.emplace<QFileDevice::FileError>(error);
+ } else {
+ auto &result = statRequest.result.emplace<QIORingResult<Operation::Stat>>();
+ result.size = info.EndOfFile.QuadPart;
+ }
+ invokeCallback(statRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ case Operation::Cancel: {
+ auto *cancelRequest = request.requestData<Operation::Cancel>();
+ auto *otherOperation = reinterpret_cast<GenericRequestType *>(cancelRequest->handle);
+ if (!otherOperation || !addrItMap.contains(otherOperation)) {
+ qCDebug(lcQIORing, "Invalid cancel for non-existant operation");
+ invokeCallback(*cancelRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ qCDebug(lcQIORing) << "Cancelling operation of type" << otherOperation->operation()
+ << "which was"
+ << (otherOperation->wasQueued() ? "queued" : "not queued");
+ Q_ASSERT(&request != otherOperation);
+ if (!otherOperation->wasQueued()) {
+ // The request hasn't been queued yet, so we can just drop it from
+ // the pending requests and call the callback.
+ auto it = addrItMap.take(otherOperation);
+ finishRequestWithError(*otherOperation, QFileDevice::AbortError);
+ pendingRequests.erase(it); // otherOperation is deleted
+ invokeCallback(*cancelRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ qintptr fd = -1;
+ invokeOnOp(*otherOperation, [&fd](auto *request) {
+ if constexpr (QtPrivate::OperationHasFd_v<decltype(*request)>)
+ fd = request->fd;
+ });
+ if (fd == -1) {
+ qCDebug(lcQIORing, "Invalid cancel for non-existant fd");
+ invokeCallback(*cancelRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef((HANDLE(fd)));
+ hr = BuildIoRingCancelRequest(ioRingHandle, fileRef, quintptr(otherOperation),
+ quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::NumOperations:
+ Q_UNREACHABLE_RETURN(RequestPrepResult::RequestCompleted);
+ break;
+ }
+ if (hr == IORING_E_SUBMISSION_QUEUE_FULL)
+ return RequestPrepResult::QueueFull;
+ if (FAILED(hr)) {
+ finishRequestWithError(request, QFileDevice::UnspecifiedError);
+ return RequestPrepResult::RequestCompleted;
+ }
+ request.setQueued(true);
+ return RequestPrepResult::Ok;
+}
+
+bool QIORing::verifyFd(GenericRequestType &req)
+{
+ bool result = true;
+ invokeOnOp(req, [&](auto *request) {
+ if constexpr (QtPrivate::OperationHasFd_v<decltype(*request)>) {
+ result = quintptr(request->fd) > 0 && quintptr(request->fd) != quintptr(INVALID_HANDLE_VALUE);
+ }
+ });
+ return result;
+}
+
+void QIORing::GenericRequestType::cleanupExtra(Operation op, void *extra)
+{
+ switch (op) {
+ case QtPrivate::Operation::Read:
+ case QtPrivate::Operation::VectoredRead:
+ case QtPrivate::Operation::Write:
+ case QtPrivate::Operation::VectoredWrite:
+ delete static_cast<QtPrivate::ReadWriteExtra *>(extra);
+ break;
+ case QtPrivate::Operation::Open:
+ case QtPrivate::Operation::Close:
+ case QtPrivate::Operation::Flush:
+ case QtPrivate::Operation::Stat:
+ case QtPrivate::Operation::Cancel:
+ case QtPrivate::Operation::NumOperations:
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/itemmodels/qrangemodel.cpp b/src/corelib/itemmodels/qrangemodel.cpp
index d72722f063d..f37812876ea 100644
--- a/src/corelib/itemmodels/qrangemodel.cpp
+++ b/src/corelib/itemmodels/qrangemodel.cpp
@@ -1385,6 +1385,7 @@ void QRangeModel::resetRoleNames()
/*!
\property QRangeModel::autoConnectPolicy
+ \since 6.11
\brief if and when the model auto-connects to property changed notifications.
If QRangeModel operates on a data structure that holds the same type of
diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h
index 3e35ad3981c..f6b08099fe7 100644
--- a/src/corelib/itemmodels/qrangemodel_impl.h
+++ b/src/corelib/itemmodels/qrangemodel_impl.h
@@ -23,6 +23,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qscopedvaluerollback.h>
#include <QtCore/qset.h>
+#include <QtCore/qvarlengtharray.h>
#include <algorithm>
#include <functional>
@@ -1203,76 +1204,48 @@ public:
return std::move(result.data());
}
+ static constexpr bool isRangeModelRole(int role)
+ {
+ return role == Qt::RangeModelDataRole
+ || role == Qt::RangeModelAdapterRole;
+ }
+
+ static constexpr bool isPrimaryRole(int role)
+ {
+ return role == Qt::DisplayRole || role == Qt::EditRole;
+ }
+
QMap<int, QVariant> itemData(const QModelIndex &index) const
{
QMap<int, QVariant> result;
- bool tried = false;
- const auto readItemData = [this, &index, &result, &tried](const auto &value){
- Q_UNUSED(this);
- Q_UNUSED(index);
- using value_type = q20::remove_cvref_t<decltype(value)>;
- using multi_role = QRangeModelDetails::is_multi_role<value_type>;
- using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
- if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
- using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
- tried = true;
- const auto roles = this->itemModel().roleNames().keys();
- for (auto &role : roles) {
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
- continue;
- QVariant data = ItemAccess::readRole(value, role);
- if (data.isValid())
- result[role] = std::move(data);
- }
- } else if constexpr (multi_role()) {
- tried = true;
- if constexpr (std::is_convertible_v<value_type, decltype(result)>) {
+ if (index.isValid()) {
+ bool tried = false;
+
+ // optimisation for items backed by a QMap<int, QVariant> or equivalent
+ readAt(index, [&result, &tried](const auto &value) {
+ if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) {
+ tried = true;
result = value;
- } else {
- const auto roleNames = [this]() -> QHash<int, QByteArray> {
- Q_UNUSED(this);
- if constexpr (!multi_role::int_key)
- return this->itemModel().roleNames();
- else
- return {};
- }();
- for (auto it = std::begin(value); it != std::end(value); ++it) {
- const int role = [&roleNames, key = QRangeModelDetails::key(it)]() {
- Q_UNUSED(roleNames);
- if constexpr (multi_role::int_key)
- return int(key);
- else
- return roleNames.key(key.toUtf8(), -1);
- }();
-
- if (role != -1 && role != Qt::RangeModelDataRole && role != Qt::RangeModelAdapterRole)
- result.insert(role, QRangeModelDetails::value(it));
- }
}
- } else if constexpr (has_metaobject<value_type>) {
- if (row_traits::fixed_size() <= 1) {
- tried = true;
- const auto roleNames = this->itemModel().roleNames();
- const auto end = roleNames.keyEnd();
- for (auto it = roleNames.keyBegin(); it != end; ++it) {
- const int role = *it;
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
- continue;
- QVariant data = readRole(index, role, QRangeModelDetails::pointerTo(value));
- if (data.isValid())
- result[role] = std::move(data);
- }
+ });
+ if (!tried) {
+ const auto roles = this->itemModel().roleNames().keys();
+ QVarLengthArray<QModelRoleData, 16> roleDataArray;
+ roleDataArray.reserve(roles.size());
+ for (auto role : roles) {
+ if (isRangeModelRole(role))
+ continue;
+ roleDataArray.emplace_back(role);
}
- }
- };
-
- if (index.isValid()) {
- readAt(index, readItemData);
+ QModelRoleDataSpan roleDataSpan(roleDataArray);
+ multiData(index, roleDataSpan);
- if (!tried) { // no multi-role item found
- result = this->itemModel().QAbstractItemModel::itemData(index);
- result.remove(Qt::RangeModelAdapterRole);
+ for (auto &&roleData : std::move(roleDataSpan)) {
+ QVariant data = roleData.data();
+ if (data.isValid())
+ result[roleData.role()] = std::move(data);
+ }
}
}
return result;
@@ -1288,27 +1261,34 @@ public:
using multi_role = QRangeModelDetails::is_multi_role<value_type>;
using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
+ const auto readModelData = [&value](QModelRoleData &roleData){
+ const int role = roleData.role();
+ if (role == Qt::RangeModelDataRole) {
+ // Qt QML support: "modelData" role returns the entire multi-role item.
+ // QML can only use raw pointers to QObject (so we unwrap), and gadgets
+ // only by value (so we take the reference).
+ if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
+ roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
+ else
+ roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
+ } else if (role == Qt::RangeModelAdapterRole) {
+ // for QRangeModelAdapter however, we want to respect smart pointer wrappers
+ if constexpr (std::is_copy_assignable_v<value_type>)
+ roleData.setData(QVariant::fromValue(value));
+ else
+ roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
+ } else {
+ return false;
+ }
+ return true;
+ };
+
if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
tried = true;
for (auto &roleData : roleDataSpan) {
- if (roleData.role() == Qt::RangeModelDataRole) {
- // Qt QML support: "modelData" role returns the entire multi-role item.
- // QML can only use raw pointers to QObject (so we unwrap), and gadgets
- // only by value (so we take the reference).
- if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
- roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else if (roleData.role() == Qt::RangeModelAdapterRole) {
- // for QRangeModelAdapter however, we want to respect smart pointer wrappers
- if constexpr (std::is_copy_assignable_v<value_type>)
- roleData.setData(QVariant::fromValue(value));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else {
+ if (!readModelData(roleData))
roleData.setData(ItemAccess::readRole(value, roleData.role()));
- }
}
} else if constexpr (multi_role()) {
tried = true;
@@ -1337,21 +1317,7 @@ public:
if (row_traits::fixed_size() <= 1) {
tried = true;
for (auto &roleData : roleDataSpan) {
- if (roleData.role() == Qt::RangeModelDataRole) {
- // Qt QML support: "modelData" role returns the entire multi-role item.
- // QML can only use raw pointers to QObject (so we unwrap), and gadgets
- // only by value (so we take the reference).
- if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
- roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else if (roleData.role() == Qt::RangeModelAdapterRole) {
- // for QRangeModelAdapter however, we want to respect smart pointer wrappers
- if constexpr (std::is_copy_assignable_v<value_type>)
- roleData.setData(QVariant::fromValue(value));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else {
+ if (!readModelData(roleData)) {
roleData.setData(readRole(index, roleData.role(),
QRangeModelDetails::pointerTo(value)));
}
@@ -1360,7 +1326,7 @@ public:
tried = true;
for (auto &roleData : roleDataSpan) {
const int role = roleData.role();
- if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ if (isPrimaryRole(role)) {
roleData.setData(readProperty(index.column(),
QRangeModelDetails::pointerTo(value)));
} else {
@@ -1372,12 +1338,10 @@ public:
tried = true;
for (auto &roleData : roleDataSpan) {
const int role = roleData.role();
- if (role == Qt::DisplayRole || role == Qt::EditRole
- || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) {
+ if (isPrimaryRole(role) || isRangeModelRole(role))
roleData.setData(read(value));
- } else {
+ else
roleData.clearData();
- }
}
}
});
@@ -1395,7 +1359,8 @@ public:
auto emitDataChanged = qScopeGuard([&success, this, &index, role]{
if (success) {
Q_EMIT this->dataChanged(index, index,
- role == Qt::EditRole || role == Qt::RangeModelDataRole || role == Qt::RangeModelDataRole
+ role == Qt::EditRole || role == Qt::RangeModelDataRole
+ || role == Qt::RangeModelAdapterRole
? QList<int>{} : QList<int>{role});
}
});
@@ -1408,25 +1373,34 @@ public:
using multi_role = QRangeModelDetails::is_multi_role<value_type>;
auto setRangeModelDataRole = [&target, &data]{
- auto &targetRef = QRangeModelDetails::refTo(target);
constexpr auto targetMetaType = QMetaType::fromType<value_type>();
const auto dataMetaType = data.metaType();
constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
// we don't support replacing objects that are stored as raw pointers,
// as this makes object ownership very messy. But we can replace objects
- // stored in smart pointers.
- if constexpr (isWrapped && !std::is_pointer_v<value_type>
- && std::is_copy_assignable_v<value_type>) {
- if (data.canConvert(targetMetaType)) {
- target = data.value<value_type>();
- return true;
+ // stored in smart pointers, and we can initialize raw nullptr objects.
+ if constexpr (isWrapped) {
+ constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
+ if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
+ if (data.canConvert(targetMetaType)) {
+ target = data.value<value_type>();
+ return true;
+ }
+ } else if constexpr (is_raw_pointer) {
+ if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
+ target = data.value<value_type>();
+ return true;
+ }
+ } else {
+ Q_UNUSED(target);
}
}
// Otherwise we have a move-only or polymorph type. fall through to
// error handling.
} else if constexpr (isWrapped) {
if (QRangeModelDetails::isValid(target)) {
+ auto &targetRef = QRangeModelDetails::refTo(target);
// we need to get a wrapped value type out of the QVariant, which
// might carry a pointer. We have to try all alternatives.
if (const auto mt = QMetaType::fromType<wrapped_value_type>();
@@ -1440,10 +1414,10 @@ public:
}
}
} else if (targetMetaType == dataMetaType) {
- targetRef = data.value<value_type>();
+ QRangeModelDetails::refTo(target) = data.value<value_type>();
return true;
} else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
- targetRef = *data.value<value_type *>();
+ QRangeModelDetails::refTo(target) = *data.value<value_type *>();
return true;
}
#ifndef QT_NO_DEBUG
@@ -1455,17 +1429,16 @@ public:
if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
+ if (isRangeModelRole(role))
return setRangeModelDataRole();
return ItemAccess::writeRole(target, data, role);
} if constexpr (has_metaobject<value_type>) {
if (row_traits::fixed_size() <= 1) { // multi-role value
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
+ if (isRangeModelRole(role))
return setRangeModelDataRole();
return writeRole(role, QRangeModelDetails::pointerTo(target), data);
} else if (column <= row_traits::fixed_size() // multi-column
- && (role == Qt::DisplayRole || role == Qt::EditRole
- || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)) {
+ && (isPrimaryRole(role) || isRangeModelRole(role))) {
return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
}
} else if constexpr (multi_role::value) {
@@ -1492,14 +1465,20 @@ public:
return write(target[roleToSet], data);
else
return write(target[roleNames.value(roleToSet)], data);
- } else if (role == Qt::DisplayRole || role == Qt::EditRole
- || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) {
+ } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
return write(target, data);
}
return false;
};
success = writeAt(index, writeData);
+
+ if constexpr (itemsAreQObjects) {
+ if (success && isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
+ if (QObject *item = data.value<QObject *>())
+ Self::connectProperties(index, item, m_data.context, m_data.properties);
+ }
+ }
}
return success;
}
@@ -1607,7 +1586,7 @@ public:
tried = true;
auto targetCopy = makeCopy(target);
for (auto &&[role, value] : data.asKeyValueRange()) {
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
+ if (isRangeModelRole(role))
continue;
if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
const QByteArray roleName = roleNames.value(role);
diff --git a/src/corelib/itemmodels/qrangemodeladapter.qdoc b/src/corelib/itemmodels/qrangemodeladapter.qdoc
index 5ab128a8c5f..263bff0dd0c 100644
--- a/src/corelib/itemmodels/qrangemodeladapter.qdoc
+++ b/src/corelib/itemmodels/qrangemodeladapter.qdoc
@@ -118,11 +118,9 @@
would bypass the QAbstractItemModel notification protocol, those reference
objects prevent direct modifications of the items.
- \note Calling mutable overloads generates some overhead. Make a const copy
- of the adapter (which will not copy the data), or use \c{std::as_const} to
- make sure that only the const overloads are used in performance critical,
- read-heavy code. This is the same technique as when accessing elements in
- an implicitly shared Qt container.
+ \note Accessing the reference object always makes a call to the model to get
+ a copy of the value. This can be expensive; for performance critical access
+ to data, store a copy.
\section3 Item access
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index a5d34eac707..24cc58829c8 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -4405,6 +4405,34 @@ bool QMetaProperty::isFinal() const
}
/*!
+ \since 6.11
+ Returns \c true if the property is virtual; otherwise returns \c false.
+
+ A property is virtual if the \c{Q_PROPERTY()}'s \c VIRTUAL attribute
+ is set.
+*/
+bool QMetaProperty::isVirtual() const
+{
+ if (!mobj)
+ return false;
+ return data.flags() & Virtual;
+}
+
+/*!
+ \since 6.11
+ Returns \c true if the property does override; otherwise returns \c false.
+
+ A property does override if the \c{Q_PROPERTY()}'s \c OVERRIDE attribute
+ is set.
+*/
+bool QMetaProperty::isOverride() const
+{
+ if (!mobj)
+ return false;
+ return data.flags() & Override;
+}
+
+/*!
\since 5.15
Returns \c true if the property is required; otherwise returns \c false.
diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h
index 0f793ca753b..ff3cc751c3a 100644
--- a/src/corelib/kernel/qmetaobject.h
+++ b/src/corelib/kernel/qmetaobject.h
@@ -365,6 +365,8 @@ public:
bool isUser() const;
bool isConstant() const;
bool isFinal() const;
+ bool isVirtual() const;
+ bool isOverride() const;
bool isRequired() const;
bool isBindable() const;
diff --git a/src/corelib/kernel/qtmocconstants.h b/src/corelib/kernel/qtmocconstants.h
index 79c0138bb28..260ac2fa5f8 100644
--- a/src/corelib/kernel/qtmocconstants.h
+++ b/src/corelib/kernel/qtmocconstants.h
@@ -39,7 +39,8 @@ enum PropertyFlags : uint {
Resettable = 0x00000004,
EnumOrFlag = 0x00000008,
Alias = 0x00000010,
- // Reserved for future usage = 0x00000020,
+ Virtual = 0x00000020,
+ Override = 0x00000040,
StdCppSet = 0x00000100,
Constant = 0x00000400,
Final = 0x00000800,
diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp
index de7043e8c1d..9c26de94b6d 100644
--- a/src/corelib/mimetypes/qmimeprovider.cpp
+++ b/src/corelib/mimetypes/qmimeprovider.cpp
@@ -512,8 +512,8 @@ QMimeBinaryProvider::MimeTypeExtraMap::const_iterator
QMimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName)
{
#if QT_CONFIG(xmlstreamreader)
- auto it = m_mimetypeExtra.find(mimeName);
- if (it == m_mimetypeExtra.cend()) {
+ auto [it, insertionOccurred] = m_mimetypeExtra.try_emplace(mimeName);
+ if (insertionOccurred) {
// load comment and globPatterns
// shared-mime-info since 1.3 lowercases the xml files
@@ -523,9 +523,8 @@ QMimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName)
QFile qfile(mimeFile);
if (!qfile.open(QFile::ReadOnly))
- return m_mimetypeExtra.cend();
+ return it;
- it = m_mimetypeExtra.try_emplace(mimeName).first;
MimeTypeExtra &extra = it->second;
QString mainPattern;
diff --git a/src/corelib/tools/qflatmap_p.h b/src/corelib/tools/qflatmap_p.h
index 5a827fb4148..bdb0e24dde8 100644
--- a/src/corelib/tools/qflatmap_p.h
+++ b/src/corelib/tools/qflatmap_p.h
@@ -609,14 +609,18 @@ public:
T value(const Key &key) const
{
auto it = find(key);
- return it == end() ? T() : it.value();
+ if (it == end())
+ return T();
+ return it.value();
}
template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
T value(const X &key) const
{
auto it = find(key);
- return it == end() ? T() : it.value();
+ if (it == end())
+ return T();
+ return it.value();
}
T &operator[](const Key &key)
@@ -899,12 +903,13 @@ private:
T do_take(iterator it)
{
- if (it != end()) {
+ if (it == end())
+ return {};
+ return [&] {
T result = std::move(it.value());
erase(it);
return result;
- }
- return {};
+ }();
}
template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
diff --git a/src/corelib/tools/qmargins.h b/src/corelib/tools/qmargins.h
index f833a338b16..cbdb093adc8 100644
--- a/src/corelib/tools/qmargins.h
+++ b/src/corelib/tools/qmargins.h
@@ -333,20 +333,13 @@ private:
qreal m_right;
qreal m_bottom;
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_FLOAT_COMPARE
friend constexpr bool qFuzzyCompare(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
{
- return ((!lhs.m_left || !rhs.m_left) ? qFuzzyIsNull(lhs.m_left - rhs.m_left)
- : qFuzzyCompare(lhs.m_left, rhs.m_left))
- && ((!lhs.m_top || !rhs.m_top) ? qFuzzyIsNull(lhs.m_top - rhs.m_top)
- : qFuzzyCompare(lhs.m_top, rhs.m_top))
- && ((!lhs.m_right || !rhs.m_right) ? qFuzzyIsNull(lhs.m_right - rhs.m_right)
- : qFuzzyCompare(lhs.m_right, rhs.m_right))
- && ((!lhs.m_bottom || !rhs.m_bottom) ? qFuzzyIsNull(lhs.m_bottom - rhs.m_bottom)
- : qFuzzyCompare(lhs.m_bottom, rhs.m_bottom));
+ return QtPrivate::fuzzyCompare(lhs.m_left, rhs.m_left)
+ && QtPrivate::fuzzyCompare(lhs.m_top, rhs.m_top)
+ && QtPrivate::fuzzyCompare(lhs.m_right, rhs.m_right)
+ && QtPrivate::fuzzyCompare(lhs.m_bottom, rhs.m_bottom);
}
- QT_WARNING_POP
friend constexpr bool qFuzzyIsNull(const QMarginsF &m) noexcept
{
return qFuzzyIsNull(m.m_left) && qFuzzyIsNull(m.m_top)
diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h
index ae896ba7079..1b767324058 100644
--- a/src/corelib/tools/qpoint.h
+++ b/src/corelib/tools/qpoint.h
@@ -259,14 +259,11 @@ public:
}
private:
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_FLOAT_COMPARE
friend constexpr bool qFuzzyCompare(const QPointF &p1, const QPointF &p2) noexcept
{
- return ((!p1.xp || !p2.xp) ? qFuzzyIsNull(p1.xp - p2.xp) : qFuzzyCompare(p1.xp, p2.xp))
- && ((!p1.yp || !p2.yp) ? qFuzzyIsNull(p1.yp - p2.yp) : qFuzzyCompare(p1.yp, p2.yp));
+ return QtPrivate::fuzzyCompare(p1.xp, p2.xp)
+ && QtPrivate::fuzzyCompare(p1.yp, p2.yp);
}
- QT_WARNING_POP
friend constexpr bool qFuzzyIsNull(const QPointF &point) noexcept
{
return qFuzzyIsNull(point.xp) && qFuzzyIsNull(point.yp);
diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h
index 86509cb6483..1c5b02ed1f0 100644
--- a/src/corelib/tools/qsize.h
+++ b/src/corelib/tools/qsize.h
@@ -258,10 +258,9 @@ private:
QT_WARNING_DISABLE_FLOAT_COMPARE
friend constexpr bool qFuzzyCompare(const QSizeF &s1, const QSizeF &s2) noexcept
{
- // Cannot use qFuzzyCompare(), because it will give incorrect results
// if one of the arguments is 0.0.
- return ((!s1.wd || !s2.wd) ? qFuzzyIsNull(s1.wd - s2.wd) : qFuzzyCompare(s1.wd, s2.wd))
- && ((!s1.ht || !s2.ht) ? qFuzzyIsNull(s1.ht - s2.ht) : qFuzzyCompare(s1.ht, s2.ht));
+ return QtPrivate::fuzzyCompare(s1.wd, s2.wd)
+ && QtPrivate::fuzzyCompare(s1.ht, s2.ht);
}
QT_WARNING_POP
friend constexpr bool qFuzzyIsNull(const QSizeF &size) noexcept
diff --git a/src/gui/accessible/linux/qspi_struct_marshallers.cpp b/src/gui/accessible/linux/qspi_struct_marshallers.cpp
index 241bad502e3..5e171244cd0 100644
--- a/src/gui/accessible/linux/qspi_struct_marshallers.cpp
+++ b/src/gui/accessible/linux/qspi_struct_marshallers.cpp
@@ -28,7 +28,6 @@ QT_IMPL_METATYPE_EXTERN(QSpiRelationArray)
QT_IMPL_METATYPE_EXTERN(QSpiTextRange)
QT_IMPL_METATYPE_EXTERN(QSpiTextRangeList)
QT_IMPL_METATYPE_EXTERN(QSpiAttributeSet)
-QT_IMPL_METATYPE_EXTERN(QSpiAppUpdate)
QT_IMPL_METATYPE_EXTERN(QSpiDeviceEvent)
QT_IMPL_METATYPE_EXTERN(QSpiMatchRule)
@@ -134,23 +133,6 @@ const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiEventListener
return argument;
}
-/* QSpiAppUpdate */
-/*---------------------------------------------------------------------------*/
-
-QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update) {
- argument.beginStructure();
- argument << update.type << update.address;
- argument.endStructure();
- return argument;
-}
-
-const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update) {
- argument.beginStructure();
- argument >> update.type >> update.address;
- argument.endStructure();
- return argument;
-}
-
/* QSpiRelationArrayEntry */
/*---------------------------------------------------------------------------*/
@@ -245,7 +227,6 @@ void qSpiInitializeStructTypes()
qDBusRegisterMetaType<QSpiEventListenerArray>();
qDBusRegisterMetaType<QSpiDeviceEvent>();
qDBusRegisterMetaType<QSpiMatchRule>();
- qDBusRegisterMetaType<QSpiAppUpdate>();
qDBusRegisterMetaType<QSpiRelationArrayEntry>();
qDBusRegisterMetaType<QSpiRelationArray>();
}
diff --git a/src/gui/accessible/linux/qspi_struct_marshallers_p.h b/src/gui/accessible/linux/qspi_struct_marshallers_p.h
index fe2d52fb4c2..4c446a97040 100644
--- a/src/gui/accessible/linux/qspi_struct_marshallers_p.h
+++ b/src/gui/accessible/linux/qspi_struct_marshallers_p.h
@@ -106,21 +106,6 @@ Q_DECLARE_TYPEINFO(QSpiTextRange, Q_RELOCATABLE_TYPE);
typedef QList<QSpiTextRange> QSpiTextRangeList;
typedef QMap <QString, QString> QSpiAttributeSet;
-enum QSpiAppUpdateType {
- QSPI_APP_UPDATE_ADDED = 0,
- QSPI_APP_UPDATE_REMOVED = 1
-};
-Q_DECLARE_TYPEINFO(QSpiAppUpdateType, Q_PRIMITIVE_TYPE);
-
-struct QSpiAppUpdate {
- int type; /* Is an application added or removed */
- QString address; /* D-Bus address of application added or removed */
-};
-Q_DECLARE_TYPEINFO(QSpiAppUpdate, Q_RELOCATABLE_TYPE);
-
-QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update);
-const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update);
-
struct QSpiDeviceEvent {
unsigned int type;
int id;
@@ -171,7 +156,6 @@ QT_DECL_METATYPE_EXTERN(QSpiRelationArray, /* not exported */)
QT_DECL_METATYPE_EXTERN(QSpiTextRange, /* not exported */)
QT_DECL_METATYPE_EXTERN(QSpiTextRangeList, /* not exported */)
QT_DECL_METATYPE_EXTERN(QSpiAttributeSet, /* not exported */)
-QT_DECL_METATYPE_EXTERN(QSpiAppUpdate, /* not exported */)
QT_DECL_METATYPE_EXTERN(QSpiDeviceEvent, /* not exported */)
QT_DECL_METATYPE_EXTERN(QSpiMatchRule, /* not exported */)
diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp
index a8255e04c02..311b53aeaa3 100644
--- a/src/gui/accessible/qaccessiblecache.cpp
+++ b/src/gui/accessible/qaccessiblecache.cpp
@@ -4,6 +4,7 @@
#include "qaccessiblecache_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qloggingcategory.h>
+#include <private/qguiapplication_p.h>
#if QT_CONFIG(accessibility)
@@ -176,10 +177,28 @@ void QAccessibleCache::sendObjectDestroyedEvent(QObject *obj)
void QAccessibleCache::deleteInterface(QAccessible::Id id, QObject *obj)
{
- QAccessibleInterface *iface = idToInterface.take(id);
+ const auto it = idToInterface.find(id);
+ if (it == idToInterface.end()) // the interface may be deleted already
+ return;
+
+ QAccessibleInterface *iface = *it;
qCDebug(lcAccessibilityCache) << "delete - id:" << id << " iface:" << iface;
- if (!iface) // the interface may be deleted already
+ if (!iface) {
+ idToInterface.erase(it);
return;
+ }
+
+ // QObjects sends this from their destructor, but
+ // the object less interfaces calls deleteInterface
+ // directly
+ if (!obj && !iface->object()) {
+ if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing && QAccessible::isActive()) {
+ QAccessibleObjectDestroyedEvent event(id);
+ QAccessible::updateAccessibility(&event);
+ }
+ }
+
+ idToInterface.erase(it);
interfaceToId.take(iface);
if (!obj)
obj = iface->object();
diff --git a/src/gui/image/qabstractfileiconprovider.cpp b/src/gui/image/qabstractfileiconprovider.cpp
index 78777ec115a..ad646a6b89a 100644
--- a/src/gui/image/qabstractfileiconprovider.cpp
+++ b/src/gui/image/qabstractfileiconprovider.cpp
@@ -288,3 +288,5 @@ QString QAbstractFileIconProvider::type(const QFileInfo &info) const
}
QT_END_NAMESPACE
+
+#include "moc_qabstractfileiconprovider.cpp"
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp
index a675f59eb1f..57587322ea5 100644
--- a/src/gui/math3d/qquaternion.cpp
+++ b/src/gui/math3d/qquaternion.cpp
@@ -409,7 +409,7 @@ QQuaternion QQuaternion::fromAxisAndAngle
(float x, float y, float z, float angle)
{
float length = qHypot(x, y, z);
- if (!qFuzzyCompare(length, 1.0f) && !qFuzzyIsNull(length)) {
+ if (!qFuzzyIsNull(length) && !qFuzzyCompare(length, 1.0f)) {
x /= length;
y /= length;
z /= length;
diff --git a/src/gui/math3d/qvectornd.cpp b/src/gui/math3d/qvectornd.cpp
index dcd7bdbcf80..ec836cfa56e 100644
--- a/src/gui/math3d/qvectornd.cpp
+++ b/src/gui/math3d/qvectornd.cpp
@@ -467,7 +467,6 @@ QDataStream &operator>>(QDataStream &stream, QVector2D &vector)
float x, y;
stream >> x;
stream >> y;
- Q_ASSERT(qIsFinite(x) && qIsFinite(y));
vector.setX(x);
vector.setY(y);
return stream;
@@ -1098,7 +1097,6 @@ QDataStream &operator>>(QDataStream &stream, QVector3D &vector)
stream >> x;
stream >> y;
stream >> z;
- Q_ASSERT(qIsFinite(x) && qIsFinite(y) && qIsFinite(z));
vector.setX(x);
vector.setY(y);
vector.setZ(z);
@@ -1627,7 +1625,6 @@ QDataStream &operator>>(QDataStream &stream, QVector4D &vector)
stream >> y;
stream >> z;
stream >> w;
- Q_ASSERT(qIsFinite(x) && qIsFinite(y) && qIsFinite(z) && qIsFinite(w));
vector.setX(x);
vector.setY(y);
vector.setZ(z);
diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp
index 7acc3c5218c..5bd9799ca7d 100644
--- a/src/gui/text/qrawfont.cpp
+++ b/src/gui/text/qrawfont.cpp
@@ -224,7 +224,7 @@ void QRawFont::loadFromData(const QByteArray &fontData,
\since 6.11
*/
-int QRawFont::glyphCount() const
+quint32 QRawFont::glyphCount() const
{
return d->isValid() ? d->fontEngine->glyphCount() : 0;
}
diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h
index f13f04ebe37..a1522aa8048 100644
--- a/src/gui/text/qrawfont.h
+++ b/src/gui/text/qrawfont.h
@@ -55,7 +55,7 @@ public:
inline bool operator!=(const QRawFont &other) const
{ return !operator==(other); }
- int glyphCount() const;
+ quint32 glyphCount() const;
QString familyName() const;
QString styleName() const;
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index ede5409b112..41d2d417133 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -1746,7 +1746,7 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st
// fix up clusters so that the cluster indices will be monotonic
// and thus we never return out-of-order indices
- while (last_cluster++ < cluster && str_pos < item_length)
+ for (uint j = last_cluster; j < cluster && str_pos < item_length; ++j)
log_clusters[str_pos++] = last_glyph_pos;
last_glyph_pos = i + glyphs_shaped;
last_cluster = cluster;
diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm
index 64c806a087b..b3c22ff051e 100644
--- a/src/plugins/platforms/cocoa/qnsview_drawing.mm
+++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm
@@ -183,6 +183,9 @@
{
qCDebug(lcQpaDrawing) << "Backing properties changed for" << self;
+ if (!m_platformWindow)
+ return;
+
[self propagateBackingProperties];
// Ideally we would plumb this situation through QPA in a way that lets
diff --git a/src/plugins/platforms/wasm/qwasmdrag.cpp b/src/plugins/platforms/wasm/qwasmdrag.cpp
index ecd95f5e338..757959e5694 100644
--- a/src/plugins/platforms/wasm/qwasmdrag.cpp
+++ b/src/plugins/platforms/wasm/qwasmdrag.cpp
@@ -174,19 +174,21 @@ void QWasmDrag::onNativeDrop(DragEvent *event)
// files, but the browser expects that accepted state is set before any
// async calls.
event->acceptDrop();
+ std::shared_ptr<DragState> dragState = m_dragState;
- const auto dropCallback = [&m_dragState = m_dragState, wasmWindow, targetWindowPos,
+ const auto dropCallback = [dragState, wasmWindow, targetWindowPos,
actions, mouseButton, modifiers](QMimeData *mimeData) {
-
- auto dropResponse = std::make_shared<QPlatformDropQtResponse>(true, Qt::DropAction::CopyAction);
- *dropResponse = QWindowSystemInterface::handleDrop(wasmWindow->window(), mimeData,
+ if (mimeData) {
+ auto dropResponse = std::make_shared<QPlatformDropQtResponse>(true, Qt::DropAction::CopyAction);
+ *dropResponse = QWindowSystemInterface::handleDrop(wasmWindow->window(), mimeData,
targetWindowPos, actions,
mouseButton, modifiers);
- if (dropResponse->isAccepted())
- m_dragState->dropAction = dropResponse->acceptedAction();
+ if (dragState && dropResponse->isAccepted())
+ dragState->dropAction = dropResponse->acceptedAction();
- delete mimeData;
+ delete mimeData;
+ }
};
event->dataTransfer.toMimeDataWithFile(dropCallback);
diff --git a/src/plugins/platforms/wasm/qwasmdrag.h b/src/plugins/platforms/wasm/qwasmdrag.h
index 61df8155fa6..5bb8ec66a3c 100644
--- a/src/plugins/platforms/wasm/qwasmdrag.h
+++ b/src/plugins/platforms/wasm/qwasmdrag.h
@@ -41,7 +41,7 @@ public:
private:
struct DragState;
- std::unique_ptr<DragState> m_dragState;
+ std::shared_ptr<DragState> m_dragState;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp
index f27943070d0..2be05625971 100644
--- a/src/plugins/platforms/wayland/qwaylandwindow.cpp
+++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp
@@ -513,7 +513,6 @@ void QWaylandWindow::setGeometry(const QRect &r)
mWindowDecoration->update();
QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(window(), geometry());
- mSentInitialResize = true;
}
// Wayland has no concept of areas being exposed or not, only the entire window, when our geometry changes, we need to flag the new area as exposed
diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h
index 9e1bd92af30..7dda16cc776 100644
--- a/src/plugins/platforms/wayland/qwaylandwindow_p.h
+++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h
@@ -334,7 +334,6 @@ protected:
int mFrameCallbackTimeout = 100;
QVariantMap m_properties;
- bool mSentInitialResize = false;
QPoint mOffset;
std::optional<qreal> mScale = std::nullopt;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index db3fb160593..e2f181aa628 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -82,9 +82,12 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve
{
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
if (event->changedStates().checked || event->changedStates().checkStateMixed) {
- // Notifies states changes in checkboxes and switches.
+ // Notifies states changes in checkboxes, switches, and checkable item view items.
if (accessible->role() == QAccessible::CheckBox
- || accessible->role() == QAccessible::Switch) {
+ || accessible->role() == QAccessible::Switch
+ || accessible->role() == QAccessible::Cell
+ || accessible->role() == QAccessible::ListItem
+ || accessible->role() == QAccessible::TreeItem) {
if (auto provider = providerForAccessible(accessible)) {
long toggleState = ToggleState_Off;
if (accessible->state().checked)
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index b4c12ed1a0c..d8e41a753ef 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -856,7 +856,7 @@ void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event)
if (event->data.data32[0] && event->data.data32[0] != current_target)
return;
- const bool dropPossible = event->data.data32[1];
+ const bool dropPossible = event->data.data32[1] & 1;
setCanDrop(dropPossible);
if (dropPossible) {
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index fbd6d3154e2..6e7077b383e 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
@@ -770,6 +770,10 @@ void Generator::addProperties()
addFlag("Constant");
if (p.final)
addFlag("Final");
+ if (p.virtual_)
+ addFlag("Virtual");
+ if (p.override)
+ addFlag("Override");
if (p.user != "false")
addFlag("User");
if (p.required)
diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp
index 64af8c10fc1..baa6690350d 100644
--- a/src/tools/moc/moc.cpp
+++ b/src/tools/moc/moc.cpp
@@ -1434,6 +1434,9 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
next(IDENTIFIER);
propDef.name = lexem();
continue;
+ } else if (l[0] == 'O' && l == "OVERRIDE") {
+ propDef.override = true;
+ continue;
} else if (l[0] == 'R' && l == "REQUIRED") {
propDef.required = true;
continue;
@@ -1441,6 +1444,9 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
prev();
propDef.revision = parseRevision().toEncodedVersion<int>();
continue;
+ } else if (l[0] == 'V' && l == "VIRTUAL") {
+ propDef.virtual_ = true;
+ continue;
}
QByteArray v, v2;
@@ -1545,6 +1551,24 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
propDef.write = "";
warning(msg.constData());
}
+ if (propDef.override && propDef.virtual_) {
+ const QByteArray msg = "Issue with property declaration " + propDef.name
+ + ": VIRTUAL is redundant when overriding a property. The OVERRIDE "
+ "must only be used when actually overriding an existing property; using it on a "
+ "new property is an error.";
+ error(msg.constData());
+ }
+ if (propDef.override && propDef.final) {
+ const QByteArray msg = "Issue with property declaration " + propDef.name
+ + ": OVERRIDE is redundant when property is marked FINAL";
+ error(msg.constData());
+ }
+ if (propDef.virtual_ && propDef.final) {
+ const QByteArray msg = "Issue with property declaration " + propDef.name
+ + ": The VIRTUAL cannot be combined with FINAL, as these attributes are mutually "
+ "exclusive";
+ error(msg.constData());
+ }
}
void Moc::parseProperty(ClassDef *def, Moc::PropertyMode mode)
diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h
index aafa80d2164..a211433622a 100644
--- a/src/tools/moc/moc.h
+++ b/src/tools/moc/moc.h
@@ -130,6 +130,8 @@ struct PropertyDef
TypeTags typeTag;
bool constant = false;
bool final = false;
+ bool virtual_ = false;
+ bool override = false;
bool required = false;
int relativeIndex = -1; // property index in current metaobject
int lineNumber = 0;
diff --git a/src/widgets/accessible/itemviews.cpp b/src/widgets/accessible/itemviews.cpp
index cc3a230f9b4..ba941012dd7 100644
--- a/src/widgets/accessible/itemviews.cpp
+++ b/src/widgets/accessible/itemviews.cpp
@@ -99,6 +99,21 @@ QHeaderView *QAccessibleTable::verticalHeader() const
return header;
}
+// Normally cellAt takes row/column in the range
+// [0 .. rowCount())
+// [0 .. columnCount())
+//
+// As an extension we allow clients to ask for headers
+//
+// * Has both vertical and horizontal headers:
+// (-1,-1) -> corner button
+// * Has column headers:
+// (-1, column) -> column header for column \a column
+// * has row headers
+// (row, -1) -> row header for row \a row
+//
+// If asking for a header that does not exist, The invalid
+// index warning is logged, and nullptr is returned.
QAccessibleInterface *QAccessibleTable::cellAt(int row, int column) const
{
const QAbstractItemView *theView = view();
@@ -107,6 +122,22 @@ QAccessibleInterface *QAccessibleTable::cellAt(int row, int column) const
return nullptr;
Q_ASSERT(role() != QAccessible::List);
Q_ASSERT(role() != QAccessible::Tree);
+
+ const int vHeader = verticalHeader() ? 1 : 0;
+ const int hHeader = horizontalHeader() ? 1 : 0;
+
+ const int doHHeader = ((row == -1) && hHeader);
+ const int doVHeader = ((column == -1) && vHeader);
+
+ if (doVHeader && doHHeader)
+ return child(0);
+
+ if (doVHeader)
+ return child((row + hHeader) * (columnCount() + vHeader) + (column + vHeader));
+
+ if (doHHeader)
+ return child((row + hHeader) * (columnCount() + vHeader) + (column + vHeader));
+
QModelIndex index = theModel->index(row, column, theView->rootIndex());
if (Q_UNLIKELY(!index.isValid())) {
qWarning() << "QAccessibleTable::cellAt: invalid index: " << index << " for " << theView;
diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp
index 6288aae096a..05233ba5801 100644
--- a/src/widgets/itemviews/qabstractitemview.cpp
+++ b/src/widgets/itemviews/qabstractitemview.cpp
@@ -172,6 +172,43 @@ void QAbstractItemViewPrivate::checkMouseMove(const QPersistentModelIndex &index
}
}
+#if QT_CONFIG(accessibility)
+void QAbstractItemViewPrivate::updateItemAccessibility(const QModelIndex &index,
+ const QList<int> &roles)
+{
+ Q_Q(QAbstractItemView);
+
+ if (!QAccessible::isActive())
+ return;
+
+ const int childIndex = accessibleChildIndex(index);
+ if (childIndex < 0)
+ return;
+
+ // see QAccessibleTableCell for how role data are mapped to the a11y layer
+
+ for (int role : roles) {
+ if (role == Qt::AccessibleTextRole
+ || (role == Qt::DisplayRole
+ && index.data(Qt::AccessibleTextRole).toString().isEmpty())) {
+ QAccessibleEvent event(q, QAccessible::NameChanged);
+ event.setChild(childIndex);
+ QAccessible::updateAccessibility(&event);
+ } else if (role == Qt::AccessibleDescriptionRole) {
+ QAccessibleEvent event(q, QAccessible::DescriptionChanged);
+ event.setChild(childIndex);
+ QAccessible::updateAccessibility(&event);
+ } else if (role == Qt::CheckStateRole) {
+ QAccessible::State state;
+ state.checked = true;
+ QAccessibleStateChangeEvent event(q, state);
+ event.setChild(childIndex);
+ QAccessible::updateAccessibility(&event);
+ }
+ }
+}
+#endif
+
#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
// stores and restores the selection and current item when flicking
@@ -3495,6 +3532,10 @@ void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelInde
accessibleEvent.setLastRow(bottomRight.row());
accessibleEvent.setLastColumn(bottomRight.column());
QAccessible::updateAccessibility(&accessibleEvent);
+
+ // send accessibility events as needed when current item is modified
+ if (topLeft == bottomRight && topLeft == currentIndex())
+ d->updateItemAccessibility(topLeft, roles);
}
#endif
d->updateGeometry();
diff --git a/src/widgets/itemviews/qabstractitemview_p.h b/src/widgets/itemviews/qabstractitemview_p.h
index 60799fb8a50..f9e899d7fc8 100644
--- a/src/widgets/itemviews/qabstractitemview_p.h
+++ b/src/widgets/itemviews/qabstractitemview_p.h
@@ -272,6 +272,18 @@ public:
return isIndexValid(index) && isIndexSelectable(index);
}
+#if QT_CONFIG(accessibility)
+ virtual int accessibleChildIndex(const QModelIndex &index) const
+ {
+ Q_UNUSED(index);
+ return -1;
+ }
+#endif
+
+#if QT_CONFIG(accessibility)
+ void updateItemAccessibility(const QModelIndex &index, const QList<int> &roles);
+#endif
+
// reimplemented from QAbstractScrollAreaPrivate
QPoint contentsOffset() const override {
Q_Q(const QAbstractItemView);
diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp
index e245f98151b..50b6034500d 100644
--- a/src/widgets/itemviews/qlistview.cpp
+++ b/src/widgets/itemviews/qlistview.cpp
@@ -1959,6 +1959,14 @@ bool QListViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QMo
}
#endif
+#if QT_CONFIG(accessibility)
+int QListViewPrivate::accessibleChildIndex(const QModelIndex &index) const
+{
+ Q_Q(const QListView);
+ return q->visualIndex(index);
+}
+#endif
+
void QListViewPrivate::removeCurrentAndDisabled(QList<QModelIndex> *indexes,
const QModelIndex &current) const
{
@@ -3397,11 +3405,12 @@ void QIconModeViewBase::updateContentsSize()
*/
void QListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
+ Q_D(const QListView);
QAbstractItemView::currentChanged(current, previous);
#if QT_CONFIG(accessibility)
if (QAccessible::isActive()) {
if (current.isValid() && hasFocus()) {
- int entry = visualIndex(current);
+ int entry = d->accessibleChildIndex(current);
QAccessibleEvent event(this, QAccessible::Focus);
event.setChild(entry);
QAccessible::updateAccessibility(&event);
@@ -3417,18 +3426,19 @@ void QListView::selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected)
{
#if QT_CONFIG(accessibility)
+ Q_D(const QListView);
if (QAccessible::isActive()) {
// ### does not work properly for selection ranges.
QModelIndex sel = selected.indexes().value(0);
if (sel.isValid()) {
- int entry = visualIndex(sel);
+ int entry = d->accessibleChildIndex(sel);
QAccessibleEvent event(this, QAccessible::SelectionAdd);
event.setChild(entry);
QAccessible::updateAccessibility(&event);
}
QModelIndex desel = deselected.indexes().value(0);
if (desel.isValid()) {
- int entry = visualIndex(desel);
+ int entry = d->accessibleChildIndex(desel);
QAccessibleEvent event(this, QAccessible::SelectionRemove);
event.setChild(entry);
QAccessible::updateAccessibility(&event);
diff --git a/src/widgets/itemviews/qlistview_p.h b/src/widgets/itemviews/qlistview_p.h
index 4475fa5461f..7e36887a65c 100644
--- a/src/widgets/itemviews/qlistview_p.h
+++ b/src/widgets/itemviews/qlistview_p.h
@@ -346,6 +346,10 @@ public:
bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index) override;
#endif
+#if QT_CONFIG(accessibility)
+ int accessibleChildIndex(const QModelIndex &index) const override;
+#endif
+
inline void setGridSize(const QSize &size) { grid = size; }
inline QSize gridSize() const { return grid; }
inline void setWrapping(bool b) { wrap = b; }
diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp
index 40e3fcaf91b..2d28b3d4a81 100644
--- a/src/widgets/itemviews/qtableview.cpp
+++ b/src/widgets/itemviews/qtableview.cpp
@@ -3593,7 +3593,7 @@ void QTableView::currentChanged(const QModelIndex &current, const QModelIndex &p
if (QAccessible::isActive()) {
if (current.isValid() && hasFocus()) {
Q_D(QTableView);
- int entry = d->accessibleTable2Index(current);
+ int entry = d->accessibleChildIndex(current);
QAccessibleEvent event(this, QAccessible::Focus);
event.setChild(entry);
QAccessible::updateAccessibility(&event);
@@ -3616,14 +3616,14 @@ void QTableView::selectionChanged(const QItemSelection &selected,
// ### does not work properly for selection ranges.
QModelIndex sel = selected.indexes().value(0);
if (sel.isValid()) {
- int entry = d->accessibleTable2Index(sel);
+ int entry = d->accessibleChildIndex(sel);
QAccessibleEvent event(this, QAccessible::SelectionAdd);
event.setChild(entry);
QAccessible::updateAccessibility(&event);
}
QModelIndex desel = deselected.indexes().value(0);
if (desel.isValid()) {
- int entry = d->accessibleTable2Index(desel);
+ int entry = d->accessibleChildIndex(desel);
QAccessibleEvent event(this, QAccessible::SelectionRemove);
event.setChild(entry);
QAccessible::updateAccessibility(&event);
diff --git a/src/widgets/itemviews/qtableview_p.h b/src/widgets/itemviews/qtableview_p.h
index 8ddb8e797a9..9a7ce229880 100644
--- a/src/widgets/itemviews/qtableview_p.h
+++ b/src/widgets/itemviews/qtableview_p.h
@@ -141,11 +141,14 @@ public:
QStyleOptionViewItem::ViewItemPosition viewItemPosition(const QModelIndex &index) const;
- inline int accessibleTable2Index(const QModelIndex &index) const {
+#if QT_CONFIG(accessibility)
+ inline int accessibleChildIndex(const QModelIndex &index) const override
+ {
const int vHeader = verticalHeader ? 1 : 0;
return (index.row() + (horizontalHeader ? 1 : 0)) * (index.model()->columnCount() + vHeader)
+ index.column() + vHeader;
}
+#endif
int sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const;
int sectionSpanSize(const QHeaderView *header, int logical, int span) const;
diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp
index e38d78b72f8..570566793dc 100644
--- a/src/widgets/itemviews/qtreeview.cpp
+++ b/src/widgets/itemviews/qtreeview.cpp
@@ -4083,13 +4083,15 @@ void QTreeViewPrivate::sortIndicatorChanged(int column, Qt::SortOrder order)
model->sort(column, order);
}
-int QTreeViewPrivate::accessibleTree2Index(const QModelIndex &index) const
+#if QT_CONFIG(accessibility)
+int QTreeViewPrivate::accessibleChildIndex(const QModelIndex &index) const
{
Q_Q(const QTreeView);
// Note that this will include the header, even if its hidden.
return (q->visualIndex(index) + (q->header() ? 1 : 0)) * index.model()->columnCount() + index.column();
}
+#endif
void QTreeViewPrivate::updateIndentationFromStyle()
{
@@ -4116,7 +4118,7 @@ void QTreeView::currentChanged(const QModelIndex &current, const QModelIndex &pr
Q_D(QTreeView);
QAccessibleEvent event(this, QAccessible::Focus);
- event.setChild(d->accessibleTree2Index(current));
+ event.setChild(d->accessibleChildIndex(current));
QAccessible::updateAccessibility(&event);
}
#endif
@@ -4136,7 +4138,7 @@ void QTreeView::selectionChanged(const QItemSelection &selected,
// ### does not work properly for selection ranges.
QModelIndex sel = selected.indexes().value(0);
if (sel.isValid()) {
- int entry = d->accessibleTree2Index(sel);
+ int entry = d->accessibleChildIndex(sel);
Q_ASSERT(entry >= 0);
QAccessibleEvent event(this, QAccessible::SelectionAdd);
event.setChild(entry);
@@ -4144,7 +4146,7 @@ void QTreeView::selectionChanged(const QItemSelection &selected,
}
QModelIndex desel = deselected.indexes().value(0);
if (desel.isValid()) {
- int entry = d->accessibleTree2Index(desel);
+ int entry = d->accessibleChildIndex(desel);
Q_ASSERT(entry >= 0);
QAccessibleEvent event(this, QAccessible::SelectionRemove);
event.setChild(entry);
diff --git a/src/widgets/itemviews/qtreeview_p.h b/src/widgets/itemviews/qtreeview_p.h
index 5a4e057901c..34db2fcdacb 100644
--- a/src/widgets/itemviews/qtreeview_p.h
+++ b/src/widgets/itemviews/qtreeview_p.h
@@ -234,7 +234,9 @@ public:
return (viewIndex(index) + (header ? 1 : 0)) * model->columnCount()+index.column();
}
- int accessibleTree2Index(const QModelIndex &index) const;
+#if QT_CONFIG(accessibility)
+ int accessibleChildIndex(const QModelIndex &index) const override;
+#endif
void updateIndentationFromStyle();
diff --git a/tests/auto/corelib/io/CMakeLists.txt b/tests/auto/corelib/io/CMakeLists.txt
index c0d5ea3136e..10327ceaefb 100644
--- a/tests/auto/corelib/io/CMakeLists.txt
+++ b/tests/auto/corelib/io/CMakeLists.txt
@@ -9,7 +9,7 @@ endif()
if(QT_FEATURE_private_tests)
add_subdirectory(qabstractfileengine)
add_subdirectory(qfileinfo)
- if(LINUX AND QT_FEATURE_liburing)
+ if((LINUX AND QT_FEATURE_liburing) OR (WIN32 AND QT_FEATURE_windows_ioring))
add_subdirectory(qioring)
endif()
add_subdirectory(qipaddress)
diff --git a/tests/auto/corelib/io/qioring/tst_qioring.cpp b/tests/auto/corelib/io/qioring/tst_qioring.cpp
index 1128bcd7979..75d4fe68c55 100644
--- a/tests/auto/corelib/io/qioring/tst_qioring.cpp
+++ b/tests/auto/corelib/io/qioring/tst_qioring.cpp
@@ -5,7 +5,12 @@
#include <QtCore/private/qioring_p.h>
+#ifdef Q_OS_WIN
+#include <QtCore/qt_windows.h>
+#include <io.h>
+#else
#include <QtCore/private/qcore_unix_p.h>
+#endif
using namespace Qt::StringLiterals;
using namespace std::chrono_literals;
@@ -30,7 +35,13 @@ private:
void tst_QIORing::closeFile(qintptr fd)
{
+#ifdef Q_OS_WIN
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ HANDLE h = HANDLE(fd);
+ CloseHandle(h);
+#else
QT_CLOSE(fd);
+#endif
}
qintptr tst_QIORing::openHelper(QIORing *ring, const QString &path, QIODevice::OpenMode flags)
@@ -109,7 +120,11 @@ void tst_QIORing::read()
QFile file(QFINDTESTDATA("data/input.txt"));
QVERIFY(file.open(QIODevice::ReadOnly));
int fd = file.handle();
+#ifdef Q_OS_WIN
+ qintptr nativeFd = _get_osfhandle(fd);
+#else
qintptr nativeFd = fd;
+#endif
QIORing ring;
QVERIFY(ring.ensureInitialized());
diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp
index 71389abc976..a4ef698d380 100644
--- a/tests/auto/corelib/io/qurl/tst_qurl.cpp
+++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp
@@ -925,7 +925,7 @@ void tst_QUrl::resolving_data()
QTest::newRow("/-on-empty-no-authority") << "scheme:" << "/" << "scheme:/";
QTest::newRow(".-on-empty-no-authority") << "scheme:" << "." << "scheme:";
QTest::newRow("./-on-empty-no-authority") << "scheme:" << "./" << "scheme:";
- QTest::newRow(".//-on-empty-no-authority") << "scheme:" << "./" << "scheme:";
+ QTest::newRow(".//-on-empty-no-authority") << "scheme:" << ".//" << "scheme:";
QTest::newRow("..-on-empty-no-authority") << "scheme:" << ".." << "scheme:";
QTest::newRow("../-on-empty-no-authority") << "scheme:" << "../" << "scheme:";
diff --git a/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp b/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp
index eab791e57eb..4124b723b4c 100644
--- a/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp
+++ b/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp
@@ -2631,8 +2631,9 @@ public:
auto &childRows() { return m_children; }
private:
- template <std::size_t I> // read-only is enough for this
- friend decltype(auto) get(const ObjectTreeItem &row) { return row.m_objects[I]; }
+ template <std::size_t I, typename Item,
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<Item>, ObjectTreeItem>, bool> = true>
+ friend decltype(auto) get(Item &&row) { return q23::forward_like<Item>(row.m_objects[I]); }
ObjectTreeItem *m_parentRow = nullptr;
std::optional<ObjectTree> m_children = std::nullopt;
@@ -2684,7 +2685,7 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects()
Object *newGrandChild = new Object;
ObjectTreeItem newBranch(newChild);
newBranch.childRows() = ObjectTree{
- ObjectTreeItem(), // skip the first column
+ ObjectTreeItem(), // skip the first row to verify that we continue through nullptr
ObjectTreeItem(newGrandChild),
ObjectTreeItem()
};
@@ -2698,9 +2699,11 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects()
QCOMPARE(dataChangedSpy.count(), 1);
dataChangedSpy.clear();
- // newGrandChild = new Object;
- // adapter.at({0, 2, 0}, 0) = newGrandChild;
- // newGrandChild->setString("0.2.0");
+ newGrandChild = new Object;
+ adapter.at({0, 2, 0}, 0) = newGrandChild;
+ QCOMPARE(dataChangedSpy.count(), 1);
+ newGrandChild->setString("0.2.0");
+ QCOMPARE(dataChangedSpy.count(), 2);
}
QTEST_MAIN(tst_QRangeModelAdapter)
diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
index 696bcdc07d7..0fc7538c515 100644
--- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
+++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
@@ -291,6 +291,8 @@ class tst_QMetaObject : public QObject
Q_PROPERTY(int value8 READ value8)
Q_PROPERTY(int value9 READ value9 CONSTANT)
Q_PROPERTY(int value10 READ value10 FINAL)
+ Q_PROPERTY(int value11 READ value10 VIRTUAL)
+ Q_PROPERTY(int value12 READ value10 OVERRIDE)
public:
enum EnumType { EnumType1 };
@@ -358,6 +360,8 @@ private slots:
void propertyNotify();
void propertyConstant();
void propertyFinal();
+ void propertyVirtual();
+ void propertyOverride();
void metaType();
@@ -2727,6 +2731,32 @@ void tst_QMetaObject::propertyFinal()
QVERIFY(!prop.isFinal());
}
+void tst_QMetaObject::propertyVirtual()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value11"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isVirtual());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isVirtual());
+}
+
+void tst_QMetaObject::propertyOverride()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value12"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isOverride());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isOverride());
+}
+
void tst_QMetaObject::metaType()
{
QCOMPARE(QObject::staticMetaObject.metaType(), QMetaType::fromType<QObject>());
diff --git a/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp b/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp
index 3272ffac0ee..18d8b604dff 100644
--- a/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp
+++ b/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp
@@ -4,6 +4,9 @@
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
+
+#include <QtCore/qdatastream.h>
+
#ifdef QVARIANT_H
# error "This test requires qvector{2,3,4}d.h to not include qvariant.h"
#endif
@@ -162,6 +165,8 @@ private slots:
void metaTypes();
void structuredBinding();
+ void nonFiniteValuesStreamingRoundTrip_data();
+ void nonFiniteValuesStreamingRoundTrip();
};
// Test the creation of QVector2D objects in various ways:
@@ -2759,6 +2764,78 @@ void tst_QVectorND::structuredBinding()
}
}
+void tst_QVectorND::nonFiniteValuesStreamingRoundTrip_data()
+{
+ QTest::addColumn<float>("value");
+
+ constexpr auto inf = std::numeric_limits<float>::infinity();
+ constexpr auto NaN = std::numeric_limits<float>::quiet_NaN();
+
+ QTest::addRow("+∞") << +inf;
+ QTest::addRow("-∞") << -inf;
+ QTest::addRow("NaN") << NaN;
+
+}
+
+void tst_QVectorND::nonFiniteValuesStreamingRoundTrip()
+{
+ QFETCH(const float, value);
+
+ const QVector2D i2{value, value};
+ const QVector3D i3{value, value, value};
+ const QVector4D i4{value, value, value, value};
+
+ QByteArray buffer;
+
+ {
+ QDataStream s(&buffer, QIODevice::WriteOnly);
+ s << i2 << i3 << i4;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+ }
+
+ {
+ QVector2D o2 = {0, 0};
+ QVector3D o3 = {1, 0, -1};
+ QVector4D o4 = {0, 1, 2, 3};
+
+ QDataStream s(&buffer, QIODevice::ReadOnly);
+ s >> o2;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+ s >> o3;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+ s >> o4;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+
+ constexpr auto convert_to_binary = [](float v) {
+ uint r;
+ static_assert(sizeof v == sizeof r);
+ memcpy(&r, &v, sizeof v);
+ return r;
+ };
+
+ #define CHECK(n, what) \
+ do { \
+ const auto i ## n ## what = convert_to_binary(i ## n . what ()); \
+ const auto o ## n ## what = convert_to_binary(o ## n . what ()); \
+ QCOMPARE(i ## n ## what, o ## n ## what); \
+ } while (false)
+
+ CHECK(2, x);
+ CHECK(2, y);
+
+ CHECK(3, x);
+ CHECK(3, y);
+ CHECK(3, z);
+
+ CHECK(4, x);
+ CHECK(4, y);
+ CHECK(4, z);
+ CHECK(4, w);
+
+ #undef CHECK
+ }
+}
+
QTEST_APPLESS_MAIN(tst_QVectorND)
#include "tst_qvectornd.moc"
diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
index 305f48c95ee..c65f6645d01 100644
--- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
+++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
@@ -3582,6 +3582,22 @@ void tst_QAccessibility::tableTest()
tableView->horizontalHeader()->setVisible(false);
}
+ {
+ QTestAccessibility::clearEvents();
+ auto cell0 = table2->cellAt(0, 2);
+ auto cell1 = table2->cellAt(1, 2);
+ auto cell2 = table2->cellAt(2, 2);
+ auto cell3 = table2->cellAt(3, 2);
+ QAccessibleObjectDestroyedEvent event0(cell0);
+ QAccessibleObjectDestroyedEvent event1(cell1);
+ QAccessibleObjectDestroyedEvent event2(cell2);
+ QAccessibleObjectDestroyedEvent event3(cell3);
+ tableView->removeColumn(2);
+ QVERIFY_EVENT(&event0);
+ QVERIFY_EVENT(&event1);
+ QVERIFY_EVENT(&event2);
+ QVERIFY_EVENT(&event3);
+ }
tvHolder.reset();
QVERIFY(!QAccessible::accessibleInterface(id00));
QTestAccessibility::clearEvents();
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp
index fb30d66e6ec..7001d676878 100644
--- a/tests/auto/tools/moc/tst_moc.cpp
+++ b/tests/auto/tools/moc/tst_moc.cpp
@@ -2576,6 +2576,26 @@ void tst_Moc::warnings_data()
<< QString()
<< u"standard input:2:1: error: Parse error at \"NONSENSE\""_s;
+ QTest::newRow("VIRTUAL FINAL property")
+ << "class X { \n Q_PROPERTY(int p READ p VIRTUAL FINAL) \n };"_ba << QStringList() << 1
+ << QString()
+ << u"standard input:2:1: error: Issue with property declaration p: "
+ u"The VIRTUAL cannot be combined with FINAL, as these attributes are mutually exclusive"_s;
+
+ QTest::newRow("FINAL OVERRIDE property")
+ << "class X { \n Q_PROPERTY(int p READ p FINAL OVERRIDE) \n };"_ba << QStringList() << 1
+ << QString()
+ << u"standard input:2:1: error: Issue with property declaration p: "
+ u"OVERRIDE is redundant when property is marked FINAL"_s;
+
+ QTest::newRow("VIRTUAL OVERRIDE property")
+ << "class X { \n Q_PROPERTY(int p READ p VIRTUAL OVERRIDE) \n };"_ba << QStringList()
+ << 1 << QString()
+ << u"standard input:2:1: error: Issue with property declaration p: VIRTUAL is "
+ u"redundant when overriding a property."
+ u" The OVERRIDE must only be used when actually overriding an existing property;"
+ u" using it on a new property is an error."_s;
+
#ifdef Q_OS_UNIX // Limit to Unix because the error message is platform-dependent
QTest::newRow("Q_PLUGIN_METADATA: unreadable file")
<< QByteArray("class X { \n Q_PLUGIN_METADATA(FILE \".\") \n };")