summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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.txt1
-rw-r--r--src/corelib/doc/snippets/CMakeLists.txt1
-rw-r--r--src/corelib/doc/snippets/code/doc_src_properties.cpp2
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp24
-rw-r--r--src/corelib/doc/snippets/qrangemodeladapter/main.cpp366
-rw-r--r--src/corelib/doc/src/objectmodel/properties.qdoc16
-rw-r--r--src/corelib/global/qnamespace.h2
-rw-r--r--src/corelib/global/qnamespace.qdoc1
-rw-r--r--src/corelib/global/qtdeprecationmarkers.h8
-rw-r--r--src/corelib/itemmodels/qrangemodel.cpp28
-rw-r--r--src/corelib/itemmodels/qrangemodel.h8
-rw-r--r--src/corelib/itemmodels/qrangemodel_impl.h234
-rw-r--r--src/corelib/itemmodels/qrangemodeladapter.h1671
-rw-r--r--src/corelib/itemmodels/qrangemodeladapter.qdoc920
-rw-r--r--src/corelib/itemmodels/qrangemodeladapter_impl.h402
-rw-r--r--src/corelib/kernel/qassociativeiterable.cpp10
-rw-r--r--src/corelib/kernel/qassociativeiterable.h50
-rw-r--r--src/corelib/kernel/qmetaassociation.cpp14
-rw-r--r--src/corelib/kernel/qmetaassociation.h7
-rw-r--r--src/corelib/kernel/qmetacontainer.h2
-rw-r--r--src/corelib/kernel/qmetaobject.cpp28
-rw-r--r--src/corelib/kernel/qmetaobject.h2
-rw-r--r--src/corelib/kernel/qsequentialiterable.cpp10
-rw-r--r--src/corelib/kernel/qsequentialiterable.h38
-rw-r--r--src/corelib/kernel/qtmocconstants.h3
-rw-r--r--src/corelib/kernel/qvariant.cpp10
-rw-r--r--src/corelib/kernel/qvariant.h10
-rw-r--r--src/corelib/mimetypes/qmimeprovider.cpp7
-rw-r--r--src/corelib/thread/qfuture.qdoc14
-rw-r--r--src/corelib/tools/qflatmap_p.h15
-rw-r--r--src/gui/accessible/linux/atspiadaptor.cpp44
-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/linux/qspimatchrulematcher.cpp2
-rw-r--r--src/gui/accessible/qaccessiblecache.cpp23
-rw-r--r--src/gui/math3d/qquaternion.cpp2
-rw-r--r--src/gui/math3d/qvectornd.cpp3
-rw-r--r--src/gui/platform/unix/qxkbcommon.cpp6
-rw-r--r--src/gui/rhi/qrhivulkan.cpp77
-rw-r--r--src/gui/rhi/qrhivulkan_p.h10
-rw-r--r--src/gui/text/qfont.cpp15
-rw-r--r--src/gui/text/qtextengine.cpp2
-rw-r--r--src/network/CMakeLists.txt1
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h1
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp12
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h6
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp16
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp2
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h2
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp3
-rw-r--r--src/network/access/qnetworkrequest.cpp99
-rw-r--r--src/network/access/qnetworkrequest.h21
-rw-r--r--src/network/access/qtcpkeepaliveconfiguration_p.h50
-rw-r--r--src/network/socket/qabstractsocket.cpp14
-rw-r--r--src/network/socket/qabstractsocket_p.h2
-rw-r--r--src/plugins/platforms/directfb/qdirectfbconvenience.cpp1
-rw-r--r--src/plugins/platforms/wasm/qwasmdrag.cpp48
-rw-r--r--src/plugins/platforms/wasm/qwasmdrag.h3
-rw-r--r--src/plugins/platforms/wasm/qwasmevent.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp6
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.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
86 files changed, 4349 insertions, 376 deletions
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 <[email protected]>;
- 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 <[email protected]>.)
- * 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 <[email protected]>.)
+ * 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 <[email protected]>.)
+ 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 5d3d3024e0b..dec68c5f9f4 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -1231,6 +1231,7 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel
itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h
itemmodels/qitemselectionmodel.cpp itemmodels/qitemselectionmodel.h itemmodels/qitemselectionmodel_p.h
itemmodels/qrangemodel.h itemmodels/qrangemodel_impl.h itemmodels/qrangemodel.cpp
+ itemmodels/qrangemodeladapter.h itemmodels/qrangemodeladapter_impl.h
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_proxymodel
diff --git a/src/corelib/doc/snippets/CMakeLists.txt b/src/corelib/doc/snippets/CMakeLists.txt
index 55db84ccbea..c0d15463e9c 100644
--- a/src/corelib/doc/snippets/CMakeLists.txt
+++ b/src/corelib/doc/snippets/CMakeLists.txt
@@ -13,6 +13,7 @@ add_library(corelib_snippets OBJECT
qmessageauthenticationcode/main.cpp
qmetatype/registerConverters.cpp
qrangemodel/main.cpp
+ qrangemodeladapter/main.cpp
qstack/main.cpp
qstringlist/main.cpp
qstringlistmodel/main.cpp
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/snippets/code/src_corelib_thread_qfuture.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
index 6531d025bef..89ac917ccd3 100644
--- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
@@ -543,6 +543,30 @@ void examples(QFuture<QString> someQStringFuture,
f.cancelChain();
//! [38]
}
+
+ {
+ auto createFuture = [] { return QtFuture::makeReadyVoidFuture(); };
+ auto runNestedComputation = [] { return QtFuture::makeReadyVoidFuture(); };
+ //! [39]
+ QFuture<void> nested;
+ auto f = createFuture()
+ .then([&]{
+ nested = runNestedComputation();
+ // do some other work
+ return nested;
+ })
+ .unwrap()
+ .then([]{
+ // other continuation
+ })
+ .onCanceled([]{
+ // handle cancellation
+ });
+ //...
+ f.cancelChain();
+ nested.cancel();
+ //! [39]
+ }
}
class SomeClass : public QObject
diff --git a/src/corelib/doc/snippets/qrangemodeladapter/main.cpp b/src/corelib/doc/snippets/qrangemodeladapter/main.cpp
new file mode 100644
index 00000000000..a0791dd1dd1
--- /dev/null
+++ b/src/corelib/doc/snippets/qrangemodeladapter/main.cpp
@@ -0,0 +1,366 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore/qrangemodeladapter.h>
+
+#ifndef QT_NO_WIDGETS
+
+using namespace Qt::StringLiterals;
+
+#include <QtWidgets/qlistview.h>
+#include <QtWidgets/qtableview.h>
+#include <QtWidgets/qtreeview.h>
+#include <vector>
+
+class Book
+{
+ Q_GADGET
+ Q_PROPERTY(QString title READ title)
+ Q_PROPERTY(QString author READ author)
+ Q_PROPERTY(QString summary MEMBER m_summary)
+ Q_PROPERTY(int rating READ rating WRITE setRating)
+public:
+ enum Roles
+ {
+ TitleRole = Qt::UserRole,
+ AuthorRole,
+ SummaryRole,
+ RatingRole,
+ };
+
+ Book() = default;
+ Book(const QString &title, const QString &author);
+
+ // C++ rule of 0: destructor, as well as copy/move operations
+ // provided by the compiler.
+
+ // read-only properties
+ QString title() const { return m_title; }
+ QString author() const { return m_author; }
+
+ // read/writable property with input validation
+ int rating() const { return m_rating; }
+ void setRating(int rating)
+ {
+ m_rating = qBound(0, rating, 5);
+ }
+
+private:
+ QString m_title;
+ QString m_author;
+ QString m_summary;
+ int m_rating = 0;
+};
+
+template <> struct QRangeModel::RowOptions<Book>
+{
+ static constexpr auto rowCategory = QRangeModel::RowCategory::MultiRoleItem;
+};
+
+struct TreeRow;
+using Tree = QList<TreeRow *>;
+
+struct TreeRow
+{
+ QString firstColumn;
+ int secondColumn = 0;
+
+ TreeRow() = default;
+ explicit TreeRow(const QString &first, int second, std::optional<Tree> children = std::nullopt)
+ : firstColumn(first), secondColumn(second), m_children(children)
+ {}
+
+ TreeRow *parentRow() const { return m_parent; }
+ void setParentRow(TreeRow *parent) { m_parent = parent; }
+ const std::optional<Tree> &childRows() const { return m_children; }
+ std::optional<Tree> &childRows() { return m_children; }
+
+private:
+ TreeRow *m_parent;
+ std::optional<Tree> m_children;
+
+ template <size_t I> friend inline decltype(auto) get(const TreeRow &row) {
+ static_assert(I < 2);
+ return false;
+ }
+};
+
+namespace std {
+ template<> struct tuple_size<TreeRow> : integral_constant<int, 2> {};
+ template<> struct tuple_element<0, TreeRow> { using type = QString; };
+ template<> struct tuple_element<1, TreeRow> { using type = int; };
+}
+
+void construct_and_use()
+{
+ //! [construct]
+ std::vector<int> data = {1, 2, 3, 4, 5};
+ QRangeModelAdapter adapter(&data);
+ //! [construct]
+
+ //! [use-model]
+ QListView listView;
+ listView.setModel(adapter.model());
+ //! [use-model]
+}
+
+void get_and_set()
+{
+ QListView tableView;
+ //! [get-range]
+ QList<Book> books = {
+ // ...
+ };
+ QRangeModelAdapter adapter(books);
+ tableView.setModel(adapter.model());
+
+ // show UI and where the user can modify the list
+
+ QList<Book> modifiedBooks = adapter;
+ // or
+ modifiedBooks = adapter.range();
+ //! [get-range]
+
+ //! [set-range]
+ // reset to the original
+ adapter = books;
+ // or
+ adapter.setRange(books);
+ //! [set-range]
+}
+
+void dataAccess()
+{
+ int row = 0;
+ int column = 0;
+ int path = 0;
+ int to = 0;
+ int branch = 0;
+
+ QRangeModelAdapter listAdapter(QList<int>{});
+ //! [list-data]
+ QVariant listItem = listAdapter.data(row);
+ //! [list-data]
+
+ QRangeModelAdapter tableAdapter(QList<QList<int>>{});
+ //! [table-data]
+ QVariant tableItem = tableAdapter.data(row, column);
+ //! [table-data]
+
+ QRangeModelAdapter treeAdapter(QList<TreeRow *>{});
+ //! [tree-data]
+ QVariant treeItem = treeAdapter.data({path, to, branch}, column);
+ //! [tree-data]
+
+ //! [multirole-data]
+ QRangeModelAdapter listOfBooks(QList<Book>{
+ // ~~~
+ });
+ QString bookTitle = listOfBooks.data(0, Book::TitleRole).toString();
+ Book multiRoleItem = listOfBooks.data(0).value<Book>();
+ //! [multirole-data]
+}
+
+void list_access()
+{
+ QListView listView;
+ {
+ //! [list-access]
+ QRangeModelAdapter list(std::vector<int>{1, 2, 3, 4, 5});
+ listView.setModel(list.model());
+
+ int firstValue = list.at(0); // == 1
+ list.at(0) = -1;
+ list.at(1) = list.at(4);
+ //! [list-access]
+ }
+ {
+ //! [list-access-multirole]
+ QRangeModelAdapter books(QList<Book>{
+ // ~~~
+ });
+ Book firstBook = books.at(0);
+ Book newBook = {};
+ books.at(0) = newBook; // dataChanged() emitted
+ //! [list-access-multirole]
+
+ //! [list-access-multirole-member-access]
+ QString title = books.at(0)->title();
+ //! [list-access-multirole-member-access]
+
+ //! [list-access-multirole-write-back]
+ // books.at(0)->setRating(5); - not possible even though 'books' is not const
+ firstBook = books.at(0);
+ firstBook.setRating(5);
+ books.at(0) = firstBook; // dataChanged() emitted
+ //! [list-access-multirole-write-back]
+ }
+}
+
+void table_access()
+{
+ QTableView tableView;
+ {
+ //! [table-item-access]
+ QRangeModelAdapter table(std::vector<std::vector<double>>{
+ {1.0, 2.0, 3.0, 4.0, 5.0},
+ {6.0, 7.0, 8.0, 9.0, 10.0},
+ });
+ tableView.setModel(table.model());
+
+ double value = table.at(0, 2); // value == 3.0
+ table.at(0, 2) = value * 2; // table[0, 2] == 6.0
+ //! [table-item-access]
+
+ //! [table-row-const-access]
+ const auto &constTable = table;
+ const std::vector<double> &topRow = constTable.at(0);
+ //! [table-row-const-access]
+
+ //! [table-row-access]
+ auto lastRow = table.at(table.rowCount() - 1);
+ lastRow = { 6.5, 7.5, 8.0, 9.0, 10 }; // emits dataChanged() for entire row
+ //! [table-row-access]
+ }
+
+ {
+ //! [table-mixed-type-access]
+ QRangeModelAdapter table(std::vector<std::tuple<int, QString>>{
+ // ~~~
+ });
+ int number = table.at(0, 0)->toInt();
+ QString text = table.at(0, 1)->toString();
+ //! [table-mixed-type-access]
+ }
+}
+
+void tree_access()
+{
+ QTreeView treeView;
+
+ //! [tree-row-access]
+ QRangeModelAdapter tree = QRangeModelAdapter(Tree{
+ new TreeRow{"Germany", 357002, Tree{
+ new TreeRow("Bavaria", 70550)
+ },
+ },
+ new TreeRow{"France", 632702},
+ });
+ treeView.setModel(tree.model());
+
+ auto germanyData = tree.at(0);
+ auto bavariaData = tree.at({0, 0});
+ //! [tree-row-access]
+
+ //! [tree-item-access]
+ auto germanyName = tree.at(0, 0);
+ auto bavariaSize = tree.at({0, 0}, 1);
+ //! [tree-item-access]
+
+ //! [tree-row-write]
+ // deletes the old row - tree was moved in
+ tree.at({0, 0}) = new TreeRow{"Berlin", 892};
+ //! [tree-row-write]
+}
+
+void read_only()
+{
+#if 0
+ //! [read-only]
+ const QStringList strings = {"On", "Off"};
+ QRangeModelAdapter adapter(strings);
+ adapter.at(0) = "Undecided"; // compile error: return value of 'at' is const
+ adapter.insertRow(0); // compiler error: requirements not satisfied
+ //! [read-only]
+#endif
+}
+
+void list_iterate()
+{
+ QRangeModelAdapter books(QList<Book>{
+ // ~~~
+ });
+ QListView view;
+ view.setModel(books.model());
+
+ //! [ranged-for-const-list]
+ for (const auto &book : std::as_const(books)) {
+ qDebug() << "The book" << book.title()
+ << "written by" << book.author()
+ << "has" << book.rating() << "stars";
+ }
+ //! [ranged-for-const-list]
+
+ //! [ranged-for-mutable-list]
+ for (auto book : books) {
+ qDebug() << "The book" << book->title()
+ << "written by" << book->author()
+ << "has" << book->rating() << "stars";
+
+ Book copy = book;
+ copy.setRating(copy.rating() + 1);
+ book = copy;
+ }
+ //! [ranged-for-mutable-list]
+}
+
+void table_iterate()
+{
+ //! [ranged-for-const-table]
+ QRangeModelAdapter table(std::vector<std::pair<int, QString>>{
+ // ~~~
+ });
+
+ for (const auto &row : std::as_const(table)) {
+ qDebug() << "Number is" << row->first << "and string is" << row->second;
+ }
+ //! [ranged-for-const-table]
+
+ //! [ranged-for-const-table-items]
+ for (const auto &row : std::as_const(table)) {
+ for (const auto &item : row) {
+ qDebug() << item; // item is a QVariant
+ }
+ }
+ //! [ranged-for-const-table-items]
+
+ //! [ranged-for-mutable-table]
+ for (auto row : table) {
+ qDebug() << "Number is" << row->first << "and string is" << row->second;
+ row = std::make_pair(42, u"forty-two"_s);
+ }
+ //! [ranged-for-mutable-table]
+
+ //! [ranged-for-mutable-table-items]
+ for (auto row : table) {
+ for (auto item : row) {
+ item = 42;
+ }
+ }
+ //! [ranged-for-mutable-table-items]
+}
+
+void tree_iterate()
+{
+ QRangeModelAdapter tree = QRangeModelAdapter(Tree{
+ new TreeRow{"1", 1, Tree{
+ new TreeRow("1.1", 11)
+ },
+ },
+ new TreeRow{"2", 2},
+ });
+
+ static_assert(std::is_same_v<typename decltype(tree)::DataReference::value_type, QVariant>);
+
+ //! [ranged-for-tree]
+ for (auto row : tree) {
+ if (row.hasChildren()) {
+ for (auto child : row.children()) {
+ // ~~~
+ }
+ }
+ }
+ //! [ranged-for-tree]
+}
+
+#endif
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/qnamespace.h b/src/corelib/global/qnamespace.h
index fb37e8df8e4..8332d1fd9bf 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1533,6 +1533,8 @@ namespace Qt {
WhatsThisPropertyRole = 31,
// QRangeModel support for QML's required property var modelData
RangeModelDataRole = 40,
+ // QRangeModelAdapter support for accessing entire multi-role objects
+ RangeModelAdapterRole = 41,
// Reserved
UserRole = 0x0100,
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index e530b28e7f1..69418af4b68 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -2830,6 +2830,7 @@
\omitvalue StatusTipPropertyRole
\omitvalue WhatsThisPropertyRole
\omitvalue RangeModelDataRole
+ \omitvalue RangeModelAdapterRole
\omitvalue StandardItemFlagsRole
\omitvalue FileInfoRole
\omitvalue RemoteObjectsCacheRole
diff --git a/src/corelib/global/qtdeprecationmarkers.h b/src/corelib/global/qtdeprecationmarkers.h
index 6ebeb3afe89..6a6efe81b9f 100644
--- a/src/corelib/global/qtdeprecationmarkers.h
+++ b/src/corelib/global/qtdeprecationmarkers.h
@@ -258,6 +258,14 @@ QT_BEGIN_NAMESPACE
# define QT_DEPRECATED_VERSION_6_14
#endif
+#if QT_WARN_DEPRECATED_UP_TO >= QT_VERSION_CHECK(6, 15, 0)
+# define QT_DEPRECATED_VERSION_X_6_15(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_15 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_15(text)
+# define QT_DEPRECATED_VERSION_6_15
+#endif
+
#define QT_DEPRECATED_VERSION_X_5(minor, text) QT_DEPRECATED_VERSION_X_5_##minor(text)
#define QT_DEPRECATED_VERSION_X(major, minor, text) QT_DEPRECATED_VERSION_X_##major##_##minor(text)
diff --git a/src/corelib/itemmodels/qrangemodel.cpp b/src/corelib/itemmodels/qrangemodel.cpp
index f533605c721..f37812876ea 100644
--- a/src/corelib/itemmodels/qrangemodel.cpp
+++ b/src/corelib/itemmodels/qrangemodel.cpp
@@ -154,7 +154,7 @@ static bool connectPropertiesHelper(const QModelIndex &index, QObject *item, QOb
const QHash<int, QMetaProperty> &properties)
{
if (!item)
- return false;
+ return true;
for (auto &&[role, property] : properties.asKeyValueRange()) {
if (property.hasNotifySignal()) {
if (!Handler(index, item, context, role, property))
@@ -171,7 +171,7 @@ bool QRangeModelImplBase::connectProperty(const QModelIndex &index, QObject *ite
int role, const QMetaProperty &property)
{
if (!item)
- return false;
+ return true; // nothing to do, continue
PropertyChangedHandler handler{index, role};
auto connection = property.enclosingMetaObject()->connect(item, property.notifySignal(),
context, std::move(handler));
@@ -199,7 +199,7 @@ bool QRangeModelImplBase::connectPropertyConst(const QModelIndex &index, QObject
int role, const QMetaProperty &property)
{
if (!item)
- return false;
+ return true; // nothing to do, continue
ConstPropertyChangedHandler handler{index, role};
if (!property.enclosingMetaObject()->connect(item, property.notifySignal(),
context, std::move(handler))) {
@@ -216,6 +216,27 @@ bool QRangeModelImplBase::connectPropertiesConst(const QModelIndex &index, QObje
return connectPropertiesHelper<QRangeModelImplBase::connectPropertyConst>(index, item, context, properties);
}
+namespace QRangeModelDetails
+{
+Q_CORE_EXPORT QVariant qVariantAtIndex(const QModelIndex &index)
+{
+ QModelRoleData result[] = {
+ QModelRoleData{Qt::RangeModelAdapterRole},
+ QModelRoleData{Qt::RangeModelDataRole},
+ QModelRoleData{Qt::DisplayRole},
+ };
+ index.multiData(result);
+ QVariant variant;
+ size_t r = 0;
+ do {
+ variant = result[r].data();
+ ++r;
+ } while (!variant.isValid() && r < std::size(result));
+
+ return variant;
+}
+}
+
/*!
\class QRangeModel
\inmodule QtCore
@@ -1364,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.h b/src/corelib/itemmodels/qrangemodel.h
index d15f40d37a9..b8500f9ae94 100644
--- a/src/corelib/itemmodels/qrangemodel.h
+++ b/src/corelib/itemmodels/qrangemodel.h
@@ -150,6 +150,14 @@ void QRangeModelImplBase::dataChanged(const QModelIndex &from, const QModelIndex
{
m_rangeModel->dataChanged(from, to, roles);
}
+void QRangeModelImplBase::beginResetModel()
+{
+ m_rangeModel->beginResetModel();
+}
+void QRangeModelImplBase::endResetModel()
+{
+ m_rangeModel->endResetModel();
+}
void QRangeModelImplBase::beginInsertColumns(const QModelIndex &parent, int start, int count)
{
m_rangeModel->beginInsertColumns(parent, start, count);
diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h
index 96dacda0260..0233727f848 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>
@@ -741,6 +742,8 @@ namespace QRangeModelDetails
} // namespace QRangeModelDetails
class QRangeModel;
+// forward declare so that we can declare friends
+template <typename, typename, typename> class QRangeModelAdapter;
class QRangeModelImplBase : public QtPrivate::QQuasiVirtualInterface<QRangeModelImplBase>
{
@@ -903,6 +906,8 @@ protected:
inline void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to);
inline void dataChanged(const QModelIndex &from, const QModelIndex &to,
const QList<int> &roles);
+ inline void beginResetModel();
+ inline void endResetModel();
inline void beginInsertColumns(const QModelIndex &parent, int start, int count);
inline void endInsertColumns();
inline void beginRemoveColumns(const QModelIndex &parent, int start, int count);
@@ -1199,75 +1204,49 @@ 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;
+ 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;
+ }
+ });
+ if (!tried) {
const auto roles = this->itemModel().roleNames().keys();
- for (auto &role : roles) {
- if (role == Qt::RangeModelDataRole)
+ QVarLengthArray<QModelRoleData, 16> roleDataArray;
+ roleDataArray.reserve(roles.size());
+ for (auto role : roles) {
+ if (isRangeModelRole(role))
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)>) {
- 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)
- result.insert(role, QRangeModelDetails::value(it));
- }
+ roleDataArray.emplace_back(role);
}
- } 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)
- continue;
- QVariant data = readRole(index, role, QRangeModelDetails::pointerTo(value));
- if (data.isValid())
- result[role] = std::move(data);
- }
+ QModelRoleDataSpan roleDataSpan(roleDataArray);
+ multiData(index, roleDataSpan);
+
+ for (auto &&roleData : std::move(roleDataSpan)) {
+ QVariant data = roleData.data();
+ if (data.isValid())
+ result[roleData.role()] = std::move(data);
}
}
- };
-
- if (index.isValid()) {
- readAt(index, readItemData);
-
- if (!tried) // no multi-role item found
- result = this->itemModel().QAbstractItemModel::itemData(index);
}
return result;
}
@@ -1282,21 +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 (!readModelData(roleData))
roleData.setData(ItemAccess::readRole(value, roleData.role()));
- }
}
} else if constexpr (multi_role()) {
tried = true;
@@ -1325,15 +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 (!readModelData(roleData)) {
roleData.setData(readRole(index, roleData.role(),
QRangeModelDetails::pointerTo(value)));
}
@@ -1342,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 {
@@ -1354,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) {
+ if (isPrimaryRole(role) || isRangeModelRole(role))
roleData.setData(read(value));
- } else {
+ else
roleData.clearData();
- }
}
}
});
@@ -1378,6 +1360,7 @@ public:
if (success) {
Q_EMIT this->dataChanged(index, index,
role == Qt::EditRole || role == Qt::RangeModelDataRole
+ || role == Qt::RangeModelAdapterRole
? QList<int>{} : QList<int>{role});
}
});
@@ -1393,13 +1376,22 @@ public:
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>) {
- // This covers move-only types, but also polymorph types like QObject.
- // We don't support replacing a stored object with another one, as this
- // makes object ownership very messy.
- // fall through to error handling
- } else if constexpr (QRangeModelDetails::is_wrapped<value_type>()) {
- if (QRangeModelDetails::isValid(targetRef)) {
+ // 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;
+ }
+ }
+ // Otherwise we have a move-only or polymorph type. fall through to
+ // error handling.
+ } else if constexpr (isWrapped) {
+ if (QRangeModelDetails::isValid(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>();
@@ -1428,16 +1420,16 @@ public:
if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
- if (role == Qt::RangeModelDataRole)
+ 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)
+ 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)) {
+ && (isPrimaryRole(role) || isRangeModelRole(role))) {
return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
}
} else if constexpr (multi_role::value) {
@@ -1464,8 +1456,7 @@ 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) {
+ } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
return write(target, data);
}
return false;
@@ -1579,7 +1570,7 @@ public:
tried = true;
auto targetCopy = makeCopy(target);
for (auto &&[role, value] : data.asKeyValueRange()) {
- if (role == Qt::RangeModelDataRole)
+ if (isRangeModelRole(role))
continue;
if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
const QByteArray roleName = roleNames.value(role);
@@ -1656,6 +1647,13 @@ public:
return this->itemModel().QAbstractItemModel::roleNames();
}
+ template <typename Fn, std::size_t ...Is>
+ static bool forEachTupleElement(const row_type &row, Fn &&fn, std::index_sequence<Is...>)
+ {
+ using std::get;
+ return (std::forward<Fn>(fn)(QRangeModelDetails::pointerTo(get<Is>(row))) && ...);
+ }
+
template <typename Fn>
bool forEachColumn(const row_type &row, int rowIndex, const QModelIndex &parent, Fn &&fn) const
{
@@ -1664,24 +1662,23 @@ public:
const auto &model = this->itemModel();
if constexpr (one_dimensional_range) {
return fn(model.index(rowIndex, 0, parent), pointerTo(row));
- } else if constexpr (dynamicColumns()) {
+ } else if constexpr (dynamicColumns() || QRangeModelDetails::array_like_v<row_type>) {
int columnIndex = -1;
return std::all_of(begin(row), end(row), [&](const auto &item) {
return fn(model.index(rowIndex, ++columnIndex, parent), pointerTo(item));
});
- } else { // tuple-like
- int columnIndex = -1;
- return std::apply([fn = std::forward<Fn>(fn), &model, rowIndex, &columnIndex, parent]
- (const auto &...item) {
- return (fn(model.index(rowIndex, ++columnIndex, parent), pointerTo(item)) && ...);
- }, row);
+ } else { // tuple-like (but not necessarily std::tuple, so can't use std::apply)
+ int column = -1;
+ return forEachTupleElement(row, [&column, &fn, &model, &rowIndex, &parent](QObject *item){
+ return std::forward<Fn>(fn)(model.index(rowIndex, ++column, parent), item);
+ }, std::make_index_sequence<static_column_count>());
}
}
bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
{
if (!QRangeModelDetails::isValid(row))
- return false;
+ return true; // nothing to do
return forEachColumn(row, rowIndex, parent, [this](const QModelIndex &index, QObject *item) {
if constexpr (isMutable())
return Self::connectProperties(index, item, m_data.context, m_data.properties);
@@ -2370,6 +2367,7 @@ protected:
return that().childRangeImpl(index);
}
+ template <typename, typename, typename> friend class QRangeModelAdapter;
ModelData m_data;
};
@@ -2403,6 +2401,26 @@ public:
: Base(std::forward<Range>(model), std::forward<Protocol>(p), itemModel)
{};
+ void setParentRow(range_type &children, row_ptr parent)
+ {
+ for (auto &&child : children)
+ this->protocol().setParentRow(QRangeModelDetails::refTo(child), parent);
+ resetParentInChildren(&children);
+ }
+
+ void deleteRemovedRows(range_type &range)
+ {
+ deleteRemovedRows(QRangeModelDetails::begin(range), QRangeModelDetails::end(range));
+ }
+
+ bool autoConnectProperties(const QModelIndex &parent) const
+ {
+ auto *children = this->childRange(parent);
+ if (!children)
+ return true;
+ return autoConnectPropertiesRange(QRangeModelDetails::refTo(children), parent);
+ }
+
protected:
QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
{
@@ -2635,9 +2653,11 @@ protected:
return false;
Q_ASSERT(QRangeModelDetails::isValid(row));
const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
- if (!autoConnectPropertiesRange(children,
- this->itemModel().index(rowIndex, 0, parent))) {
- return false;
+ if (QRangeModelDetails::isValid(children)) {
+ if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
+ this->itemModel().index(rowIndex, 0, parent))) {
+ return false;
+ }
}
++rowIndex;
}
diff --git a/src/corelib/itemmodels/qrangemodeladapter.h b/src/corelib/itemmodels/qrangemodeladapter.h
new file mode 100644
index 00000000000..bd3342e6185
--- /dev/null
+++ b/src/corelib/itemmodels/qrangemodeladapter.h
@@ -0,0 +1,1671 @@
+// 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
+
+#ifndef QRANGEMODELADAPTER_H
+#define QRANGEMODELADAPTER_H
+
+#include <QtCore/qrangemodeladapter_impl.h>
+
+QT_BEGIN_NAMESPACE
+
+template <typename Range, typename Protocol = void, typename Model = QRangeModel>
+class QT_TECH_PREVIEW_API QRangeModelAdapter
+{
+ using Impl = QRangeModelDetails::RangeImplementation<Range, Protocol>;
+ using Storage = QRangeModelDetails::AdapterStorage<Model, Impl>;
+
+ Storage storage;
+
+#ifdef Q_QDOC
+ using range_type = Range;
+ using const_row_reference = typename std::iterator_traits<Range>::const_reference;
+ using row_reference = typename std::iterator_traits<Range>::reference;
+#else
+ using range_type = QRangeModelDetails::wrapped_t<Range>;
+ using const_row_reference = typename Impl::const_row_reference;
+ using row_reference = typename Impl::row_reference;
+#endif
+ using range_features = typename QRangeModelDetails::range_traits<range_type>;
+ using row_type = std::remove_reference_t<row_reference>;
+ using row_features = QRangeModelDetails::range_traits<typename Impl::wrapped_row_type>;
+ using row_ptr = typename Impl::wrapped_row_type *;
+ using row_traits = typename Impl::row_traits;
+ using item_type = std::remove_reference_t<typename row_traits::item_type>;
+ using data_type = typename QRangeModelDetails::data_type<item_type>::type;
+ using const_data_type = QRangeModelDetails::asConst_t<data_type>;
+ using protocol_traits = typename Impl::protocol_traits;
+
+ template <typename I> static constexpr bool is_list = I::protocol_traits::is_list;
+ template <typename I> static constexpr bool is_table = I::protocol_traits::is_table;
+ template <typename I> static constexpr bool is_tree = I::protocol_traits::is_tree;
+ template <typename I> static constexpr bool canInsertColumns = I::dynamicColumns()
+ && I::isMutable()
+ && row_features::has_insert;
+ template <typename I> static constexpr bool canRemoveColumns = I::dynamicColumns()
+ && I::isMutable()
+ && row_features::has_erase;
+
+ template <typename I> using if_writable = std::enable_if_t<I::isMutable(), bool>;
+ template <typename I> using if_list = std::enable_if_t<is_list<I>, bool>;
+ template <typename I> using unless_list = std::enable_if_t<!is_list<I>, bool>;
+ template <typename I> using if_table = std::enable_if_t<is_table<I>, bool>;
+ template <typename I> using if_tree = std::enable_if_t<is_tree<I>, bool>;
+ template <typename I> using unless_tree = std::enable_if_t<!is_tree<I>, bool>;
+ template <typename I> using if_flat = std::enable_if_t<is_list<I> || is_table<I>, bool>;
+
+ template <typename I>
+ using if_canInsertRows = std::enable_if_t<I::canInsertRows(), bool>;
+ template <typename I>
+ using if_canRemoveRows = std::enable_if_t<I::canRemoveRows(), bool>;
+ template <typename F>
+ using if_canMoveItems = std::enable_if_t<F::has_rotate || F::has_splice, bool>;
+
+ template <typename I>
+ using if_canInsertColumns = std::enable_if_t<canInsertColumns<I>, bool>;
+ template <typename I>
+ using if_canRemoveColumns = std::enable_if_t<canRemoveColumns<I>, bool>;
+
+ template <typename Row>
+ static constexpr bool is_compatible_row = std::is_convertible_v<Row, const_row_reference>;
+ template <typename Row>
+ using if_compatible_row = std::enable_if_t<is_compatible_row<Row>, bool>;
+
+ template <typename C>
+ static constexpr bool is_compatible_row_range = is_compatible_row<
+ decltype(*std::begin(std::declval<C&>()))
+ >;
+ template <typename C>
+ using if_compatible_row_range = std::enable_if_t<is_compatible_row_range<C>, bool>;
+ template <typename Data>
+ static constexpr bool is_compatible_data = true;
+ // std::is_convertible_v<Data, decltype(*std::begin(std::declval<const_row_reference>()))>;
+ template <typename Data>
+ using if_compatible_data = std::enable_if_t<is_compatible_data<Data>, bool>;
+ template <typename C>
+ static constexpr bool is_compatible_data_range = is_compatible_data<
+ decltype(*std::begin(std::declval<C&>()))
+ >;
+ template <typename C>
+ using if_compatible_data_range = std::enable_if_t<is_compatible_data_range<C>, bool>;
+
+ template <typename R>
+ using if_assignable_range = std::enable_if_t<std::is_assignable_v<range_type, R>, bool>;
+
+ friend class QRangeModel;
+ template <typename T>
+ static constexpr bool is_adapter = QRangeModelDetails::is_any_of<q20::remove_cvref_t<T>,
+ QRangeModelAdapter>::value;
+ template <typename T>
+ using unless_adapter = std::enable_if_t<!is_adapter<T>, bool>;
+
+#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_INTEGRITY)
+ // An adapter on a mutable range can make itself an adapter on a const
+ // version of that same range. To make the constructor for a sub-range
+ // accessible, befriend the mutable version. We can use more
+ // generic pattern matching here, as we only use as input what asConst
+ // might produce as output.
+ template <typename T> static constexpr T asMutable(const T &);
+ template <typename T> static constexpr T *asMutable(const T *);
+ template <template <typename, typename...> typename U, typename T, typename ...Args>
+ static constexpr U<T, Args...> asMutable(const U<const T, Args...> &);
+
+ template <typename T>
+ using asMutable_t = decltype(asMutable(std::declval<T>()));
+ friend class QRangeModelAdapter<asMutable_t<Range>, Protocol, Model>;
+#else
+ template <typename R, typename P, typename M>
+ friend class QRangeModelAdapter;
+#endif
+
+ explicit QRangeModelAdapter(const std::shared_ptr<QRangeModel> &model, const QModelIndex &root,
+ std::in_place_t) // disambiguate from range/protocol c'tor
+ : storage{model, root}
+ {}
+
+ explicit QRangeModelAdapter(QRangeModel *model)
+ : storage(model)
+ {}
+
+public:
+ struct DataReference
+ {
+ using value_type = data_type;
+ using const_value_type = const_data_type;
+ using pointer = QRangeModelDetails::data_pointer_t<const_value_type>;
+
+ explicit DataReference(const QModelIndex &index) noexcept
+ : m_index(index)
+ {
+ Q_ASSERT_X(m_index.isValid(), "QRangeModelAdapter::at", "Index at position is invalid");
+ }
+
+ DataReference(const DataReference &other) = default;
+
+ // reference (not std::reference_wrapper) semantics
+ DataReference &operator=(const DataReference &other)
+ {
+ *this = other.get();
+ return *this;
+ }
+
+ ~DataReference() = default;
+
+ DataReference &operator=(const value_type &value)
+ {
+ constexpr Qt::ItemDataRole dataRole = Qt::RangeModelAdapterRole;
+
+ if (m_index.isValid()) {
+ auto model = const_cast<QAbstractItemModel *>(m_index.model());
+ [[maybe_unused]] bool couldWrite = false;
+ if constexpr (std::is_same_v<q20::remove_cvref_t<value_type>, QVariant>)
+ couldWrite = model->setData(m_index, value, dataRole);
+ else
+ couldWrite = model->setData(m_index, QVariant::fromValue(value), dataRole);
+#ifndef QT_NO_DEBUG
+ if (!couldWrite) {
+ qWarning() << "Writing value of type" << QMetaType::fromType<value_type>().name()
+ << "to role" << dataRole << "at index" << m_index
+ << "of the model failed";
+ }
+ } else {
+ qCritical("Data reference for invalid index, can't write to model");
+#endif
+ }
+ return *this;
+ }
+
+ const_value_type get() const
+ {
+ Q_ASSERT_X(m_index.isValid(), "QRangeModelAdapter::at", "Index at position is invalid");
+ return QRangeModelDetails::dataAtIndex<q20::remove_cvref_t<value_type>>(m_index);
+ }
+
+ operator const_value_type() const
+ {
+ return get();
+ }
+
+ pointer operator->() const
+ {
+ return {get()};
+ }
+
+ bool isValid() const { return m_index.isValid(); }
+
+ private:
+ QModelIndex m_index;
+
+ friend inline bool comparesEqual(const DataReference &lhs, const DataReference &rhs)
+ {
+ return lhs.m_index == rhs.m_index
+ || lhs.get() == rhs.get();
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_NON_NOEXCEPT(DataReference);
+
+ friend inline bool comparesEqual(const DataReference &lhs, const value_type &rhs)
+ {
+ return lhs.get() == rhs;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_NON_NOEXCEPT(DataReference, value_type);
+
+ friend inline void swap(DataReference lhs, DataReference rhs)
+ {
+ const value_type lhsValue = lhs.get();
+ lhs = rhs;
+ rhs = lhsValue; // no point in moving, we have to go through QVariant anyway
+ }
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend inline QDebug operator<<(QDebug dbg, const DataReference &ref)
+ {
+ return dbg << ref.get();
+ }
+#endif
+#ifndef QT_NO_DATASTREAM
+ friend inline QDataStream &operator<<(QDataStream &ds, const DataReference &ref)
+ {
+ return ds << ref.get();
+ }
+ friend inline QDataStream &operator>>(QDataStream &ds, DataReference &ref)
+ {
+ value_type value;
+ ds >> value;
+ ref = value;
+ return ds;
+ }
+#endif
+ };
+ template <typename Iterator, typename Adapter>
+ struct ColumnIteratorBase
+ {
+ using iterator_category = std::random_access_iterator_tag;
+ using difference_type = int;
+
+ ColumnIteratorBase() = default;
+ ColumnIteratorBase(const ColumnIteratorBase &other) = default;
+ ColumnIteratorBase(ColumnIteratorBase &&other) = default;
+ ColumnIteratorBase &operator=(const ColumnIteratorBase &other) = default;
+ ColumnIteratorBase &operator=(ColumnIteratorBase &&other) = default;
+
+ ColumnIteratorBase(const QModelIndex &rowIndex, int column, Adapter *adapter) noexcept
+ : m_rowIndex(rowIndex), m_column(column), m_adapter(adapter)
+ {
+ }
+
+ void swap(ColumnIteratorBase &other) noexcept
+ {
+ using std::swap;
+ swap(m_rowIndex, other.m_rowIndex);
+ swap(m_column, other.m_column);
+ q_ptr_swap(m_adapter, other.m_adapter);
+ }
+
+ friend Iterator &operator++(Iterator &that) noexcept
+ {
+ ++that.m_column;
+ return that;
+ }
+ friend Iterator operator++(Iterator &that, int) noexcept
+ {
+ auto copy = that;
+ ++that;
+ return copy;
+ }
+ friend Iterator operator+(const Iterator &that, difference_type n) noexcept
+ {
+ return {that.m_rowIndex, that.m_column + n, that.m_adapter};
+ }
+ friend Iterator operator+(difference_type n, const Iterator &that) noexcept
+ {
+ return that + n;
+ }
+ friend Iterator &operator+=(Iterator &that, difference_type n) noexcept
+ {
+ that.m_column += n;
+ return that;
+ }
+
+ friend Iterator &operator--(Iterator &that) noexcept
+ {
+ --that.m_column;
+ return that;
+ }
+ friend Iterator operator--(Iterator &that, int) noexcept
+ {
+ auto copy = that;
+ --that;
+ return copy;
+ }
+ friend Iterator operator-(const Iterator &that, difference_type n) noexcept
+ {
+ return {that.m_rowIndex, that.m_column - n, that.m_adapter};
+ }
+ friend Iterator operator-(difference_type n, const Iterator &that) noexcept
+ {
+ return that - n;
+ }
+ friend Iterator &operator-=(Iterator &that, difference_type n) noexcept
+ {
+ that.m_column -= n;
+ return that;
+ }
+
+ friend difference_type operator-(const Iterator &lhs, const Iterator &rhs) noexcept
+ {
+ Q_PRE(lhs.m_rowIndex == rhs.m_rowIndex);
+ Q_PRE(lhs.m_adapter == rhs.m_adapter);
+ return lhs.m_column - rhs.m_column;
+ }
+
+ protected:
+ ~ColumnIteratorBase() = default;
+ QModelIndex m_rowIndex;
+ int m_column = -1;
+ Adapter *m_adapter = nullptr;
+
+ private:
+ friend bool comparesEqual(const Iterator &lhs, const Iterator &rhs)
+ {
+ Q_ASSERT(lhs.m_rowIndex == rhs.m_rowIndex);
+ return lhs.m_column == rhs.m_column;
+ }
+ friend Qt::strong_ordering compareThreeWay(const Iterator &lhs, const Iterator &rhs)
+ {
+ Q_ASSERT(lhs.m_rowIndex == rhs.m_rowIndex);
+ return qCompareThreeWay(lhs.m_column, rhs.m_column);
+ }
+
+ Q_DECLARE_STRONGLY_ORDERED_NON_NOEXCEPT(Iterator)
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend inline QDebug operator<<(QDebug dbg, const Iterator &it)
+ {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ return dbg << "ColumnIterator(" << it.m_rowIndex.siblingAtColumn(it.m_column) << ")";
+ }
+#endif
+ };
+
+ struct ConstColumnIterator : ColumnIteratorBase<ConstColumnIterator, const QRangeModelAdapter>
+ {
+ using Base = ColumnIteratorBase<ConstColumnIterator, const QRangeModelAdapter>;
+ using difference_type = typename Base::difference_type;
+ using value_type = data_type;
+ using reference = const_data_type;
+ using pointer = QRangeModelDetails::data_pointer_t<value_type>;
+
+ using Base::Base;
+ using Base::operator=;
+ ~ConstColumnIterator() = default;
+
+ pointer operator->() const
+ {
+ return pointer{operator*()};
+ }
+
+ reference operator*() const
+ {
+ return std::as_const(this->m_adapter)->at(this->m_rowIndex.row(), this->m_column);
+ }
+
+ reference operator[](difference_type n) const
+ {
+ return *(*this + n);
+ }
+ };
+
+ struct ColumnIterator : ColumnIteratorBase<ColumnIterator, QRangeModelAdapter>
+ {
+ using Base = ColumnIteratorBase<ColumnIterator, QRangeModelAdapter>;
+ using difference_type = typename Base::difference_type;
+ using value_type = DataReference;
+ using reference = DataReference;
+ using pointer = reference;
+
+ using Base::Base;
+ using Base::operator=;
+ ~ColumnIterator() = default;
+
+ operator ConstColumnIterator() const
+ {
+ return ConstColumnIterator{this->m_rowIndex, this->m_column, this->m_adapter};
+ }
+
+ pointer operator->() const
+ {
+ return operator*();
+ }
+
+ reference operator*() const
+ {
+ return reference{this->m_rowIndex.siblingAtColumn(this->m_column)};
+ }
+
+ reference operator[](difference_type n) const
+ {
+ return *(*this + n);
+ }
+ };
+
+ template <typename Reference, typename const_row_type, typename = void>
+ struct RowGetter
+ {
+ const_row_type get() const
+ {
+ using namespace QRangeModelDetails;
+ const Reference *that = static_cast<const Reference *>(this);
+ const auto *impl = that->m_adapter->storage.implementation();
+ auto *childRange = impl->childRange(that->m_index.parent());
+ if constexpr (std::is_convertible_v<const row_type &, const_row_type>) {
+ return *std::next(QRangeModelDetails::begin(childRange), that->m_index.row());
+ } else {
+ const auto &row = *std::next(QRangeModelDetails::begin(childRange),
+ that->m_index.row());
+ return const_row_type{QRangeModelDetails::begin(row), QRangeModelDetails::end(row)};
+ }
+ }
+
+ const_row_type operator->() const
+ {
+ return {get()};
+ }
+
+ operator const_row_type() const
+ {
+ return get();
+ }
+ };
+
+ template <typename Reference, typename const_row_type>
+ struct RowGetter<Reference, const_row_type,
+ std::enable_if_t<std::is_reference_v<const_row_type>>>
+ {
+ const_row_type get() const
+ {
+ using namespace QRangeModelDetails;
+ using QRangeModelDetails::begin;
+
+ const Reference *that = static_cast<const Reference *>(this);
+ const auto *impl = that->m_adapter->storage.implementation();
+ return *std::next(begin(refTo(impl->childRange(that->m_index.parent()))),
+ that->m_index.row());
+ }
+
+ auto operator->() const
+ {
+ return std::addressof(get());
+ }
+
+ operator const_row_type() const
+ {
+ return get();
+ }
+ };
+
+ template <typename Reference, typename const_row_type>
+ struct RowGetter<Reference, const_row_type,
+ std::enable_if_t<std::is_pointer_v<const_row_type>>>
+ {
+ const_row_type get() const
+ {
+ using namespace QRangeModelDetails;
+ using QRangeModelDetails::begin;
+
+ const Reference *that = static_cast<const Reference *>(this);
+ const auto *impl = that->m_adapter->storage.implementation();
+ return *std::next(begin(refTo(impl->childRange(that->m_index.parent()))),
+ that->m_index.row());
+ }
+
+ const_row_type operator->() const
+ {
+ return get();
+ }
+
+ operator const_row_type() const
+ {
+ return get();
+ }
+ };
+
+ template <typename Reference, typename Adapter>
+ struct RowReferenceBase : RowGetter<Reference, QRangeModelDetails::asConstRow_t<row_type>>
+ {
+ using const_iterator = ConstColumnIterator;
+ using size_type = int;
+ using difference_type = int;
+ using const_row_type = QRangeModelDetails::asConstRow_t<row_type>;
+
+ RowReferenceBase(const QModelIndex &index, Adapter *adapter) noexcept
+ : m_index(index), m_adapter(adapter)
+ {}
+
+ template <typename I = Impl, if_tree<I> = true>
+ bool hasChildren() const
+ {
+ return m_adapter->model()->hasChildren(m_index);
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ auto children() const
+ {
+ using ConstRange = QRangeModelDetails::asConst_t<Range>;
+ return QRangeModelAdapter<ConstRange, Protocol, Model>(m_adapter->storage.m_model,
+ m_index, std::in_place);
+ }
+
+ ConstColumnIterator cbegin() const
+ {
+ return ConstColumnIterator{m_index, 0, m_adapter};
+ }
+ ConstColumnIterator cend() const
+ {
+ return ConstColumnIterator{m_index, m_adapter->columnCount(), m_adapter};
+ }
+
+ ConstColumnIterator begin() const { return cbegin(); }
+ ConstColumnIterator end() const { return cend(); }
+
+ size_type size() const
+ {
+ return m_adapter->columnCount();
+ }
+
+ auto at(int column) const
+ {
+ Q_ASSERT(column >= 0 && column < m_adapter->columnCount());
+ return *ConstColumnIterator{m_index, column, m_adapter};
+ }
+
+ auto operator[](int column) const
+ {
+ return at(column);
+ }
+
+ protected:
+ friend struct RowGetter<Reference, const_row_type>;
+ ~RowReferenceBase() = default;
+ QModelIndex m_index;
+ Adapter *m_adapter;
+
+ private:
+ friend bool comparesEqual(const Reference &lhs, const Reference &rhs)
+ {
+ Q_ASSERT(lhs.m_adapter == rhs.m_adapter);
+ return lhs.m_index == rhs.m_index;
+ }
+ friend Qt::strong_ordering compareThreeWay(const Reference &lhs, const Reference &rhs)
+ {
+ Q_ASSERT(lhs.m_adapter == rhs.m_adapter);
+ return qCompareThreeWay(lhs.m_index, rhs.m_index);
+ }
+
+ Q_DECLARE_STRONGLY_ORDERED_NON_NOEXCEPT(Reference)
+
+ friend bool comparesEqual(const Reference &lhs, const row_type &rhs)
+ {
+ return lhs.get() == rhs;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_NON_NOEXCEPT(Reference, row_type)
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend inline QDebug operator<<(QDebug dbg, const Reference &ref)
+ {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ return dbg << "RowReference(" << ref.m_index << ")";
+ }
+#endif
+#ifndef QT_NO_DATASTREAM
+ friend inline QDataStream &operator<<(QDataStream &ds, const Reference &ref)
+ {
+ return ds << ref.get();
+ }
+#endif
+ };
+
+ struct ConstRowReference : RowReferenceBase<ConstRowReference, const QRangeModelAdapter>
+ {
+ using Base = RowReferenceBase<ConstRowReference, const QRangeModelAdapter>;
+ using Base::Base;
+
+ ConstRowReference() = default;
+ ConstRowReference(const ConstRowReference &) = default;
+ ConstRowReference(ConstRowReference &&) = default;
+ ConstRowReference &operator=(const ConstRowReference &) = default;
+ ConstRowReference &operator=(ConstRowReference &&) = default;
+ ~ConstRowReference() = default;
+ };
+
+ struct RowReference : RowReferenceBase<RowReference, QRangeModelAdapter>
+ {
+ using Base = RowReferenceBase<RowReference, QRangeModelAdapter>;
+ using iterator = ColumnIterator;
+ using const_iterator = typename Base::const_iterator;
+ using size_type = typename Base::size_type;
+ using difference_type = typename Base::difference_type;
+ using const_row_type = typename Base::const_row_type;
+
+ using Base::Base;
+ RowReference() = delete;
+ ~RowReference() = default;
+ RowReference(const RowReference &other) = default;
+ RowReference(RowReference &&other) = default;
+
+ // assignment has reference (std::reference_wrapper) semantics
+ RowReference &operator=(const ConstRowReference &other)
+ {
+ *this = other.get();
+ return *this;
+ }
+
+ RowReference &operator=(const RowReference &other)
+ {
+ *this = other.get();
+ return *this;
+ }
+
+ RowReference &operator=(const row_type &other)
+ {
+ assign(other);
+ return *this;
+ }
+
+ RowReference &operator=(row_type &&other)
+ {
+ assign(std::move(other));
+ return *this;
+ }
+
+ operator ConstRowReference() const
+ {
+ return ConstRowReference{this->m_index, this->m_adapter};
+ }
+
+ template <typename ConstRowType = const_row_type,
+ std::enable_if_t<!std::is_same_v<ConstRowType, const row_type &>, bool> = true>
+ RowReference &operator=(const ConstRowType &other)
+ {
+ assign(other);
+ return *this;
+ }
+
+ template <typename T, typename It, typename Sentinel>
+ RowReference &operator=(const QRangeModelDetails::RowView<T, It, Sentinel> &other)
+ {
+ *this = row_type{other.begin(), other.end()};
+ return *this;
+ }
+
+ friend inline void swap(RowReference lhs, RowReference rhs)
+ {
+ auto lhsRow = lhs.get();
+ lhs = rhs.get();
+ rhs = std::move(lhsRow);
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ auto children()
+ {
+ return QRangeModelAdapter(this->m_adapter->storage.m_model, this->m_index,
+ std::in_place);
+ }
+
+ using Base::begin;
+ ColumnIterator begin()
+ {
+ return ColumnIterator{this->m_index, 0, this->m_adapter};
+ }
+
+ using Base::end;
+ ColumnIterator end()
+ {
+ return ColumnIterator{this->m_index, this->m_adapter->columnCount(), this->m_adapter};
+ }
+
+ using Base::at;
+ auto at(int column)
+ {
+ Q_ASSERT(column >= 0 && column < this->m_adapter->columnCount());
+ return *ColumnIterator{this->m_index, column, this->m_adapter};
+ }
+
+ using Base::operator[];
+ auto operator[](int column)
+ {
+ return at(column);
+ }
+
+ private:
+ template <typename RHS>
+ void verifyRows(const row_type &oldRow, const RHS &newRow)
+ {
+ using namespace QRangeModelDetails;
+ if constexpr (test_size<row_type>::value) {
+ // prevent that tables get populated with wrongly sized rows
+ Q_ASSERT_X(Impl::size(newRow) == Impl::size(oldRow),
+ "RowReference::operator=()",
+ "The new row has the wrong size!");
+ }
+
+ if constexpr (is_tree<Impl>) {
+ // we cannot hook invalid rows up to the tree hierarchy
+ Q_ASSERT_X(isValid(newRow),
+ "RowReference::operator=()",
+ "An invalid row can not inserted into a tree!");
+ }
+ }
+
+ template <typename R>
+ void assign(R &&other)
+ {
+ auto *impl = this->m_adapter->storage.implementation();
+ decltype(auto) oldRow = impl->rowData(this->m_index);
+
+ verifyRows(oldRow, other);
+
+ if constexpr (is_tree<Impl>) {
+ using namespace QRangeModelDetails;
+ auto &protocol = impl->protocol();
+ auto *oldParent = protocol.parentRow(refTo(oldRow));
+
+ // the old children will be removed; we don't try to overwrite
+ // them with the new children, we replace them completely
+ if (decltype(auto) oldChildren = protocol.childRows(refTo(oldRow));
+ isValid(oldChildren)) {
+ if (int oldChildCount = this->m_adapter->model()->rowCount(this->m_index)) {
+ impl->beginRemoveRows(this->m_index, 0, oldChildCount - 1);
+ impl->deleteRemovedRows(refTo(oldChildren));
+ // make sure the list is empty before we emit rowsRemoved
+ refTo(oldChildren) = range_type{};
+ impl->endRemoveRows();
+ }
+ }
+
+ if constexpr (protocol_traits::has_deleteRow)
+ protocol.deleteRow(oldRow);
+ oldRow = std::forward<R>(other);
+ if constexpr (protocol_traits::has_setParentRow) {
+ protocol.setParentRow(refTo(oldRow), oldParent);
+ if (decltype(auto) newChildren = protocol.childRows(refTo(oldRow));
+ isValid(newChildren)) {
+ impl->beginInsertRows(this->m_index, 0, Impl::size(refTo(newChildren)) - 1);
+ impl->setParentRow(refTo(newChildren), pointerTo(oldRow));
+ impl->endInsertRows();
+ }
+ }
+ } else {
+ oldRow = std::forward<R>(other);
+ }
+ this->m_adapter->emitDataChanged(this->m_index,
+ this->m_index.siblingAtColumn(this->m_adapter->columnCount() - 1));
+ if constexpr (Impl::itemsAreQObjects) {
+ if (this->m_adapter->model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) {
+ impl->autoConnectPropertiesInRow(oldRow, this->m_index.row(), this->m_index.parent());
+ if constexpr (is_tree<Impl>)
+ impl->autoConnectProperties(this->m_index);
+ }
+
+ }
+ }
+
+#ifndef QT_NO_DATASTREAM
+ friend inline QDataStream &operator>>(QDataStream &ds, RowReference &ref)
+ {
+ row_type value;
+ ds >> value;
+ ref = value;
+ return ds;
+ }
+#endif
+ };
+
+ template <typename Iterator, typename Adapter>
+ struct RowIteratorBase : QRangeModelDetails::ParentIndex<is_tree<Impl>>
+ {
+ using iterator_category = std::random_access_iterator_tag;
+ using difference_type = int;
+
+ RowIteratorBase() = default;
+ RowIteratorBase(const RowIteratorBase &) = default;
+ RowIteratorBase(RowIteratorBase &&) = default;
+ RowIteratorBase &operator=(const RowIteratorBase &) = default;
+ RowIteratorBase &operator=(RowIteratorBase &&) = default;
+
+ RowIteratorBase(int row, const QModelIndex &parent, Adapter *adapter)
+ : QRangeModelDetails::ParentIndex<is_tree<Impl>>{parent}
+ , m_row(row), m_adapter(adapter)
+ {}
+
+ void swap(RowIteratorBase &other) noexcept
+ {
+ using std::swap;
+ swap(m_row, other.m_row);
+ swap(this->m_rootIndex, other.m_rootIndex);
+ q_ptr_swap(m_adapter, other.m_adapter);
+ }
+
+ friend Iterator &operator++(Iterator &that) noexcept
+ {
+ ++that.m_row;
+ return that;
+ }
+ friend Iterator operator++(Iterator &that, int) noexcept
+ {
+ auto copy = that;
+ ++that;
+ return copy;
+ }
+ friend Iterator operator+(const Iterator &that, difference_type n) noexcept
+ {
+ return {that.m_row + n, that.root(), that.m_adapter};
+ }
+ friend Iterator operator+(difference_type n, const Iterator &that) noexcept
+ {
+ return that + n;
+ }
+ friend Iterator &operator+=(Iterator &that, difference_type n) noexcept
+ {
+ that.m_row += n;
+ return that;
+ }
+
+ friend Iterator &operator--(Iterator &that) noexcept
+ {
+ --that.m_row;
+ return that;
+ }
+ friend Iterator operator--(Iterator &that, int) noexcept
+ {
+ auto copy = that;
+ --that;
+ return copy;
+ }
+ friend Iterator operator-(const Iterator &that, difference_type n) noexcept
+ {
+ return {that.m_row - n, that.root(), that.m_adapter};
+ }
+ friend Iterator operator-(difference_type n, const Iterator &that) noexcept
+ {
+ return that - n;
+ }
+ friend Iterator &operator-=(Iterator &that, difference_type n) noexcept
+ {
+ that.m_row -= n;
+ return that;
+ }
+
+ friend difference_type operator-(const Iterator &lhs, const Iterator &rhs) noexcept
+ {
+ return lhs.m_row - rhs.m_row;
+ }
+
+ protected:
+ ~RowIteratorBase() = default;
+ int m_row = -1;
+ Adapter *m_adapter = nullptr;
+
+ private:
+ friend bool comparesEqual(const Iterator &lhs, const Iterator &rhs) noexcept
+ {
+ return lhs.m_row == rhs.m_row && lhs.root() == rhs.root();
+ }
+ friend Qt::strong_ordering compareThreeWay(const Iterator &lhs, const Iterator &rhs) noexcept
+ {
+ if (lhs.root() == rhs.root())
+ return qCompareThreeWay(lhs.m_row, rhs.m_row);
+ return qCompareThreeWay(lhs.root(), rhs.root());
+ }
+
+ Q_DECLARE_STRONGLY_ORDERED(Iterator)
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend inline QDebug operator<<(QDebug dbg, const Iterator &it)
+ {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ return dbg << "RowIterator(" << it.m_row << it.root() << ")";
+ }
+#endif
+ };
+
+public:
+ struct ConstRowIterator : public RowIteratorBase<ConstRowIterator, const QRangeModelAdapter>
+ {
+ using Base = RowIteratorBase<ConstRowIterator, const QRangeModelAdapter>;
+ using Base::Base;
+
+ using difference_type = typename Base::difference_type;
+ using value_type = std::conditional_t<is_list<Impl>,
+ const_data_type,
+ ConstRowReference>;
+ using reference = std::conditional_t<is_list<Impl>,
+ const_data_type,
+ ConstRowReference>;
+ using pointer = std::conditional_t<is_list<Impl>,
+ QRangeModelDetails::data_pointer_t<const_data_type>,
+ ConstRowReference>;
+
+ ConstRowIterator(const ConstRowIterator &other) = default;
+ ConstRowIterator(ConstRowIterator &&other) = default;
+ ConstRowIterator &operator=(const ConstRowIterator &other) = default;
+ ConstRowIterator &operator=(ConstRowIterator &&other) = default;
+ ~ConstRowIterator() = default;
+
+ pointer operator->() const
+ {
+ return pointer{operator*()};
+ }
+
+ reference operator*() const
+ {
+ if constexpr (is_list<Impl>) {
+ return this->m_adapter->at(this->m_row);
+ } else {
+ const QModelIndex index = this->m_adapter->model()->index(this->m_row, 0,
+ this->root());
+ return ConstRowReference{index, this->m_adapter};
+ }
+ }
+
+ reference operator[](difference_type n) const
+ {
+ return *(*this + n);
+ }
+ };
+
+ struct RowIterator : public RowIteratorBase<RowIterator, QRangeModelAdapter>
+ {
+ using Base = RowIteratorBase<RowIterator, QRangeModelAdapter>;
+ using Base::Base;
+
+ using difference_type = typename Base::difference_type;
+ using value_type = std::conditional_t<is_list<Impl>,
+ DataReference,
+ RowReference>;
+ using reference = std::conditional_t<is_list<Impl>,
+ DataReference,
+ RowReference>;
+ using pointer = std::conditional_t<is_list<Impl>,
+ DataReference,
+ RowReference>;
+
+ RowIterator(const RowIterator &other) = default;
+ RowIterator(RowIterator &&other) = default;
+ RowIterator &operator=(const RowIterator &other) = default;
+ RowIterator &operator=(RowIterator &&other) = default;
+ ~RowIterator() = default;
+
+ operator ConstRowIterator() const
+ {
+ return ConstRowIterator{this->m_row, this->root(), this->m_adapter};
+ }
+
+ pointer operator->() const
+ {
+ return pointer{operator*()};
+ }
+
+ reference operator*() const
+ {
+ const QModelIndex index = this->m_adapter->model()->index(this->m_row, 0, this->root());
+ if constexpr (is_list<Impl>) {
+ return reference{index};
+ } else {
+ return reference{index, this->m_adapter};
+ }
+ }
+
+ reference operator[](difference_type n) const
+ {
+ return *(*this + n);
+ }
+ };
+
+ using const_iterator = ConstRowIterator;
+ using iterator = RowIterator;
+
+ template <typename R, typename P,
+ std::enable_if_t<!std::is_void_v<P>, bool> = true>
+ explicit QRangeModelAdapter(R &&range, P &&protocol)
+ : QRangeModelAdapter(new Model(std::forward<R>(range), std::forward<P>(protocol)))
+ {}
+
+ template <typename R, typename P = void,
+ unless_adapter<R> = true,
+ std::enable_if_t<std::is_void_v<P>, bool> = true>
+ explicit QRangeModelAdapter(R &&range)
+ : QRangeModelAdapter(new Model(std::forward<R>(range)))
+ {}
+
+ // compiler-generated copy/move SMF are fine!
+
+ Model *model() const
+ {
+ return storage.m_model.get();
+ }
+
+ const range_type &range() const
+ {
+ return QRangeModelDetails::refTo(storage.implementation()->childRange(storage.root()));
+ }
+
+ Q_IMPLICIT operator const range_type &() const
+ {
+ return range();
+ }
+
+ template <typename NewRange = range_type, if_assignable_range<NewRange> = true>
+ void setRange(NewRange &&newRange)
+ {
+ using namespace QRangeModelDetails;
+
+ auto *impl = storage.implementation();
+ const QModelIndex root = storage.root();
+ const qsizetype newLastRow = qsizetype(Impl::size(refTo(newRange))) - 1;
+ auto *oldRange = impl->childRange(root);
+ const qsizetype oldLastRow = qsizetype(Impl::size(oldRange)) - 1;
+
+ if (!root.isValid()) {
+ impl->beginResetModel();
+ impl->deleteOwnedRows();
+ } else if constexpr (is_tree<Impl>) {
+ if (oldLastRow > 0) {
+ impl->beginRemoveRows(root, 0, model()->rowCount(root) - 1);
+ impl->deleteRemovedRows(refTo(oldRange));
+ impl->endRemoveRows();
+ }
+ if (newLastRow > 0)
+ impl->beginInsertRows(root, 0, newLastRow);
+ } else {
+ Q_ASSERT_X(false, "QRangeModelAdapter::setRange",
+ "Internal error: The root index in a table or list must be invalid.");
+ }
+ refTo(oldRange) = std::forward<NewRange>(newRange);
+ if (!root.isValid()) {
+ impl->endResetModel();
+ } else if constexpr (is_tree<Impl>) {
+ if (newLastRow > 0) {
+ Q_ASSERT(model()->hasChildren(root));
+ // if it was moved, then newRange is now likely to be empty. Get
+ // the inserted row.
+ impl->setParentRow(refTo(impl->childRange(storage.root())),
+ pointerTo(impl->rowData(root)));
+ impl->endInsertRows();
+ }
+ }
+ if constexpr (Impl::itemsAreQObjects) {
+ if (model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) {
+ const auto begin = QRangeModelDetails::begin(refTo(oldRange));
+ const auto end = QRangeModelDetails::end(refTo(oldRange));
+ int rowIndex = 0;
+ for (auto it = begin; it != end; ++it, ++rowIndex)
+ impl->autoConnectPropertiesInRow(*it, rowIndex, root);
+ }
+ }
+ }
+
+ template <typename NewRange = range_type, if_assignable_range<NewRange> = true>
+ QRangeModelAdapter &operator=(NewRange &&newRange)
+ {
+ setRange(std::forward<NewRange>(newRange));
+ return *this;
+ }
+
+ // iterator API
+ ConstRowIterator cbegin() const
+ {
+ return ConstRowIterator{ 0, storage.root(), this };
+ }
+ ConstRowIterator begin() const { return cbegin(); }
+
+ ConstRowIterator cend() const
+ {
+ return ConstRowIterator{ rowCount(), storage.root(), this };
+ }
+ ConstRowIterator end() const { return cend(); }
+
+ template <typename I = Impl, if_writable<I> = true>
+ RowIterator begin()
+ {
+ return RowIterator{ 0, storage.root(), this };
+ }
+
+ template <typename I = Impl, if_writable<I> = true>
+ RowIterator end()
+ {
+ return RowIterator{ rowCount(), storage.root(), this };
+ }
+
+ int size() const
+ {
+ return rowCount();
+ }
+
+ template <typename I = Impl, if_list<I> = true>
+ QModelIndex index(int row) const
+ {
+ return storage->index(row, 0, storage.root());
+ }
+
+ template <typename I = Impl, unless_list<I> = true>
+ QModelIndex index(int row, int column) const
+ {
+ return storage->index(row, column, storage.root());
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ QModelIndex index(QSpan<const int> path, int col) const
+ {
+ Q_PRE(path.size());
+ QModelIndex result = storage.root();
+ auto count = path.size();
+ for (const int r : path) {
+ if (--count)
+ result = storage->index(r, 0, result);
+ else
+ result = storage->index(r, col, result);
+ }
+ return result;
+ }
+
+ int columnCount() const
+ {
+ // all rows and tree branches have the same column count
+ return storage->columnCount({});
+ }
+
+ int rowCount() const
+ {
+ return storage->rowCount(storage.root());
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ int rowCount(int row) const
+ {
+ return storage->rowCount(index(row, 0));
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ int rowCount(QSpan<const int> path) const
+ {
+ return storage->rowCount(index(path, 0));
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ constexpr bool hasChildren(int row) const
+ {
+ return storage.m_model->hasChildren(index(row, 0));
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ constexpr bool hasChildren(QSpan<const int> path) const
+ {
+ return storage.m_model->hasChildren(index(path, 0));
+ }
+
+ template <typename I = Impl, if_list<I> = true>
+ QVariant data(int row) const
+ {
+ return QRangeModelDetails::dataAtIndex<QVariant>(index(row));
+ }
+
+ template <typename I = Impl, if_list<I> = true>
+ QVariant data(int row, int role) const
+ {
+ return QRangeModelDetails::dataAtIndex<QVariant>(index(row), role);
+ }
+
+ template <typename I = Impl, if_list<I> = true, if_writable<I> = true>
+ bool setData(int row, const QVariant &value, int role = Qt::EditRole)
+ {
+ return storage->setData(index(row), value, role);
+ }
+
+ template <typename I = Impl, unless_list<I> = true>
+ QVariant data(int row, int column) const
+ {
+ return QRangeModelDetails::dataAtIndex<QVariant>(index(row, column));
+ }
+
+ template <typename I = Impl, unless_list<I> = true>
+ QVariant data(int row, int column, int role) const
+ {
+ return QRangeModelDetails::dataAtIndex<QVariant>(index(row, column), role);
+ }
+
+ template <typename I = Impl, unless_list<I> = true, if_writable<I> = true>
+ bool setData(int row, int column, const QVariant &value, int role = Qt::EditRole)
+ {
+ return storage->setData(index(row, column), value, role);
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ QVariant data(QSpan<const int> path, int column) const
+ {
+ return QRangeModelDetails::dataAtIndex<QVariant>(index(path, column));
+ }
+
+ template <typename I = Impl, if_tree<I> = true>
+ QVariant data(QSpan<const int> path, int column, int role) const
+ {
+ return QRangeModelDetails::dataAtIndex<QVariant>(index(path, column), role);
+ }
+
+ template <typename I = Impl, if_tree<I> = true, if_writable<I> = true>
+ bool setData(QSpan<const int> path, int column, const QVariant &value, int role = Qt::EditRole)
+ {
+ return storage->setData(index(path, column), value, role);
+ }
+
+ // at/operator[int] for list: returns value at row
+ // if multi-role value: return the entire object
+ template <typename I= Impl, if_list<I> = true>
+ const_data_type at(int row) const
+ {
+ return QRangeModelDetails::dataAtIndex<data_type>(index(row));
+ }
+ template <typename I = Impl, if_list<I> = true>
+ const_data_type operator[](int row) const { return at(row); }
+
+ template <typename I= Impl, if_list<I> = true, if_writable<I> = true>
+ auto at(int row) { return DataReference{this->index(row)}; }
+ template <typename I = Impl, if_list<I> = true, if_writable<I> = true>
+ auto operator[](int row) { return DataReference{this->index(row)}; }
+
+ // at/operator[int] for table or tree: a reference or view of the row
+ template <typename I = Impl, unless_list<I> = true>
+ decltype(auto) at(int row) const
+ {
+ return ConstRowReference{index(row, 0), this}.get();
+ }
+ template <typename I = Impl, unless_list<I> = true>
+ decltype(auto) operator[](int row) const { return at(row); }
+
+ template <typename I = Impl, if_table<I> = true, if_writable<I> = true>
+ decltype(auto) at(int row)
+ {
+ return RowReference{index(row, 0), this};
+ }
+ template <typename I = Impl, if_table<I> = true, if_writable<I> = true>
+ decltype(auto) operator[](int row) { return at(row); }
+
+ // at/operator[int, int] for table: returns value at row/column
+ template <typename I = Impl, unless_list<I> = true>
+ const_data_type at(int row, int column) const
+ {
+ return QRangeModelDetails::dataAtIndex<data_type>(index(row, column));
+ }
+
+#ifdef __cpp_multidimensional_subscript
+ template <typename I = Impl, unless_list<I> = true>
+ const_data_type operator[](int row, int column) const { return at(row, column); }
+#endif
+
+ template <typename I = Impl, unless_list<I> = true, if_writable<I> = true>
+ auto at(int row, int column)
+ {
+ return DataReference{this->index(row, column)};
+ }
+#ifdef __cpp_multidimensional_subscript
+ template <typename I = Impl, unless_list<I> = true, if_writable<I> = true>
+ auto operator[](int row, int column) { return at(row, column); }
+#endif
+
+ // at/operator[int] for tree: return a wrapper that maintains reference to
+ // parent.
+ template <typename I = Impl, if_tree<I> = true, if_writable<I> = true>
+ auto at(int row)
+ {
+ return RowReference{index(row, 0), this};
+ }
+ template <typename I = Impl, if_tree<I> = true, if_writable<I> = true>
+ auto operator[](int row) { return at(row); }
+
+ // at/operator[path] for tree: a reference or view of the row
+ template <typename I = Impl, if_tree<I> = true>
+ decltype(auto) at(QSpan<const int> path) const
+ {
+ return ConstRowReference{index(path, 0), this}.get();
+ }
+ template <typename I = Impl, if_tree<I> = true>
+ decltype(auto) operator[](QSpan<const int> path) const { return at(path); }
+
+ template <typename I = Impl, if_tree<I> = true, if_writable<I> = true>
+ auto at(QSpan<const int> path)
+ {
+ return RowReference{index(path, 0), this};
+ }
+ template <typename I = Impl, if_tree<I> = true, if_writable<I> = true>
+ auto operator[](QSpan<const int> path) { return at(path); }
+
+ // at/operator[path, column] for tree: return value
+ template <typename I = Impl, if_tree<I> = true>
+ const_data_type at(QSpan<const int> path, int column) const
+ {
+ Q_PRE(path.size());
+ return QRangeModelDetails::dataAtIndex<data_type>(index(path, column));
+ }
+
+#ifdef __cpp_multidimensional_subscript
+ template <typename I = Impl, if_tree<I> = true>
+ const_data_type operator[](QSpan<const int> path, int column) const { return at(path, column); }
+#endif
+
+ template <typename I = Impl, if_tree<I> = true, if_writable<I> = true>
+ auto at(QSpan<const int> path, int column)
+ {
+ Q_PRE(path.size());
+ return DataReference{this->index(path, column)};
+ }
+#ifdef __cpp_multidimensional_subscript
+ template <typename I = Impl, if_tree<I> = true, if_writable<I> = true>
+ auto operator[](QSpan<const int> path, int column) { return at(path, column); }
+#endif
+
+ template <typename I = Impl, if_canInsertRows<I> = true>
+ bool insertRow(int before)
+ {
+ return storage.m_model->insertRow(before);
+ }
+
+ template <typename I = Impl, if_canInsertRows<I> = true, if_tree<I> = true>
+ bool insertRow(QSpan<const int> before)
+ {
+ Q_PRE(before.size());
+ return storage.m_model->insertRow(before.back(), this->index(before.first(before.size() - 1), 0));
+ }
+
+ template <typename D = row_type, typename I = Impl,
+ if_canInsertRows<I> = true, if_compatible_row<D> = true>
+ bool insertRow(int before, D &&data)
+ {
+ return insertRowImpl(before, storage.root(), std::forward<D>(data));
+ }
+
+ template <typename D = row_type, typename I = Impl,
+ if_canInsertRows<I> = true, if_compatible_row<D> = true, if_tree<I> = true>
+ bool insertRow(QSpan<const int> before, D &&data)
+ {
+ return insertRowImpl(before, storage.root(), std::forward<D>(data));
+ }
+
+ template <typename C, typename I = Impl,
+ if_canInsertRows<I> = true, if_compatible_row_range<C> = true>
+ bool insertRows(int before, C &&data)
+ {
+ return insertRowsImpl(before, storage.root(), std::forward<C>(data));
+ }
+
+ template <typename C, typename I = Impl,
+ if_canInsertRows<I> = true, if_compatible_row_range<C> = true, if_tree<I> = true>
+ bool insertRows(QSpan<const int> before, C &&data)
+ {
+ return insertRowsImpl(before.back(), this->index(before.first(before.size() - 1), 0),
+ std::forward<C>(data));
+ }
+
+ template <typename I = Impl, if_canRemoveRows<I> = true>
+ bool removeRow(int row)
+ {
+ return removeRows(row, 1);
+ }
+
+ template <typename I = Impl, if_canRemoveRows<I> = true, if_tree<I> = true>
+ bool removeRow(QSpan<const int> path)
+ {
+ return removeRows(path, 1);
+ }
+
+ template <typename I = Impl, if_canRemoveRows<I> = true>
+ bool removeRows(int row, int count)
+ {
+ return storage->removeRows(row, count, storage.root());
+ }
+
+ template <typename I = Impl, if_canRemoveRows<I> = true, if_tree<I> = true>
+ bool removeRows(QSpan<const int> path, int count)
+ {
+ return storage->removeRows(path.back(), count,
+ this->index(path.first(path.size() - 1), 0));
+ }
+
+ template <typename F = range_features, if_canMoveItems<F> = true>
+ bool moveRow(int source, int destination)
+ {
+ return moveRows(source, 1, destination);
+ }
+
+ template <typename F = range_features, if_canMoveItems<F> = true>
+ bool moveRows(int source, int count, int destination)
+ {
+ return storage->moveRows(storage.root(), source, count, storage.root(), destination);
+ }
+
+ template <typename I = Impl, typename F = range_features,
+ if_canMoveItems<F> = true, if_tree<I> = true>
+ bool moveRow(QSpan<const int> source, QSpan<const int> destination)
+ {
+ return moveRows(source, 1, destination);
+ }
+
+ template <typename I = Impl, typename F = range_features,
+ if_canMoveItems<F> = true, if_tree<I> = true>
+ bool moveRows(QSpan<const int> source, int count, QSpan<const int> destination)
+ {
+ return storage->moveRows(this->index(source.first(source.size() - 1), 0),
+ source.back(),
+ count,
+ this->index(destination.first(destination.size() - 1), 0),
+ destination.back());
+ }
+
+ template <typename I = Impl, if_canInsertColumns<I> = true>
+ bool insertColumn(int before)
+ {
+ return storage.m_model->insertColumn(before);
+ }
+
+ template <typename D = row_type, typename I = Impl,
+ if_canInsertColumns<I> = true, if_compatible_data<D> = true>
+ bool insertColumn(int before, D &&data)
+ {
+ return insertColumnImpl(before, storage.root(), std::forward<D>(data));
+ }
+
+ template <typename C, typename I = Impl,
+ if_canInsertColumns<I> = true, if_compatible_data_range<C> = true>
+ bool insertColumns(int before, C &&data)
+ {
+ return insertColumnsImpl(before, storage.root(), std::forward<C>(data));
+ }
+
+ template <typename I = Impl, if_canRemoveColumns<I> = true>
+ bool removeColumn(int column)
+ {
+ return storage.m_model->removeColumn(column);
+ }
+
+ template <typename I = Impl, if_canRemoveColumns<I> = true>
+ bool removeColumns(int column, int count)
+ {
+ return storage->removeColumns(column, count, {});
+ }
+
+ template <typename F = row_features, if_canMoveItems<F> = true>
+ bool moveColumn(int from, int to)
+ {
+ return moveColumns(from, 1, to);
+ }
+
+ template <typename F = row_features, if_canMoveItems<F> = true>
+ bool moveColumns(int from, int count, int to)
+ {
+ return storage->moveColumns(storage.root(), from, count, storage.root(), to);
+ }
+
+ template <typename I = Impl, typename F = row_features,
+ if_canMoveItems<F> = true, if_tree<I> = true>
+ bool moveColumn(QSpan<const int> source, int to)
+ {
+ const QModelIndex parent = this->index(source.first(source.size() - 1), 0);
+ return storage->moveColumns(parent, source.back(), 1, parent, to);
+ }
+
+ template <typename I = Impl, typename F = row_features,
+ if_canMoveItems<F> = true, if_tree<I> = true>
+ bool moveColumns(QSpan<const int> source, int count, int destination)
+ {
+ const QModelIndex parent = this->index(source.first(source.size() - 1), 0);
+ return storage->moveColumns(parent, source.back(), count, parent, destination);
+ }
+
+private:
+ friend inline
+ bool comparesEqual(const QRangeModelAdapter &lhs, const QRangeModelAdapter &rhs) noexcept
+ {
+ return lhs.storage.m_model == rhs.storage.m_model;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QRangeModelAdapter)
+
+ friend inline
+ bool comparesEqual(const QRangeModelAdapter &lhs, const range_type &rhs)
+ {
+ return lhs.range() == rhs;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_NON_NOEXCEPT(QRangeModelAdapter, range_type)
+
+
+ void emitDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+ {
+ Q_EMIT storage.implementation()->dataChanged(topLeft, bottomRight, {});
+ }
+
+ template <typename P>
+ static auto setParentRow(P protocol, row_type &newRow, row_ptr parentRow)
+ -> decltype(protocol.setParentRow(std::declval<row_type&>(), std::declval<row_ptr>()))
+ {
+ return protocol.setParentRow(newRow, parentRow);
+ }
+
+ template <typename ...Args> static constexpr void setParentRow(Args &&...) {}
+
+ template <typename D>
+ bool insertRowImpl(int before, const QModelIndex &parent, D &&data)
+ {
+ return storage.implementation()->doInsertRows(before, 1, parent, [&data, this]
+ (range_type &range, auto parentRow, int row, int count) {
+ Q_UNUSED(this);
+ const auto oldSize = range.size();
+ auto newRow = range.emplace(QRangeModelDetails::pos(range, row), std::forward<D>(data));
+ setParentRow(storage.implementation()->protocol(), *newRow, parentRow);
+ return range.size() == oldSize + count;
+ });
+ }
+
+ template <typename LHS>
+ static auto selfInsertion(LHS *lhs, LHS *rhs) -> decltype(lhs == rhs)
+ {
+ if (lhs == rhs) {
+#ifndef QT_NO_DEBUG
+ qCritical("Inserting data into itself is not supported");
+#endif
+ return true;
+ }
+ return false;
+ }
+ template <typename LHS, typename RHS>
+ static constexpr bool selfInsertion(LHS *, RHS *) { return false; }
+
+ template <typename C>
+ bool insertRowsImpl(int before, const QModelIndex &parent, C &&data)
+ {
+ bool result = false;
+ result = storage->doInsertRows(before, int(std::size(data)), parent, [&data, this]
+ (range_type &range, auto parentRow, int row, int count){
+ Q_UNUSED(parentRow);
+ Q_UNUSED(this);
+ const auto pos = QRangeModelDetails::pos(range, row);
+ const auto oldSize = range.size();
+
+ auto dataRange = [&data]{
+ if constexpr (std::is_rvalue_reference_v<C&&>) {
+ return std::make_pair(
+ std::move_iterator(std::begin(data)),
+ std::move_iterator(std::end(data))
+ );
+ } else {
+ return std::make_pair(std::begin(data), std::end(data));
+ }
+ }();
+
+ if constexpr (range_features::has_insert_range) {
+ if (selfInsertion(&range, &data))
+ return false;
+ auto start = range.insert(pos, dataRange.first, dataRange.second);
+ if constexpr (protocol_traits::has_setParentRow) {
+ while (count) {
+ setParentRow(storage->protocol(), *start, parentRow);
+ ++start;
+ --count;
+ }
+ } else {
+ Q_UNUSED(start);
+ }
+ } else {
+ auto newRow = range.insert(pos, count, row_type{});
+ while (dataRange.first != dataRange.second) {
+ *newRow = *dataRange.first;
+ setParentRow(storage->protocol(), *newRow, parentRow);
+ ++dataRange.first;
+ ++newRow;
+ }
+ }
+ return range.size() == oldSize + count;
+ });
+ return result;
+ }
+
+ template <typename D, typename = void>
+ struct DataFromList {
+ static constexpr auto first(D &data) { return &data; }
+ static constexpr auto next(D &, D *entry) { return entry; }
+ };
+
+ template <typename D>
+ struct DataFromList<D, std::enable_if_t<QRangeModelDetails::range_traits<D>::value>>
+ {
+ static constexpr auto first(D &data) { return std::begin(data); }
+ static constexpr auto next(D &data, typename D::iterator entry)
+ {
+ ++entry;
+ if (entry == std::end(data))
+ entry = first(data);
+ return entry;
+ }
+ };
+
+ template <typename D, typename = void> struct RowFromTable
+ {
+ static constexpr auto first(D &data) { return &data; }
+ static constexpr auto next(D &, D *entry) { return entry; }
+ };
+
+ template <typename D>
+ struct RowFromTable<D, std::enable_if_t<std::conjunction_v<
+ QRangeModelDetails::range_traits<D>,
+ QRangeModelDetails::range_traits<typename D::value_type>
+ >>
+ > : DataFromList<D>
+ {};
+
+ template <typename D>
+ bool insertColumnImpl(int before, const QModelIndex &parent, D data)
+ {
+ auto entry = DataFromList<D>::first(data);
+
+ return storage->doInsertColumns(before, 1, parent, [&entry, &data]
+ (auto &range, auto pos, int count) {
+ const auto oldSize = range.size();
+ range.insert(pos, *entry);
+ entry = DataFromList<D>::next(data, entry);
+ return range.size() == oldSize + count;
+ });
+ }
+
+ template <typename C>
+ bool insertColumnsImpl(int before, const QModelIndex &parent, C data)
+ {
+ bool result = false;
+ auto entries = RowFromTable<C>::first(data);
+ auto begin = std::begin(*entries);
+ auto end = std::end(*entries);
+ result = storage->doInsertColumns(before, int(std::size(*entries)), parent,
+ [&begin, &end, &entries, &data](auto &range, auto pos, int count) {
+ const auto oldSize = range.size();
+ if constexpr (row_features::has_insert_range) {
+ range.insert(pos, begin, end);
+ } else {
+ auto start = range.insert(pos, count, {});
+ std::copy(begin, end, start);
+ }
+ entries = RowFromTable<C>::next(data, entries);
+ begin = std::begin(*entries);
+ end = std::end(*entries);
+ return range.size() == oldSize + count;
+ });
+ return result;
+ }
+};
+
+template <typename Range, typename Protocol>
+QRangeModelAdapter(Range &&, Protocol &&) -> QRangeModelAdapter<Range, Protocol>;
+
+template <typename Range>
+QRangeModelAdapter(Range &&) -> QRangeModelAdapter<Range, void>;
+
+QT_END_NAMESPACE
+
+#endif // QRANGEMODELADAPTER_H
diff --git a/src/corelib/itemmodels/qrangemodeladapter.qdoc b/src/corelib/itemmodels/qrangemodeladapter.qdoc
new file mode 100644
index 00000000000..263bff0dd0c
--- /dev/null
+++ b/src/corelib/itemmodels/qrangemodeladapter.qdoc
@@ -0,0 +1,920 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+// Qt-Security score:insignificant reason:default
+
+#include <QtCore/qrangemodeladapter.h>
+
+/*!
+ \class QRangeModelAdapter
+ \inmodule QtCore
+ \since 6.11
+ \preliminary
+ \ingroup model-view
+ \brief QRangeModelAdapter provides QAbstractItemModel-compliant access to any C++ range.
+ \compares equality
+ \compareswith equality Range
+ \endcompareswith
+
+ QRangeModelAdapter provides a type-safe and structure-aware C++ API around
+ a C++ range and a QRangeModel. Modifications made to the C++ range using
+ the adapter will inform clients of the QRangeModel about the changes. This
+ makes sure that item views are updated, caches are cleaned, and persistent
+ item indexes are invalidated and adapted correctly.
+
+ \section1 Construction and model ownership
+
+ QRangeModelAdapter has to be constructed from a C++ range. As with
+ QRangeModel, the range can be provided by lvalue or rvalue reference, as a
+ reference wrapper, and as a raw or smart pointer.
+
+ \snippet qrangemodeladapter/main.cpp construct
+
+ Constructing the adapter from a range implicitly constructs a QRangeModel
+ instance from that same range. Use model() to get it, and pass it to Qt
+ Widgets or Qt Quick item views as usual.
+
+ \snippet qrangemodeladapter/main.cpp use-model
+
+ The adapter owns the model. QRangeModelAdapter is a value type, so it can be
+ copied and moved. All copies share the same QRangeModel, which will be
+ destroyed when the last copy of the adapter is destroyed.
+
+ If the adapter was created from an lvalue or rvalue reference, then the
+ adapter and model will operate on a copy of the original range object.
+ Otherwise, modifications made through the adapter or model will be written
+ to the original range object. To get the updated range, use the range()
+ function explicitly, or use the implicit conversion of the adapter to a
+ the range.
+
+ \snippet qrangemodeladapter/main.cpp get-range
+
+ To replace the entire range data with data from another (compatible) range,
+ use the setRange() function or the assignment operator.
+
+ \snippet qrangemodeladapter/main.cpp set-range
+
+ \section1 Accessing item data
+
+ The QRangeModelAdapter API provides type-safe read and write access to the
+ range that the model operates on. The adapter API is based on the typical
+ API for C++ containers and ranges, including iterators. To access
+ individual rows and items, use at(), the corresponding subscript
+ \c{operator[]}, or data(). Which overloads of those functions are available
+ depends on the range for which the adapter was constructed.
+
+ \section2 Reading item data as a QVariant
+
+ The data() function always returns a QVariant with the value stored at the
+ specified position and role. In a list, an item can be accessed by a single
+ integer value specifying the row:
+
+ \snippet qrangemodeladapter/main.cpp list-data
+
+ If the range is a table, then items are specified by row and column:
+
+ \snippet qrangemodeladapter/main.cpp table-data
+
+ If the range is a tree, then items are located using a path of rows, and a
+ single column value:
+
+ \snippet qrangemodeladapter/main.cpp tree-data
+
+ Using a single integer as the row provides access to the toplevel tree
+ items.
+
+ \snippet qrangemodeladapter/main.cpp multirole-data
+
+ If no role is specified, then the QVariant will hold the entire item at the
+ position. Use the \l{QVariant::fromValue()} template function to retrieve a
+ copy of the item.
+
+ \section2 Reading and writing using at()
+
+ That the data() function returns a QVariant makes it flexible, but removes
+ type safety. For ranges where all items are of the same type, the at()
+ function provides a type-safe alternative that is more compatible with
+ regular C++ containers. As with data(), at() overloads exist to access an
+ item at a row for lists, at a row/column pair for table, and a path/column
+ pair for trees. However, at() always returns the whole item at the
+ specified position; it's not possible to read an individual role values for
+ an item.
+
+ As expected from a C++ container API, the const overloads of at() (and the
+ corresponding subscript \c{operator[]}) provide immutable access to the
+ value, while the mutable overloads return a reference object that a new
+ value can be assigned to. Note that a QRangeModelAdapter operating on a
+ const range behaves in that respect like a const QRangeModelAdapter. The
+ mutable overloads are removed from the overload set, so the compiler will
+ always select the const version. Trying to call a function that modifies a
+ range will result in a compiler error, even if the adapter itself is
+ mutable:
+
+ \snippet qrangemodeladapter/main.cpp read-only
+
+ The returned reference objects are wrappers that convert implicitly to the
+ underlying type, have a \c{get()} function to explicitly access the
+ underlying value, and an \c{operator->()} that provides direct access to
+ const member functions. However, to prevent accidental data changes that
+ would bypass the QAbstractItemModel notification protocol, those reference
+ objects prevent direct modifications of the items.
+
+ \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
+
+ If the range is represented as a list, then only the overloads taking a row
+ are available.
+
+ \snippet qrangemodeladapter/main.cpp list-access
+
+ The const overload returns the item at that row, while the mutable overload
+ returns a wrapper that implicitly converts to and from the value type of the
+ list.
+
+ \snippet qrangemodeladapter/main.cpp list-access-multirole
+
+ Assign a value to the wrapper to modify the data in the list. The model will
+ emit \l{QAbstractItemModel::}{dataChanged()} for all roles.
+
+ When using the mutable overloads, you can also access the item type's const
+ members using the overloaded arrow operator.
+
+ \snippet qrangemodeladapter/main.cpp list-access-multirole-member-access
+
+ It is not possible to access non-const members of the item. Such
+ modifications would bypass the adapter, which couldn't notify the model
+ about the changes. To modify the value stored in the model, make a copy,
+ modify the properties of the copy, and then write that copy back.
+
+ \snippet qrangemodeladapter/main.cpp list-access-multirole-write-back
+
+ This will make the model emit \l{QAbstractItemModel::}{dataChanged()} for
+ this item, and for all roles.
+
+ If the range is represented as a table, then you can access an individual
+ item by row and columns, using the \l{at(int, int)}{at(row, column)}
+ overload. For trees, that overload gives access to top-level items, while
+ the \l{at(QSpan<const int>, int)}{at(path, column)} overload provides
+ access to items nested within the tree.
+
+ Accessing an individual item in a table or tree is equivalent to accessing
+ an item in a list.
+
+ \snippet qrangemodeladapter/main.cpp table-item-access
+
+ If the range doesn't store all columns using the same data type, then
+ \c{at(row,column)} returns a (a reference wrapper with a) QVariant holding
+ the item.
+
+ \snippet qrangemodeladapter/main.cpp table-mixed-type-access
+
+ \section2 Accessing rows in tables and trees
+
+ For tables and trees, the overloads of at() and subscript \c{operator[]}
+ without the column parameter provide access to the entire row. The value
+ returned by the const overloads will be a reference type that gives access
+ to the row data. If that row holds pointers, then that reference type will
+ be a view of the row, giving access to pointers to const items.
+
+ \section3 Table row access
+
+ The \l{at(int)} overload is still available, but it returns the entire table
+ wor. This makes it possible to work with all the values in the row at once.
+
+ \snippet qrangemodeladapter/main.cpp table-row-const-access
+
+ As with items, the const overload provides direct access to the row type,
+ while the mutable overload returns a wrapper that acts as a reference to the
+ row. The wrapper provides access to const member functions using the
+ overloaded arrow operator. To modify the values, write a modified row type
+ back.
+
+ \snippet qrangemodeladapter/main.cpp table-row-access
+
+ When assigning a new value to the row, then the model emits the
+ \l{QAbstractItemModel::}{dataChanged()} signal for all items in the row,
+ and for all roles.
+
+ \note When using a row type with runtime sizes, such as a \c{std::vector} or
+ a QList, make sure that the new row has the correct size.
+
+ \section3 Tree row access
+
+ Rows in trees are specified by a sequence of integers, one entry for each
+ level in the tree. Note that in the following snippets, the Tree is a range
+ holding raw row pointers, and that the adapter is created with an rvalue
+ reference of that range. This gives the QRangeModel ownership over the row
+ data.
+
+ \snippet qrangemodeladapter/main.cpp tree-row-access
+
+ The overload of \l{at(int)}{at()} taking a single row value provides
+ access to the top-level rows, or items in the top-level rows.
+
+ \snippet qrangemodeladapter/main.cpp tree-item-access
+
+ The basic pattern for accessing rows and items in a tree is identical to
+ accessing rows and items in a table. However, the adapter will make sure
+ that the tree structure is maintained when modifying entire rows.
+
+ \snippet qrangemodeladapter/main.cpp tree-row-write
+
+ In this example, a new row object is created, and assigned to the first
+ child of the first top-level item.
+
+ \section1 Iterator API
+
+ Use begin() and end() to get iterators over the rows of the model, or use
+ ranged-for. If the range is a list of items, dereferencing the iterator
+ will give access to item data.
+
+ \snippet qrangemodeladapter/main.cpp ranged-for-const-list
+
+ As with the const and mutable overloads of at(), a mutable iterator will
+ dereference to a wrapper.
+
+ \snippet qrangemodeladapter/main.cpp ranged-for-mutable-list
+
+ Use the overloaded arrow operator to access const members of the item type,
+ and assign to the wrapper to replace the value.
+
+ It the range is a table or tree, then iterating over the model will give
+ access to the rows.
+
+ \snippet qrangemodeladapter/main.cpp ranged-for-const-table
+
+ Both the const and the mutable iterator will dereference to a wrapper type
+ for the row. This make sure that we can consistently iterate over each
+ column, even if the underlying row type is not a range (e.g. it might be a
+ tuple or gadget).
+
+ \snippet qrangemodeladapter/main.cpp ranged-for-const-table-items
+
+ When iterating over a mutable table we can overwrite the entire row.
+
+ \snippet qrangemodeladapter/main.cpp ranged-for-mutable-table
+
+ The model emits the \l{QAbstractItemModel::}{dataChanged()} signal for
+ all items in all row, and for all roles.
+
+ \snippet qrangemodeladapter/main.cpp ranged-for-mutable-table-items
+
+ Iterating over the mutable rows allows us to modify individual items.
+
+ When iterating over a tree, the row wrapper has two additional member
+ functions, hasChildren() and children(), that allow us to traverse the
+ entire tree using iterators.
+
+ \snippet qrangemodeladapter/main.cpp ranged-for-tree
+
+ The object returned by children() is a QRangeModelAdapter operating on
+ the same model as the callee, but all operations will use the source row
+ index as the parent index.
+
+ \sa QRangeModel
+*/
+
+/*!
+ \typedef QRangeModelAdapter::const_row_reference
+*/
+
+/*!
+ \typedef QRangeModelAdapter::row_reference
+*/
+
+/*!
+ \typedef QRangeModelAdapter::range_type
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> QRangeModelAdapter<Range, Protocol, Model>::QRangeModelAdapter(Range &&range, Protocol &&protocol)
+ \fn template <typename Range, typename Protocol, typename Model> QRangeModelAdapter<Range, Protocol, Model>::QRangeModelAdapter(Range &&range)
+
+ Constructs a QRangeModelAdapter that operates on \a range. For tree ranges,
+ the optional \a protocol will be used for tree traversal.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> bool QRangeModelAdapter<Range, Protocol, Model>::operator==(const QRangeModelAdapter &lhs, const QRangeModelAdapter &rhs)
+
+ \return whether \a lhs is equal to \a rhs. Two adapters are equal if they
+ both hold the same \l{model()}{model} instance.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> bool QRangeModelAdapter<Range, Protocol, Model>::operator!=(const QRangeModelAdapter &lhs, const QRangeModelAdapter &rhs)
+
+ \return whether \a lhs is not equal to \a rhs. Two adapters are equal if
+ they both hold the same \l{model()}{model} instance.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> Model *QRangeModelAdapter<Range, Protocol, Model>::model() const
+
+ \return the QRangeModel instance created by this adapter.
+
+ \sa range(), at()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> const QRangeModelAdapter<Range, Protocol, Model>::range_type &QRangeModelAdapter<Range, Protocol, Model>::range() const
+ \fn template <typename Range, typename Protocol, typename Model> QRangeModelAdapter<Range, Protocol, Model>::operator const QRangeModelAdapter<Range, Protocol, Model>::range_type &() const
+
+ \return a const reference to the range that the model adapter operates on.
+
+ \sa setRange(), at(), model()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(NewRange &&newRange)
+ \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(NewRange &&newRange)
+
+ Assigns \a newRange to the stored range, possibly using move semantics.
+ This function makes the model() emit the \l{QAbstractItemModel::}{modelAboutToBeReset()}
+ and \l{QAbstractItemModel::}{modelReset()} signals.
+
+ \constraints \c Range is mutable, and \a newRange is of a type that can be
+ assigned to \c Range.
+
+ \sa range(), at(), model()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>> QModelIndex QRangeModelAdapter<Range, Protocol, Model>::index(int row) const
+ \overload
+
+ Returns the QModelIndex for \a row.
+
+ \constraints \c Range is a one-dimensional list.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QModelIndex QRangeModelAdapter<Range, Protocol, Model>::index(int row, int column) const
+ \overload
+
+ Returns the QModelIndex for the item at \a row, \a column.
+
+ \constraints \c Range is a table or tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> QModelIndex QRangeModelAdapter<Range, Protocol, Model>::index(QSpan<const int> path, int column) const
+ \overload
+
+ Returns the QModelIndex for the item at \a column for the row in the tree
+ specified by \a path.
+
+ \constraints \c Range is a tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> int QRangeModelAdapter<Range, Protocol, Model>::columnCount() const
+
+ \return the number of columns. This will be one if the \c Range represents a
+ list, otherwise this returns be the number of elements in each row.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> int QRangeModelAdapter<Range, Protocol, Model>::rowCount() const
+ \overload
+
+ \return the number of rows. If the \c Range represents a list or table, then
+ this is the number of rows. For trees, this is the number of top-level rows.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> int QRangeModelAdapter<Range, Protocol, Model>::rowCount(int row) const
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> int QRangeModelAdapter<Range, Protocol, Model>::rowCount(QSpan<const int> row) const
+ \overload
+
+ \return the number of rows under \a row.
+ \constraints \c Range is a tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> int QRangeModelAdapter<Range, Protocol, Model>::hasChildren(int row) const
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> int QRangeModelAdapter<Range, Protocol, Model>::hasChildren(QSpan<const int> row) const
+ \overload
+
+ \return whether there are any rows under \a row.
+ \constraints \c Range is a tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>> QVariant QRangeModelAdapter<Range, Protocol, Model>::data(int row) const
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>> QVariant QRangeModelAdapter<Range, Protocol, Model>::data(int row, int role) const
+
+ \return a QVariant holding the data stored under the given \a role for the
+ item at \a row, or an invalid QVariant if there is no item. If \a role is
+ not specified, then returns a QVariant holding the complete item.
+
+ \constraints \c Range is a list.
+
+ \sa setData(), at()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> bool QRangeModelAdapter<Range, Protocol, Model>::setData(int row, const QVariant &value, int role)
+
+ Sets the \a role data for the item at \a row to \a value.
+
+ Returns \c{true} if successful; otherwise returns \c{false}.
+
+ \constraints \c Range is a mutable list.
+
+ \sa data(), at()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QVariant QRangeModelAdapter<Range, Protocol, Model>::data(int row, int column) const
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QVariant QRangeModelAdapter<Range, Protocol, Model>::data(int row, int column, int role) const
+
+ \return a QVariant holding the data stored under the given \a role for the
+ item referred to by a \a row and \a column, or an invalid QVariant if there
+ is no data stored for that position or role. If \a role is not specified,
+ then returns a QVariant holding the complete item.
+
+ \constraints \c Range is a table or tree.
+
+ \sa setData(), at()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> bool QRangeModelAdapter<Range, Protocol, Model>::setData(int row, int column, const QVariant &value, int role)
+
+ Sets the \a role data for the item referred to by \a row and \a column to
+ \a value.
+
+ Returns \c{true} if successful; otherwise returns \c{false}.
+
+ \constraints \c Range is mutable, and not a list.
+
+ \sa data(), at()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> QVariant QRangeModelAdapter<Range, Protocol, Model>::data(QSpan<const int> path, int column) const
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> QVariant QRangeModelAdapter<Range, Protocol, Model>::data(QSpan<const int> path, int column, int role) const
+
+ \return a QVariant holding the data stored under the given \a role for the
+ item referred to by \a path and \a column, or an invalid QVariant if there
+ is no data stored for that position or role. If \a role is not specified,
+ then returns a QVariant holding the complete item.
+
+ \constraints \c Range is a tree.
+
+ \sa setData(), at()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> bool QRangeModelAdapter<Range, Protocol, Model>::setData(QSpan<const int> path, int column, const QVariant &value, int role)
+
+ Sets the \a role data for the item referred to by \a path and \a column to
+ \a value.
+
+ Returns \c{true} if successful; otherwise returns \c{false}.
+
+ \constraints \c Range is a mutable tree.
+
+ \sa data(), at()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(int row) const
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) const
+
+ \return the value at \a row as the type stored in \c Range.
+
+ \constraints \c Range is a list.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(int row)
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_list<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row)
+
+ \return the value at \a row wrapped into a mutable reference to the type
+ stored in \c Range.
+
+//! [data-ref]
+ \note Modifications to the range will invalidate that reference. To modify
+ the reference, assign a new value to it. Unless the value stored in the
+ \c Range is a pointer, it is not possible to access individual members of
+ the stored value.
+//! [data-ref]
+
+ \constraints \c Range is a mutable list.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QRangeModelAdapter<Range, Protocol, Model>::const_row_reference QRangeModelAdapter<Range, Protocol, Model>::at(int row) const
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QRangeModelAdapter<Range, Protocol, Model>::const_row_reference QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) const
+
+ \return a constant reference to the row at \a row, as stored in \c Range.
+
+ \constraints \c Range is a table or tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> QRangeModelAdapter<Range, Protocol, Model>::row_reference QRangeModelAdapter<Range, Protocol, Model>::at(int row)
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> QRangeModelAdapter<Range, Protocol, Model>::row_reference QRangeModelAdapter<Range, Protocol, Model>::operator[](int row)
+
+ \return a mutable reference to the row at \a row, as stored in \c Range.
+
+ \constraints \c Range is a mutable table, but not a tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(int row)
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row)
+
+ \return a mutable wrapper holding a reference to the tree row specified by \a row.
+
+//! [treerow-ref]
+ To modify the tree row, assign a new value to it. Assigning a new tree row
+ will set the parent the new tree row to be the parent of the old tree row.
+ However, neither the old nor the new tree row must have any child rows. To
+ access the tree row, dereferencing the wrapper using \c{operator*()}, or use
+ \c{operator->()} to access tree row members.
+
+ \note Modifications to the range will invalidate the wrapper.
+//! [treerow-ref]
+
+ \constraints \c Range is a tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(int row, int column) const
+\omit
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row, int column) const
+\endomit
+
+ \return a copy of the value stored as the item specified by \a row and
+ \a column. If the item is a multi-role item, then this returns a copy of
+ the entire item. If the rows in the \c Range store different types at
+ different columns, then the return type will be a QVariant.
+
+ \constraints \c Range is a table or tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(int row, int column)
+\omit
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row, int column)
+\endomit
+
+ \return a mutable reference to the value stored as the item specified by
+ \a row and \a column. If the item is a multi-role item, then this will be
+ a reference to the entire item.
+
+ \include qrangemodeladapter.qdoc data-ref
+
+ \constraints \c Range is a mutable table.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path)
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path)
+
+ \return a mutable wrapper holding a reference to the tree row specified by \a path.
+
+ \include qrangemodeladapter.qdoc treerow-ref
+
+ \constraints \c Range is a tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path, int column) const
+\omit
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path, int column) const
+\endomit
+
+ \return a copy of the value stored as the item specified by \a path and
+ \a column. If the item is a multi-role item, then this returns a copy of
+ the entire item. If the rows in the \c Range store different types at
+ different columns, then the return type will be a QVariant.
+
+ \constraints \c Range is a tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path, int column)
+\omit
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path, int column)
+\endomit
+
+ \return a mutable reference to the value stored as the item specified by
+ \a path and \a column. If the item is a multi-role item, then this will be
+ a reference to the entire item. If the rows in the \c Range store different
+ types at different columns, then the return type will be a QVariant.
+
+ \include qrangemodeladapter.qdoc data-ref
+
+ \constraints \c Range is a mutable tree.
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertRows<I>> bool QRangeModelAdapter<Range, Protocol, Model>::insertRow(int before)
+ \overload
+
+ Inserts a single empty row before the row at \a before, and returns whether
+ the insertion was successful.
+//! [insert-row-appends]
+ If \a before is the same value as rowCount(), then the new row will be appended.
+//! [insert-row-appends]
+
+ \constraints \c Range supports insertion of elements.
+
+ \sa insertRows(), removeRow(), insertColumn()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertRows<I>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::insertRow(QSpan<const int> before)
+ \overload
+
+ Inserts a single empty row before the row at the path specified by \a before,
+ and returns whether the insertion was successful.
+ \include qrangemodeladapter.qdoc insert-row-appends
+
+ \constraints \c Range is a tree that supports insertion of elements.
+
+ \sa insertRows(), removeRow(), insertColumn()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename D, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertRows<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_row<D>> bool QRangeModelAdapter<Range, Protocol, Model>::insertRow(int before, D &&data)
+ \overload
+
+ Inserts a single row constructed from \a data before the row at \a before,
+ and returns whether the insertion was successful.
+ \include qrangemodeladapter.qdoc insert-row-appends
+
+ \constraints \c Range supports insertion of elements, and if
+ a row can be constructed from \a data.
+
+ \sa insertRows(), removeRow(), insertColumn()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename D, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertRows<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_row<D>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::insertRow(QSpan<const int> before, D &&data)
+ \overload
+
+ Inserts a single row constructed from \a data before the row at \a before,
+ and returns whether the insertion was successful.
+ \include qrangemodeladapter.qdoc insert-row-appends
+
+ \constraints \c Range is a tree that supports insertion of elements, and if
+ a row can be constructed from \a data.
+
+ \sa insertRows(), removeRow(), insertColumn()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename C, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertRows<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_row_range<C>> bool QRangeModelAdapter<Range, Protocol, Model>::insertRows(int before, C &&data)
+ \overload
+
+ Inserts rows constructed from the elements in \a data before the row at
+ \a before, and returns whether the insertion was successful.
+ \include qrangemodeladapter.qdoc insert-row-appends
+
+ \constraints \c Range supports insertion of elemnets, and if
+ rows can be constructed from the elements in \a data.
+
+ \sa insertRow(), removeRows(), insertColumns()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename C, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertRows<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_row_range<C>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::insertRows(QSpan<const int> before, C &&data)
+ \overload
+
+ Inserts rows constructed from the elements in \a data before the row at
+ \a before, and returns whether the insertion was successful.
+ \include qrangemodeladapter.qdoc insert-row-appends
+
+ \constraints \c Range is a tree that supports insertion of elements, and if
+ rows can be constructed from the elements in \a data.
+
+ \sa insertRow(), removeRows(), insertColumns()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canRemoveRows<I>> bool QRangeModelAdapter<Range, Protocol, Model>::removeRow(int row)
+ \overload
+
+ Removes the given \a row and returns whether the removal was successful.
+
+ \constraints \c Range supports the removal of elements.
+
+ \sa removeRows(), removeColumn(), insertRow()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canRemoveRows<I>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::removeRow(QSpan<const int> path)
+ \overload
+
+ Removes the row at the given \a path, including all children of that row,
+ and returns whether the removal was successful.
+
+ \constraints \c Range is a tree that supports the removal of elements.
+
+ \sa removeRows(), removeColumn(), insertRow()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canRemoveRows<I>> bool QRangeModelAdapter<Range, Protocol, Model>::removeRows(int row, int count)
+ \overload
+
+ Removes \a count rows starting at \a row, and returns whether the removal
+ was successful.
+
+ \constraints \c Range supports the removal of elements.
+
+ \sa removeRow(), removeColumns(), insertRows()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canRemoveRows<I>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::removeRows(QSpan<const int> path, int count)
+ \overload
+
+ Removes \a count rows starting at the row specified by \a path, and returns
+ whether the removal was successful.
+
+ \constraints \c Range is a tree that supports the removal of elements.
+
+ \sa removeRow(), removeColumns(), insertRows()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>> bool QRangeModelAdapter<Range, Protocol, Model>::moveRow(int source, int destination)
+ \overload
+
+ Moves the row at \a source to the position at \a destination, and returns
+ whether the row was successfully moved.
+
+ \constraints \c Range supports moving of elements.
+
+ \sa insertRow(), removeRow(), moveColumn()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::moveRow(QSpan<const int> source, QSpan<const int> destination)
+ \overload
+
+ Moves the tree branch at \a source to the position at \a destination, and
+ returns whether the branch was successfully moved.
+
+ \constraints \c Range is a tree that supports moving of elements.
+
+ \sa insertRow(), removeRow(), moveColumn()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>> bool QRangeModelAdapter<Range, Protocol, Model>::moveRows(int source, int count, int destination)
+ \overload
+
+ Moves \a count rows starting at \a source to the position at \a destination,
+ and returns whether the rows were successfully moved.
+
+ \constraints \c Range supports moving of elements.
+
+ \sa insertRows(), removeRows(), moveColumns()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::moveRows(QSpan<const int> source, int count, QSpan<const int> destination)
+ \overload
+
+ Moves \a count tree branches starting at \a source to the position at
+ \a destination, and returns whether the rows were successfully moved.
+
+ \constraints \c Range is a tree that supports moving of elements.
+
+ \sa insertRows(), removeRows(), moveColumns()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumn(int before)
+ \overload
+
+ Inserts a single empty column before the column specified by \a before into
+ all rows, and returns whether the insertion was successful.
+//! [insert-column-appends]
+ If \a before is the same value as columnCount(), then the column will be
+ appended to each row.
+//! [insert-column-appends]
+
+ \constraints \c Range has rows that support insertion of elements.
+
+ \sa removeColumn(), insertColumns(), insertRow()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename D, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_data<D>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumn(int before, D &&data)
+ \overload
+
+ Inserts a single column constructed from \a data before the column specified
+ by \a before into all rows, and returns whether the insertion was successful.
+//! [insert-column-appends]
+ If \a before is the same value as columnCount(), then the column will be
+ appended to each row.
+//! [insert-column-appends]
+
+ If \a data is a single value, then the new entry in all rows will be constructed
+ from that single value.
+
+ If \a data is a container, then the elements in that container will be used
+ sequentially to construct the column for each subsequent row. If there are
+ fewer elements in \a data than there are rows, then function wraps around
+ and starts again from the first element.
+
+ \code
+ \endcode
+
+ \constraints \c Range has rows that support insertion of elements, and the
+ elements can be constructed from the entries in \a data.
+
+ \sa removeColumn(), insertColumns(), insertRow()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename C, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_data_range<C>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumns(int before, C &&data)
+
+ Inserts columns constructed from the elements in \a data before the column
+ specified by \a before into all rows, and returns whether the insertion was
+ successful.
+//! [insert-column-appends]
+ If \a before is the same value as columnCount(), then the column will be
+ appended to each row.
+//! [insert-column-appends]
+
+ If the elements in \a data are values, then the new entries in all rows will
+ be constructed from those values.
+
+ If the elements in \a data are containers, then the entries in the outer
+ container will be used sequentially to construct the new entries for each
+ subsequent row. If there are fewer elements in \a data than there are rows,
+ then the function wraps around and starts again from the first element.
+
+ \code
+ \endcode
+
+ \constraints \c Range has rows that support insertion of elements, and the
+ elements can be constructed from the entries in \a data.
+
+ \sa removeColumns(), insertColumn(), insertRows()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canRemoveColumns<I>> bool QRangeModelAdapter<Range, Protocol, Model>::removeColumn(int column)
+
+ Removes the given \a column from each row, and returns whether the removal
+ was successful.
+
+ \constraints \c Range has rows that support removal of elements.
+
+ \sa insertColumn(), removeColumns(), removeRow()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canRemoveColumns<I>> bool QRangeModelAdapter<Range, Protocol, Model>::removeColumns(int column, int count)
+
+ Removes \a count columns starting by the given \a column from each row, and
+ returns whether the removal was successful.
+
+ \constraints \c Range has rows that support removal of elements.
+
+ \sa insertColumns(), removeColumn(), removeRow()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>> bool QRangeModelAdapter<Range, Protocol, Model>::moveColumn(int from, int to)
+
+ Moves the column at \a from to the column at \a to, and returns whether the
+ column was successfully moved.
+
+ \constraints \c Range has rows that support moving of elements.
+
+ \sa insertColumn(), removeColumn(), moveColumns(), moveRow()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>> bool QRangeModelAdapter<Range, Protocol, Model>::moveColumns(int from, int count, int to)
+
+ Moves \a count columns starting at \a from to the position at \a to, and
+ returns whether the columns were successfully moved.
+
+ \constraints \c Range has rows that support moving of elements.
+
+ \sa insertColumns(), removeColumns(), moveColumn(), moveRows()
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::moveColumn(QSpan<const int> source, int to)
+ \internal Not possible to create a tree from a row type that can rotate/splice?
+*/
+
+/*!
+ \fn template <typename Range, typename Protocol, typename Model> template <typename I, typename F, QRangeModelAdapter<Range, Protocol, Model>::if_canMoveItems<F>, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> bool QRangeModelAdapter<Range, Protocol, Model>::moveColumns(QSpan<const int> source, int count, int to)
+ \internal Not possible to create a tree from a row type that can rotate/splice?
+*/
diff --git a/src/corelib/itemmodels/qrangemodeladapter_impl.h b/src/corelib/itemmodels/qrangemodeladapter_impl.h
new file mode 100644
index 00000000000..ad1dd152b22
--- /dev/null
+++ b/src/corelib/itemmodels/qrangemodeladapter_impl.h
@@ -0,0 +1,402 @@
+// 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
+
+#ifndef QRANGEMODELADAPTER_IMPL_H
+#define QRANGEMODELADAPTER_IMPL_H
+
+#ifndef Q_QDOC
+
+#ifndef QRANGEMODELADAPTER_H
+#error Do not include qrangemodeladapter_impl.h directly
+#endif
+
+#if 0
+#pragma qt_sync_skip_header_check
+#pragma qt_sync_stop_processing
+#endif
+
+#include <QtCore/qrangemodel.h>
+#include <QtCore/qspan.h>
+#include <set>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+namespace QRangeModelDetails
+{
+template <typename Range, typename Protocol>
+using RangeImplementation = std::conditional_t<std::is_void_v<Protocol>,
+ std::conditional_t<is_tree_range<Range>::value,
+ QGenericTreeItemModelImpl<Range, DefaultTreeProtocol<Range>>,
+ QGenericTableItemModelImpl<Range>
+ >,
+ QGenericTreeItemModelImpl<Range, Protocol>
+ >;
+
+// we can't use wrapped_t, we only want to unpack smart pointers, and maintain
+// the pointer nature of the type.
+template <typename T, typename = void>
+struct data_type { using type = T; };
+template <>
+struct data_type<void> { using type = QVariant; };
+
+// pointer types of iterators use QtPrivate::ArrowProxy if the type does not
+// provide operator->() (or is a pointer).
+template <typename T, typename = void> struct test_pointerAccess : std::false_type {};
+template <typename T> struct test_pointerAccess<T *> : std::true_type {};
+template <typename T>
+struct test_pointerAccess<T, std::void_t<decltype(std::declval<T>().operator->())>>
+ : std::true_type
+{};
+
+template <typename T>
+using data_pointer_t = std::conditional_t<test_pointerAccess<T>::value,
+ T, QtPrivate::ArrowProxy<T>>;
+
+// Helpers to make a type const "in depth", taking into account raw pointers
+// and wrapping types, like smart pointers and std::reference_wrapper.
+
+// We need to return data by value, not by reference, as we might only have
+// temporary values (i.e. a QVariant returned by QAIM::data).
+template <typename T, typename = void> struct AsConstData { using type = T; };
+template <typename T> struct AsConstData<const T &> { using type = T; };
+template <typename T> struct AsConstData<T *> { using type = const T *; };
+template <template <typename> typename U, typename T>
+struct AsConstData<U<T>, std::enable_if_t<is_any_shared_ptr<U<T>>::value>>
+{ using type = U<const T>; };
+template <typename T> struct AsConstData<std::reference_wrapper<T>>
+{ using type = std::reference_wrapper<const T>; };
+
+template <typename T> using asConst_t = typename AsConstData<T>::type;
+
+// Rows get wrapped into a "view", as a begin/end iterator/sentinel pair.
+// The iterator dereferences to the const version of the value returned by
+// the underlying iterator.
+// Could be replaced with std::views::sub_range in C++ 20.
+template <typename const_row_type, typename Iterator, typename Sentinel>
+struct RowView
+{
+ // this is similar to C++23's std::basic_const_iterator, but we don't want
+ // to convert to the underlying const_iterator.
+ struct iterator
+ {
+ using value_type = asConst_t<typename Iterator::value_type>;
+ using difference_type = typename Iterator::difference_type;
+ using pointer = QRangeModelDetails::data_pointer_t<value_type>;
+ using reference = value_type;
+ using const_reference = value_type;
+ using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
+
+ template <typename I, typename Category>
+ static constexpr bool is_atLeast = std::is_base_of_v<Category,
+ typename std::iterator_traits<I>::iterator_category>;
+ template <typename I, typename Category>
+ using if_atLeast = std::enable_if_t<is_atLeast<I, Category>, bool>;
+
+ reference operator*() const { return *m_it; }
+ pointer operator->() const { return operator*(); }
+
+ // QRM requires at least forward_iterator, so we provide both post- and
+ // prefix increment unconditionally
+ friend constexpr iterator &operator++(iterator &it)
+ noexcept(noexcept(++std::declval<Iterator&>()))
+ {
+ ++it.m_it;
+ return it;
+ }
+ friend constexpr iterator operator++(iterator &it, int)
+ noexcept(noexcept(std::declval<Iterator&>()++))
+ {
+ iterator copy = it;
+ ++copy.m_it;
+ return copy;
+ }
+
+ template <typename I = Iterator, if_atLeast<I, std::bidirectional_iterator_tag> = true>
+ friend constexpr iterator &operator--(iterator &it)
+ noexcept(noexcept(--std::declval<I&>()))
+ {
+ --it.m_it;
+ return it;
+ }
+ template <typename I = Iterator, if_atLeast<I, std::bidirectional_iterator_tag> = true>
+ friend constexpr iterator operator--(iterator &it, int)
+ noexcept(noexcept(std::declval<I&>()--))
+ {
+ iterator copy = it;
+ --it.m_it;
+ return copy;
+ }
+
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr iterator &operator+=(iterator &it, difference_type n)
+ noexcept(noexcept(std::declval<I&>() += 1))
+ {
+ it.m_it += n;
+ return it;
+ }
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr iterator &operator-=(iterator &it, difference_type n)
+ noexcept(noexcept(std::declval<I&>() -= 1))
+ {
+ it.m_it -= n;
+ return it;
+ }
+
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr iterator operator+(const iterator &it, difference_type n)
+ noexcept(noexcept(std::declval<I&>() + 1))
+ {
+ iterator copy = it;
+ copy.m_it += n;
+ return copy;
+ }
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr iterator operator+(difference_type n, const iterator &it)
+ noexcept(noexcept(1 + std::declval<I&>()))
+ {
+ return it + n;
+ }
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr iterator operator-(const iterator &it, difference_type n)
+ noexcept(noexcept(std::declval<I&>() - 1))
+ {
+ iterator copy = it;
+ copy.m_it = it.m_it - n;
+ return copy;
+ }
+
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ constexpr reference operator[](difference_type n) const
+ noexcept(noexcept(I::operator[]()))
+ {
+ return m_it[n];
+ }
+
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr difference_type operator-(const iterator &lhs, const iterator &rhs)
+ noexcept(noexcept(std::declval<I&>() - std::declval<I&>()))
+ {
+ return lhs.m_it - rhs.m_it;
+ }
+
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr bool operator<(const iterator &lhs, const iterator &rhs)
+ noexcept(noexcept(std::declval<I&>() < std::declval<I&>()))
+ {
+ return lhs.m_it < rhs.m_it;
+ }
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr bool operator<=(const iterator &lhs, const iterator &rhs)
+ noexcept(noexcept(std::declval<I&>() <= std::declval<I&>()))
+ {
+ return lhs.m_it <= rhs.m_it;
+ }
+
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr bool operator>(const iterator &lhs, const iterator &rhs)
+ noexcept(noexcept(std::declval<I&>() > std::declval<I&>()))
+ {
+ return lhs.m_it > rhs.m_it;
+ }
+ template <typename I = Iterator, if_atLeast<I, std::random_access_iterator_tag> = true>
+ friend constexpr bool operator>=(const iterator &lhs, const iterator &rhs)
+ noexcept(noexcept(std::declval<I&>() >= std::declval<I&>()))
+ {
+ return lhs.m_it >= rhs.m_it;
+ }
+
+ // This would implement the P2836R1 fix from std::basic_const_iterator,
+ // but a const_iterator on a range<pointer> would again allow us to
+ // mutate the pointed-to object, which is exactly what we want to
+ // prevent.
+ /*
+ template <typename CI, std::enable_if_t<std::is_convertible_v<const Iterator &, CI>, bool> = true>
+ operator CI() const
+ {
+ return CI{m_it};
+ }
+
+ template <typename CI, std::enable_if_t<std::is_convertible_v<Iterator, CI>, bool> = true>
+ operator CI() &&
+ {
+ return CI{std::move(m_it)};
+ }
+ */
+
+ friend bool comparesEqual(const iterator &lhs, const iterator &rhs) noexcept
+ {
+ return lhs.m_it == rhs.m_it;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(iterator)
+
+ Iterator m_it;
+ };
+
+ using value_type = typename iterator::value_type;
+ using difference_type = typename iterator::difference_type;
+
+ friend bool comparesEqual(const RowView &lhs, const RowView &rhs) noexcept
+ {
+ return lhs.m_begin == rhs.m_begin;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(RowView)
+
+ template <typename RHS>
+ bool operator==(const RHS &rhs) const noexcept
+ {
+ return m_begin == QRangeModelDetails::begin(rhs);
+ }
+ template <typename RHS>
+ bool operator!=(const RHS &rhs) const noexcept
+ {
+ return !operator==(rhs);
+ }
+
+ value_type at(difference_type n) const { return *std::next(m_begin, n); }
+
+ iterator begin() const { return iterator{m_begin}; }
+ iterator end() const { return iterator{m_end}; }
+
+ Iterator m_begin;
+ Sentinel m_end;
+};
+
+// Const-in-depth mapping for row types. We do store row types, and they might
+// be move-only, so we return them by const reference.
+template <typename T, typename = void> struct AsConstRow { using type = const T &; };
+// Otherwise the mapping for basic row types is the same as for data.
+template <typename T> struct AsConstRow<T *> : AsConstData<T *> {};
+template <template <typename> typename U, typename T>
+struct AsConstRow<U<T>, std::enable_if_t<is_any_shared_ptr<U<T>>::value>> : AsConstData<U<T>> {};
+template <typename T> struct AsConstRow<std::reference_wrapper<T>>
+ : AsConstData<std::reference_wrapper<T>> {};
+
+template <typename T> using if_range = std::enable_if_t<is_range_v<T>, bool>;
+// If the row type is a range, then we assume that the first type is the
+// element type.
+template <template <typename, typename ...> typename R, typename T, typename ...Args>
+struct AsConstRow<R<T, Args...>, if_range<R<T, Args...>>>
+{
+ using type = const R<T, Args...> &;
+};
+
+// specialize for range of pointers and smart pointers
+template <template <typename, typename ...> typename R, typename T, typename ...Args>
+struct AsConstRow<R<T *, Args...>, if_range<R<T *, Args...>>>
+{
+ using row_type = R<T, Args...>;
+ using const_iterator = typename row_type::const_iterator;
+ using const_row_type = R<asConst_t<T>>;
+ using type = RowView<const_row_type, const_iterator, const_iterator>;
+};
+
+template <template <typename, typename ...> typename R, typename T, typename ...Args>
+struct AsConstRow<R<T, Args...>,
+ std::enable_if_t<std::conjunction_v<is_range<R<T, Args...>>, is_any_shared_ptr<T>>>
+>
+{
+ using row_type = R<T, Args...>;
+ using const_iterator = typename row_type::const_iterator;
+ using const_row_type = R<asConst_t<T>>;
+ using type = RowView<const_row_type, const_iterator, const_iterator>;
+};
+
+template <typename T>
+using asConstRow_t = typename AsConstRow<T>::type;
+
+Q_CORE_EXPORT QVariant qVariantAtIndex(const QModelIndex &index);
+
+template <typename Type>
+static inline Type dataAtIndex(const QModelIndex &index)
+{
+ Q_ASSERT_X(index.isValid(), "QRangeModelAdapter::dataAtIndex", "Index at position is invalid");
+ QVariant variant = qVariantAtIndex(index);
+
+ if constexpr (std::is_same_v<QVariant, Type>)
+ return variant;
+ else
+ return variant.value<Type>();
+}
+
+template <typename Type>
+static inline Type dataAtIndex(const QModelIndex &index, int role)
+{
+ Q_ASSERT_X(index.isValid(), "QRangeModelAdapter::dataAtIndex", "Index at position is invalid");
+ QVariant variant = index.data(role);
+
+ if constexpr (std::is_same_v<QVariant, Type>)
+ return variant;
+ else
+ return variant.value<Type>();
+}
+
+template <bool isTree = false>
+struct ParentIndex
+{
+ ParentIndex(const QModelIndex &dummy = {}) { Q_ASSERT(!dummy.isValid()); }
+ QModelIndex root() const { return {}; }
+};
+
+template <>
+struct ParentIndex<true>
+{
+ const QModelIndex m_rootIndex;
+ QModelIndex root() const { return m_rootIndex; }
+};
+
+template <typename Model, typename Impl>
+struct AdapterStorage : ParentIndex<Impl::protocol_traits::is_tree>
+{
+ // If it is, then we can shortcut the model and operate on the container.
+ // Otherwise we have to go through the model's vtable. For now, this is always
+ // the case.
+ static constexpr bool isRangeModel = std::is_same_v<Model, QRangeModel>;
+ static_assert(isRangeModel, "The model must be a QRangeModel (not a subclass).");
+ std::shared_ptr<QRangeModel> m_model;
+
+ template <typename I = Impl, std::enable_if_t<I::protocol_traits::is_tree, bool> = true>
+ explicit AdapterStorage(const std::shared_ptr<QRangeModel> &model, const QModelIndex &root)
+ : ParentIndex<Impl::protocol_traits::is_tree>{root}, m_model(model)
+ {
+ }
+
+ explicit AdapterStorage(Model *model)
+ : m_model{model}
+ {}
+
+ const Impl *implementation() const
+ {
+ return static_cast<const Impl *>(QRangeModelImplBase::getImplementation(m_model.get()));
+ }
+
+ Impl *implementation()
+ {
+ return static_cast<Impl *>(QRangeModelImplBase::getImplementation(m_model.get()));
+ }
+
+ auto *operator->()
+ {
+ if constexpr (isRangeModel)
+ return implementation();
+ else
+ return m_model.get();
+ }
+
+ const auto *operator->() const
+ {
+ if constexpr (isRangeModel)
+ return implementation();
+ else
+ return m_model.get();
+ }
+};
+
+} // QRangeModelDetails
+
+QT_END_NAMESPACE
+
+#endif // Q_QDOC
+
+#endif // QRANGEMODELADAPTER_IMPL_H
diff --git a/src/corelib/kernel/qassociativeiterable.cpp b/src/corelib/kernel/qassociativeiterable.cpp
index 7c85ce2c10a..8a2fc63c441 100644
--- a/src/corelib/kernel/qassociativeiterable.cpp
+++ b/src/corelib/kernel/qassociativeiterable.cpp
@@ -7,7 +7,7 @@
QT_BEGIN_NAMESPACE
-#if QT_DEPRECATED_SINCE(6, 13)
+#if QT_DEPRECATED_SINCE(6, 15)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
@@ -107,7 +107,7 @@ QVariantConstPointer QAssociativeConstIterator::operator->() const
/*!
\class QAssociativeIterable
- \deprecated [6.13] Use QMetaAssociation::Iterable instead.
+ \deprecated [6.15] Use QMetaAssociation::Iterable instead.
\since 5.2
\inmodule QtCore
\brief The QAssociativeIterable class is an iterable interface for an associative container in a QVariant.
@@ -273,7 +273,7 @@ void QAssociativeIterable::setValue(const QVariant &key, const QVariant &mapped)
/*!
\typealias QAssociativeIterable::const_iterator
- \deprecated [6.13] Use QMetaAssociation::Iterable::ConstIterator instead.
+ \deprecated [6.15] Use QMetaAssociation::Iterable::ConstIterator instead.
\inmodule QtCore
\brief The QAssociativeIterable::const_iterator allows iteration over a container in a QVariant.
@@ -286,7 +286,7 @@ void QAssociativeIterable::setValue(const QVariant &key, const QVariant &mapped)
/*!
\typealias QAssociativeIterable::iterator
\since 6.0
- \deprecated [6.13] Use QMetaAssociation::Iterable::Iterator instead.
+ \deprecated [6.15] Use QMetaAssociation::Iterable::Iterator instead.
\inmodule QtCore
\brief The QAssociativeIterable::iterator allows iteration over a container in a QVariant.
@@ -297,6 +297,6 @@ void QAssociativeIterable::setValue(const QVariant &key, const QVariant &mapped)
*/
QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 13)
+#endif // QT_DEPRECATED_SINCE(6, 15)
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qassociativeiterable.h b/src/corelib/kernel/qassociativeiterable.h
index 02038133fa5..39f66d45fa0 100644
--- a/src/corelib/kernel/qassociativeiterable.h
+++ b/src/corelib/kernel/qassociativeiterable.h
@@ -9,12 +9,20 @@
QT_BEGIN_NAMESPACE
-#if QT_DEPRECATED_SINCE(6, 13)
+#if QT_DEPRECATED_SINCE(6, 15)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU < 1300
+ // GCC < 13 doesn't accept both deprecation and visibility on the same class
+ #define QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15(text) Q_CORE_EXPORT
+#else
+ #define QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15(text) \
+ Q_CORE_EXPORT QT_DEPRECATED_VERSION_X_6_15(text)
+#endif
+
class
-QT_DEPRECATED_VERSION_X_6_13("Use QMetaAssociation::Iterable::Iterator instead.")
+QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15("Use QMetaAssociation::Iterable::Iterator instead.")
QAssociativeIterator : public QIterator<QMetaAssociation>
{
public:
@@ -27,15 +35,15 @@ public:
: QIterator(std::move(it))
{}
- Q_CORE_EXPORT QVariant key() const;
- Q_CORE_EXPORT QVariantRef<QAssociativeIterator> value() const;
+ QVariant key() const;
+ QVariantRef<QAssociativeIterator> value() const;
- Q_CORE_EXPORT QVariantRef<QAssociativeIterator> operator*() const;
- Q_CORE_EXPORT QVariantPointer<QAssociativeIterator> operator->() const;
+ QVariantRef<QAssociativeIterator> operator*() const;
+ QVariantPointer<QAssociativeIterator> operator->() const;
};
class
-QT_DEPRECATED_VERSION_X_6_13("Use QMetaAssociation::Iterable::ConstIterator instead.")
+QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15("Use QMetaAssociation::Iterable::ConstIterator instead.")
QAssociativeConstIterator : public QConstIterator<QMetaAssociation>
{
public:
@@ -48,15 +56,15 @@ public:
: QConstIterator(std::move(it))
{}
- Q_CORE_EXPORT QVariant key() const;
- Q_CORE_EXPORT QVariant value() const;
+ QVariant key() const;
+ QVariant value() const;
- Q_CORE_EXPORT QVariant operator*() const;
- Q_CORE_EXPORT QVariantConstPointer operator->() const;
+ QVariant operator*() const;
+ QVariantConstPointer operator->() const;
};
class
-QT_DEPRECATED_VERSION_X_6_13("Use QMetaAssociation::Iterable instead.")
+QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15("Use QMetaAssociation::Iterable instead.")
QAssociativeIterable : public QIterable<QMetaAssociation>
{
public:
@@ -125,16 +133,16 @@ public:
iterator mutableBegin() { return iterator(QIterable::mutableBegin()); }
iterator mutableEnd() { return iterator(QIterable::mutableEnd()); }
- Q_CORE_EXPORT const_iterator find(const QVariant &key) const;
+ const_iterator find(const QVariant &key) const;
const_iterator constFind(const QVariant &key) const { return find(key); }
- Q_CORE_EXPORT iterator mutableFind(const QVariant &key);
+ iterator mutableFind(const QVariant &key);
- Q_CORE_EXPORT bool containsKey(const QVariant &key);
- Q_CORE_EXPORT void insertKey(const QVariant &key);
- Q_CORE_EXPORT void removeKey(const QVariant &key);
+ bool containsKey(const QVariant &key);
+ void insertKey(const QVariant &key);
+ void removeKey(const QVariant &key);
- Q_CORE_EXPORT QVariant value(const QVariant &key) const;
- Q_CORE_EXPORT void setValue(const QVariant &key, const QVariant &mapped);
+ QVariant value(const QVariant &key) const;
+ void setValue(const QVariant &key, const QVariant &mapped);
};
template<>
@@ -176,8 +184,10 @@ Q_DECLARE_TYPEINFO(QAssociativeIterable, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QAssociativeIterable::iterator, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QAssociativeIterable::const_iterator, Q_RELOCATABLE_TYPE);
+#undef QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15
+
QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 13)
+#endif // QT_DEPRECATED_SINCE(6, 15)
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qmetaassociation.cpp b/src/corelib/kernel/qmetaassociation.cpp
index 7fbb356d9a0..5eae6658a57 100644
--- a/src/corelib/kernel/qmetaassociation.cpp
+++ b/src/corelib/kernel/qmetaassociation.cpp
@@ -464,13 +464,6 @@ QMetaType QMetaAssociation::mappedMetaType() const
*/
/*!
- \fn QVariant::Reference<QMetaAssociation::Iterable::Iterator> QMetaAssociation::Iterable::Iterator::operator[](qsizetype n) const
- Returns the item offset from the current one by \a n, converted to a
- QVariant::Reference. The resulting QVariant::Reference resolves to the
- mapped value if there is one, or to the key value if not.
-*/
-
-/*!
\fn QVariant QMetaAssociation::Iterable::ConstIterator::key() const
Returns the key this iterator points to.
*/
@@ -494,11 +487,4 @@ QMetaType QMetaAssociation::mappedMetaType() const
iterator if there is one, or otherwise the key.
*/
-/*!
- \fn QVariant QMetaAssociation::Iterable::ConstIterator::operator[](qsizetype n) const
- Returns the item offset from the current one by \a n, converted to a
- QVariant. The returned value is the mapped value at the current iterator if
- there is one, or otherwise the key.
-*/
-
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qmetaassociation.h b/src/corelib/kernel/qmetaassociation.h
index d7b3fb65242..6d8de13e90a 100644
--- a/src/corelib/kernel/qmetaassociation.h
+++ b/src/corelib/kernel/qmetaassociation.h
@@ -41,7 +41,6 @@ public:
reference operator*() const { return reference(*this); }
pointer operator->() const { return pointer(*this); }
- reference operator[](qsizetype n) const { return reference(*this + n); }
};
class AssociativeConstIterator : public QConstIterator<QMetaAssociation>
@@ -68,7 +67,6 @@ public:
mapped_type operator*() const;
pointer operator->() const { return pointer(*this); }
- mapped_type operator[](qsizetype n) const;
};
} // namespace QtMetaContainerPrivate
@@ -126,11 +124,6 @@ inline AssociativeConstIterator::mapped_type AssociativeConstIterator::operator*
return reference(*this);
}
-inline AssociativeConstIterator::mapped_type AssociativeConstIterator::operator[](qsizetype n) const
-{
- return reference(*this + n);
-}
-
class Association : public QIterable<QMetaAssociation>
{
public:
diff --git a/src/corelib/kernel/qmetacontainer.h b/src/corelib/kernel/qmetacontainer.h
index f8e73a8b0a2..c9d3a6bf9c6 100644
--- a/src/corelib/kernel/qmetacontainer.h
+++ b/src/corelib/kernel/qmetacontainer.h
@@ -1078,7 +1078,6 @@ public:
QVariant::Reference<Iterator> operator*() const;
QVariant::Pointer<Iterator> operator->() const;
- QVariant::Reference<Iterator> operator[](qsizetype n) const;
};
class ConstIterator : public QConstIterator<QMetaAssociation>
@@ -1089,7 +1088,6 @@ public:
QVariant operator*() const;
QVariant::ConstPointer<ConstIterator> operator->() const;
- QVariant operator[](qsizetype n) const;
};
using RandomAccessIterator = Iterator;
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/qsequentialiterable.cpp b/src/corelib/kernel/qsequentialiterable.cpp
index 5b040404654..b256b129d2c 100644
--- a/src/corelib/kernel/qsequentialiterable.cpp
+++ b/src/corelib/kernel/qsequentialiterable.cpp
@@ -7,13 +7,13 @@
QT_BEGIN_NAMESPACE
-#if QT_DEPRECATED_SINCE(6, 13)
+#if QT_DEPRECATED_SINCE(6, 15)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
/*!
\class QSequentialIterable
- \deprecated [6.13] Use QMetaSequence::Iterable instead.
+ \deprecated [6.15] Use QMetaSequence::Iterable instead.
\since 5.2
\inmodule QtCore
\brief The QSequentialIterable class is an iterable interface for a container in a QVariant.
@@ -163,7 +163,7 @@ void QSequentialIterable::set(qsizetype idx, const QVariant &value)
/*!
\typealias QSequentialIterable::const_iterator
- \deprecated [6.13] Use QMetaSequence::Iterable::ConstIterator instead.
+ \deprecated [6.15] Use QMetaSequence::Iterable::ConstIterator instead.
\brief The QSequentialIterable::const_iterator allows iteration over a container in a QVariant.
A QSequentialIterable::const_iterator can only be created by a QSequentialIterable instance,
@@ -173,7 +173,7 @@ void QSequentialIterable::set(qsizetype idx, const QVariant &value)
/*!
\typealias QSequentialIterable::iterator
\since 6.0
- \deprecated [6.13] Use QMetaSequence::Iterable::Iterator instead.
+ \deprecated [6.15] Use QMetaSequence::Iterable::Iterator instead.
\brief The QSequentialIterable::iterator allows iteration over a container in a QVariant.
A QSequentialIterable::iterator can only be created by a QSequentialIterable instance,
@@ -225,6 +225,6 @@ QVariantConstPointer QSequentialConstIterator::operator->() const
}
QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 13)
+#endif // QT_DEPRECATED_SINCE(6, 15)
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qsequentialiterable.h b/src/corelib/kernel/qsequentialiterable.h
index 738bebf3631..92252cb19dd 100644
--- a/src/corelib/kernel/qsequentialiterable.h
+++ b/src/corelib/kernel/qsequentialiterable.h
@@ -9,12 +9,20 @@
QT_BEGIN_NAMESPACE
-#if QT_DEPRECATED_SINCE(6, 13)
+#if QT_DEPRECATED_SINCE(6, 15)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU < 1300
+ // GCC < 13 doesn't accept both deprecation and visibility on the same class
+ #define QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15(text) Q_CORE_EXPORT
+#else
+ #define QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15(text) \
+ Q_CORE_EXPORT QT_DEPRECATED_VERSION_X_6_15(text)
+#endif
+
class
-QT_DEPRECATED_VERSION_X_6_13("Use QMetaSequence::Iterable::Iterator instead.")
+QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15("Use QMetaSequence::Iterable::Iterator instead.")
QSequentialIterator : public QIterator<QMetaSequence>
{
public:
@@ -26,12 +34,12 @@ public:
: QIterator(std::move(it))
{}
- Q_CORE_EXPORT QVariantRef<QSequentialIterator> operator*() const;
- Q_CORE_EXPORT QVariantPointer<QSequentialIterator> operator->() const;
+ QVariantRef<QSequentialIterator> operator*() const;
+ QVariantPointer<QSequentialIterator> operator->() const;
};
class
-QT_DEPRECATED_VERSION_X_6_13("Use QMetaSequence::Iterable::ConstIterator instead.")
+QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15("Use QMetaSequence::Iterable::ConstIterator instead.")
QSequentialConstIterator : public QConstIterator<QMetaSequence>
{
public:
@@ -43,12 +51,12 @@ public:
: QConstIterator(std::move(it))
{}
- Q_CORE_EXPORT QVariant operator*() const;
- Q_CORE_EXPORT QVariantConstPointer operator->() const;
+ QVariant operator*() const;
+ QVariantConstPointer operator->() const;
};
class
-QT_DEPRECATED_VERSION_X_6_13("Use QMetaSequence::Iterable instead.")
+QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15("Use QMetaSequence::Iterable instead.")
QSequentialIterable : public QIterable<QMetaSequence>
{
public:
@@ -118,14 +126,14 @@ public:
iterator mutableBegin() { return iterator(QIterable::mutableBegin()); }
iterator mutableEnd() { return iterator(QIterable::mutableEnd()); }
- Q_CORE_EXPORT QVariant at(qsizetype idx) const;
- Q_CORE_EXPORT void set(qsizetype idx, const QVariant &value);
+ QVariant at(qsizetype idx) const;
+ void set(qsizetype idx, const QVariant &value);
enum Position { Unspecified, AtBegin, AtEnd };
- Q_CORE_EXPORT void addValue(const QVariant &value, Position position = Unspecified);
- Q_CORE_EXPORT void removeValue(Position position = Unspecified);
+ void addValue(const QVariant &value, Position position = Unspecified);
+ void removeValue(Position position = Unspecified);
- Q_CORE_EXPORT QMetaType valueMetaType() const;
+ QMetaType valueMetaType() const;
};
template<>
@@ -158,8 +166,10 @@ Q_DECLARE_TYPEINFO(QSequentialIterable, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QSequentialIterable::iterator, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QSequentialIterable::const_iterator, Q_RELOCATABLE_TYPE);
+#undef QT_CORE_DEPRECATED_EXPORT_VERSION_X_6_15
+
QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 13)
+#endif // QT_DEPRECATED_SINCE(6, 15)
QT_END_NAMESPACE
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/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index d2b2aa1cebb..7cad20e9fd4 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -2852,14 +2852,14 @@ const void *QtPrivate::QVariantTypeCoercer::coerce(const QVariant &value, const
return converted.constData();
}
-#if QT_DEPRECATED_SINCE(6, 13)
+#if QT_DEPRECATED_SINCE(6, 15)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
/*!
\class QVariantRef
\since 6.0
- \deprecated [6.13] Use QVariant::Reference instead.
+ \deprecated [6.15] Use QVariant::Reference instead.
\inmodule QtCore
\brief The QVariantRef acts as a non-const reference to a QVariant.
@@ -2914,7 +2914,7 @@ QT_WARNING_DISABLE_DEPRECATED
/*!
\class QVariantConstPointer
\since 6.0
- \deprecated [6.13] Use QVariant::ConstPointer instead.
+ \deprecated [6.15] Use QVariant::ConstPointer instead.
\inmodule QtCore
\brief Emulated const pointer to QVariant based on a pointer.
@@ -2952,7 +2952,7 @@ const QVariant *QVariantConstPointer::operator->() const
/*!
\class QVariantPointer
\since 6.0
- \deprecated [6.13] Use QVariant::Pointer instead.
+ \deprecated [6.15] Use QVariant::Pointer instead.
\inmodule QtCore
\brief QVariantPointer is a template class that emulates a pointer to QVariant based on a pointer.
@@ -2982,7 +2982,7 @@ const QVariant *QVariantConstPointer::operator->() const
*/
QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 13)
+#endif // QT_DEPRECATED_SINCE(6, 15)
/*!
\class QVariant::ConstReference
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index 47fb34cbe65..82eec0693d6 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -983,12 +983,12 @@ private:
};
}
-#if QT_DEPRECATED_SINCE(6, 13)
+#if QT_DEPRECATED_SINCE(6, 15)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
template<typename Pointer> class
-QT_DEPRECATED_VERSION_X_6_13("Use QVariant::Reference instead.")
+QT_DEPRECATED_VERSION_X_6_15("Use QVariant::Reference instead.")
QVariantRef
{
private:
@@ -1014,7 +1014,7 @@ public:
};
class
-QT_DEPRECATED_VERSION_X_6_13("Use QVariant::ConstPointer instead.")
+QT_DEPRECATED_VERSION_X_6_15("Use QVariant::ConstPointer instead.")
QVariantConstPointer
{
private:
@@ -1028,7 +1028,7 @@ public:
};
template<typename Pointer> class
-QT_DEPRECATED_VERSION_X_6_13("Use QVariant::Pointer instead.")
+QT_DEPRECATED_VERSION_X_6_15("Use QVariant::Pointer instead.")
QVariantPointer
{
private:
@@ -1041,7 +1041,7 @@ public:
};
QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 13)
+#endif // QT_DEPRECATED_SINCE(6, 15)
QT_END_NAMESPACE
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/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index c4d4daae99f..f3f32e20adc 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -215,6 +215,20 @@
It's recommended to use it on the QFuture object that represents the entire
continuation chain, like it's shown in the example above.
+ If any of the continuations in the chain executes an asynchronous
+ computation and returns a QFuture representing it, the \c cancelChain() call
+ will not be propagated into such nested computation once it is started.
+ The reason for that is that the future will be available in the continuation
+ chain only when the outer future is fulfilled, but the cancellation might
+ happen when both an outer and a nested futures are still waiting for their
+ computations to be finished. In such cases, the nested future needs to be
+ captured and canceled explicitly.
+
+ \snippet code/src_corelib_thread_qfuture.cpp 39
+
+ In this example, if \c runNestedComputation() is already in progress,
+ it can only be canceled by calling \c {nested.cancel()}.
+
\sa cancel()
*/
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/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp
index dad0ac2b74a..dae83437b89 100644
--- a/src/gui/accessible/linux/atspiadaptor.cpp
+++ b/src/gui/accessible/linux/atspiadaptor.cpp
@@ -132,7 +132,7 @@ AtSpiAdaptor::~AtSpiAdaptor()
*/
QString AtSpiAdaptor::introspect(const QString &path) const
{
- static const QLatin1StringView accessibleIntrospection(
+ constexpr auto accessibleIntrospection =
" <interface name=\"org.a11y.atspi.Accessible\">\n"
" <property access=\"read\" type=\"s\" name=\"Name\"/>\n"
" <property access=\"read\" type=\"s\" name=\"Description\"/>\n"
@@ -182,9 +182,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"s\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView actionIntrospection(
+ constexpr auto actionIntrospection =
" <interface name=\"org.a11y.atspi.Action\">\n"
" <property access=\"read\" type=\"i\" name=\"NActions\"/>\n"
" <method name=\"GetDescription\">\n"
@@ -208,9 +208,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"b\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView applicationIntrospection(
+ constexpr auto applicationIntrospection =
" <interface name=\"org.a11y.atspi.Application\">\n"
" <property access=\"read\" type=\"s\" name=\"ToolkitName\"/>\n"
" <property access=\"read\" type=\"s\" name=\"Version\"/>\n"
@@ -223,9 +223,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"s\" name=\"address\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView collectionIntrospection(
+ constexpr auto collectionIntrospection =
" <interface name=\"org.a11y.atspi.Collection\">\n"
" <method name=\"GetMatches\">\n"
" <arg direction=\"in\" name=\"rule\" type=\"(aiia{ss}iaiiasib)\"/>\n"
@@ -266,9 +266,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"QSpiReferenceSet\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView componentIntrospection(
+ constexpr auto componentIntrospection =
" <interface name=\"org.a11y.atspi.Component\">\n"
" <method name=\"Contains\">\n"
" <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
@@ -329,9 +329,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"b\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView editableTextIntrospection(
+ constexpr auto editableTextIntrospection =
" <interface name=\"org.a11y.atspi.EditableText\">\n"
" <method name=\"SetTextContents\">\n"
" <arg direction=\"in\" type=\"s\" name=\"newContents\"/>\n"
@@ -362,9 +362,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"b\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView selectionIntrospection(
+ constexpr auto selectionIntrospection =
" <interface name=\"org.a11y.atspi.Selection\">\n"
" <property name=\"NSelectedChildren\" type=\"i\" access=\"read\"/>\n"
" <method name=\"GetSelectedChild\">\n"
@@ -395,9 +395,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"b\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView tableIntrospection(
+ constexpr auto tableIntrospection =
" <interface name=\"org.a11y.atspi.Table\">\n"
" <property access=\"read\" type=\"i\" name=\"NRows\"/>\n"
" <property access=\"read\" type=\"i\" name=\"NColumns\"/>\n"
@@ -503,9 +503,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"b\" name=\"is_selected\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView tableCellIntrospection(
+ constexpr auto tableCellIntrospection =
" <interface name=\"org.a11y.atspi.TableCell\">\n"
" <property access=\"read\" name=\"ColumnSpan\" type=\"i\" />\n"
" <property access=\"read\" name=\"Position\" type=\"(ii)\">\n"
@@ -531,9 +531,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView textIntrospection(
+ constexpr auto textIntrospection =
" <interface name=\"org.a11y.atspi.Text\">\n"
" <property access=\"read\" type=\"i\" name=\"CharacterCount\"/>\n"
" <property access=\"read\" type=\"i\" name=\"CaretOffset\"/>\n"
@@ -670,9 +670,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"out\" type=\"b\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
- static const QLatin1StringView valueIntrospection(
+ constexpr auto valueIntrospection =
" <interface name=\"org.a11y.atspi.Value\">\n"
" <property access=\"read\" type=\"d\" name=\"MinimumValue\"/>\n"
" <property access=\"read\" type=\"d\" name=\"MaximumValue\"/>\n"
@@ -682,7 +682,7 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"in\" type=\"d\" name=\"value\"/>\n"
" </method>\n"
" </interface>\n"
- );
+ ""_L1;
QAccessibleInterface * interface = interfaceFromPath(path);
if (!interface) {
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/linux/qspimatchrulematcher.cpp b/src/gui/accessible/linux/qspimatchrulematcher.cpp
index 48357f7ae63..a63bdd04443 100644
--- a/src/gui/accessible/linux/qspimatchrulematcher.cpp
+++ b/src/gui/accessible/linux/qspimatchrulematcher.cpp
@@ -37,7 +37,7 @@ QSpiMatchRuleMatcher::QSpiMatchRuleMatcher(const QSpiMatchRule &matchRule)
}
}
- // use qualified interface names to match what accessibleInterfaces() returns
+ // use qualified interface names to match what AtSpiAdaptor::accessibleInterfaces returns
m_interfaces.reserve(matchRule.interfaces.size());
for (const QString &ifaceName : matchRule.interfaces)
m_interfaces.push_back("org.a11y.atspi."_L1 + ifaceName);
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/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/platform/unix/qxkbcommon.cpp b/src/gui/platform/unix/qxkbcommon.cpp
index ebac9f108e8..e755892cf36 100644
--- a/src/gui/platform/unix/qxkbcommon.cpp
+++ b/src/gui/platform/unix/qxkbcommon.cpp
@@ -328,6 +328,12 @@ static constexpr const auto KeyTbl = qMakeArray(
Xkb2Qt<XKB_KEY_XF86Option, Qt::Key_Option>,
Xkb2Qt<XKB_KEY_XF86Paste, Qt::Key_Paste>,
Xkb2Qt<XKB_KEY_XF86Phone, Qt::Key_Phone>,
+#ifdef XKB_KEY_XF86PickupPhone
+ Xkb2Qt<XKB_KEY_XF86PickupPhone, Qt::Key_Call>,
+#endif
+#ifdef XKB_KEY_XF86HangupPhone
+ Xkb2Qt<XKB_KEY_XF86HangupPhone, Qt::Key_Hangup>,
+#endif
Xkb2Qt<XKB_KEY_XF86Reply, Qt::Key_Reply>,
Xkb2Qt<XKB_KEY_XF86Reload, Qt::Key_Reload>,
Xkb2Qt<XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows>,
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index a511eb854cb..33e35ba6694 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -2159,6 +2159,18 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
}
#endif
+ // Add self-dependency to be able to add memory barriers for writes in graphics stages
+ VkSubpassDependency selfDependency;
+ VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+ selfDependency.srcSubpass = 0;
+ selfDependency.dstSubpass = 0;
+ selfDependency.srcStageMask = stageMask;
+ selfDependency.dstStageMask = stageMask;
+ selfDependency.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ selfDependency.dstAccessMask = selfDependency.srcAccessMask;
+ selfDependency.dependencyFlags = 0;
+ rpD->subpassDeps.append(selfDependency);
+
// rpD->subpassDeps stays empty: don't yet know the correct initial/final
// access and stage stuff for the implicit deps at this point, so leave it
// to the resource tracking and activateTextureRenderTarget() to generate
@@ -4864,6 +4876,17 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
cmd.args.beginRenderPass.useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
: VK_SUBPASS_CONTENTS_INLINE);
break;
+ case QVkCommandBuffer::Command::MemoryBarrier: {
+ VkMemoryBarrier barrier;
+ barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+ barrier.pNext = nullptr;
+ barrier.dstAccessMask = cmd.args.memoryBarrier.dstAccessMask;
+ barrier.srcAccessMask = cmd.args.memoryBarrier.srcAccessMask;
+ df->vkCmdPipelineBarrier(cbD->cb, cmd.args.memoryBarrier.srcStageMask, cmd.args.memoryBarrier.dstStageMask, cmd.args.memoryBarrier.dependencyFlags,
+ 1, &barrier,
+ 0, VK_NULL_HANDLE,
+ 0, VK_NULL_HANDLE);
+ } break;
case QVkCommandBuffer::Command::EndRenderPass:
df->vkCmdEndRenderPass(cbD->cb);
break;
@@ -5702,6 +5725,9 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
auto &descSetBd(srbD->boundResourceData[currentFrameSlot]);
bool rewriteDescSet = false;
+ bool addWriteBarrier = false;
+ VkPipelineStageFlags writeBarrierSrcStageMask = 0;
+ VkPipelineStageFlags writeBarrierDstStageMask = 0;
// Do host writes and mark referenced shader resources as in-use.
// Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
@@ -5789,9 +5815,22 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
access = QRhiPassResourceTracker::TexStorageStore;
else
access = QRhiPassResourceTracker::TexStorageLoadStore;
+
+ const auto stage = QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage);
+ const auto prevAccess = passResTracker.textures().find(texD);
+ if (prevAccess != passResTracker.textures().end()) {
+ const QRhiPassResourceTracker::Texture &tex = prevAccess->second;
+ if (tex.access == QRhiPassResourceTracker::TexStorageStore
+ || tex.access == QRhiPassResourceTracker::TexStorageLoadStore) {
+ addWriteBarrier = true;
+ writeBarrierDstStageMask |= toVkPipelineStage(stage);
+ writeBarrierSrcStageMask |= toVkPipelineStage(tex.stage);
+ }
+ }
+
trackedRegisterTexture(&passResTracker, texD,
access,
- QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
+ stage);
if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
rewriteDescSet = true;
@@ -5818,9 +5857,21 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
access = QRhiPassResourceTracker::BufStorageStore;
else
access = QRhiPassResourceTracker::BufStorageLoadStore;
+
+ const auto stage = QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage);
+ const auto prevAccess = passResTracker.buffers().find(bufD);
+ if (prevAccess != passResTracker.buffers().end()) {
+ const QRhiPassResourceTracker::Buffer &buf = prevAccess->second;
+ if (buf.access == QRhiPassResourceTracker::BufStorageStore
+ || buf.access == QRhiPassResourceTracker::BufStorageLoadStore) {
+ addWriteBarrier = true;
+ writeBarrierDstStageMask |= toVkPipelineStage(stage);
+ writeBarrierSrcStageMask |= toVkPipelineStage(buf.stage);
+ }
+ }
trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
access,
- QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
+ stage);
if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
rewriteDescSet = true;
@@ -5835,6 +5886,28 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
}
+ if (addWriteBarrier) {
+ if (cbD->passUsesSecondaryCb) {
+ VkMemoryBarrier barrier;
+ barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+ barrier.pNext = nullptr;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ barrier.srcAccessMask = barrier.dstAccessMask;
+ df->vkCmdPipelineBarrier(cbD->activeSecondaryCbStack.last(), writeBarrierSrcStageMask, writeBarrierDstStageMask, 0,
+ 1, &barrier,
+ 0, VK_NULL_HANDLE,
+ 0, VK_NULL_HANDLE);
+ } else {
+ QVkCommandBuffer::Command &cmd(cbD->commands.get());
+ cmd.cmd = QVkCommandBuffer::Command::MemoryBarrier;
+ cmd.args.memoryBarrier.dependencyFlags = 0;
+ cmd.args.memoryBarrier.dstStageMask = writeBarrierDstStageMask;
+ cmd.args.memoryBarrier.srcStageMask = writeBarrierSrcStageMask;
+ cmd.args.memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ cmd.args.memoryBarrier.srcAccessMask = cmd.args.memoryBarrier.dstAccessMask;
+ }
+ }
+
// write descriptor sets, if needed
if (rewriteDescSet)
updateShaderResourceBindings(srb);
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
index 1e9318513fd..21044545ad2 100644
--- a/src/gui/rhi/qrhivulkan_p.h
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -425,7 +425,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
TransitionPassResources,
Dispatch,
ExecuteSecondary,
- SetShadingRate
+ SetShadingRate,
+ MemoryBarrier
};
Cmd cmd;
@@ -464,6 +465,13 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
struct {
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
+ VkAccessFlags srcAccessMask;
+ VkAccessFlags dstAccessMask;
+ VkDependencyFlags dependencyFlags;
+ } memoryBarrier;
+ struct {
+ VkPipelineStageFlags srcStageMask;
+ VkPipelineStageFlags dstStageMask;
int count;
int index;
} bufferBarrier;
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index 17ed5fb7ed4..c144820fa24 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -2265,8 +2265,7 @@ bool QFont::fromString(const QString &descrip)
const auto sr = QStringView(descrip).trimmed();
const auto l = sr.split(u',');
const int count = l.size();
- if (!count || (count > 2 && count < 9) || count == 9 ||
- l.first().isEmpty()) {
+ if (!count || (count > 2 && count < 10) || l.first().isEmpty()) {
qWarning("QFont::fromString: Invalid description '%s'",
descrip.isEmpty() ? "(empty)" : descrip.toLatin1().data());
return false;
@@ -2275,16 +2274,8 @@ bool QFont::fromString(const QString &descrip)
setFamily(l[0].toString());
if (count > 1 && l[1].toDouble() > 0.0)
setPointSizeF(l[1].toDouble());
- if (count == 9) {
- setStyleHint((StyleHint) l[2].toInt());
- setWeight(QFont::Weight(l[3].toInt()));
- setItalic(l[4].toInt());
- setUnderline(l[5].toInt());
- setStrikeOut(l[6].toInt());
- setFixedPitch(l[7].toInt());
- if (!d->request.fixedPitch) // assume 'false' fixedPitch equals default
- d->request.ignorePitch = true;
- } else if (count >= 10) {
+
+ if (count >= 10) {
if (l[2].toInt() > 0)
setPixelSize(l[2].toInt());
setStyleHint((StyleHint) l[3].toInt());
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/network/CMakeLists.txt b/src/network/CMakeLists.txt
index cb304fc865c..2a15dca0635 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -148,6 +148,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_http
access/qrestaccessmanager.cpp access/qrestaccessmanager.h access/qrestaccessmanager_p.h
access/qrestreply.cpp access/qrestreply.h access/qrestreply_p.h
access/qsocketabstraction_p.h
+ access/qtcpkeepaliveconfiguration_p.h
socket/qhttpsocketengine.cpp socket/qhttpsocketengine_p.h
)
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
index 2fde9e4c9d5..37e960b19dc 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -93,7 +93,6 @@ private:
// Stream's lifecycle management:
QHttp2Stream *createNewStream(const HttpMessagePair &message, bool uploadDone = false);
void connectStream(const HttpMessagePair &message, QHttp2Stream *stream);
- quint32 popStreamToResume();
QHttp2Connection *h2Connection;
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index e0f5bfc2d64..1b8bfd5d72b 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -1485,6 +1485,18 @@ void QHttpNetworkConnection::setHttp2Parameters(const QHttp2Configuration &param
d->http2Parameters = params;
}
+QTcpKeepAliveConfiguration QHttpNetworkConnection::tcpKeepAliveParameters() const
+{
+ Q_D(const QHttpNetworkConnection);
+ return d->tcpKeepAliveConfiguration;
+}
+
+void QHttpNetworkConnection::setTcpKeepAliveParameters(QTcpKeepAliveConfiguration config)
+{
+ Q_D(QHttpNetworkConnection);
+ d->tcpKeepAliveConfiguration = config;
+}
+
// SSL support below
#ifndef QT_NO_SSL
void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 67b568caea8..f35b89d1aec 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -36,6 +36,7 @@
#include <private/http2protocol_p.h>
#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <private/qtcpkeepaliveconfiguration_p.h>
#include <utility>
@@ -98,6 +99,9 @@ public:
QHttp2Configuration http2Parameters() const;
void setHttp2Parameters(const QHttp2Configuration &params);
+ QTcpKeepAliveConfiguration tcpKeepAliveParameters() const;
+ void setTcpKeepAliveParameters(QTcpKeepAliveConfiguration config);
+
#ifndef QT_NO_SSL
void setSslConfiguration(const QSslConfiguration &config);
void ignoreSslErrors(int channel = -1);
@@ -255,6 +259,8 @@ public:
QString peerVerifyName;
+ QTcpKeepAliveConfiguration tcpKeepAliveConfiguration;
+
friend class QHttpNetworkConnectionChannel;
};
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index ffd5d8ff333..e427175ce61 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -923,9 +923,19 @@ void QHttpNetworkConnectionChannel::_q_connected_abstract_socket(QAbstractSocket
// not sure yet if it helps, but it makes sense
absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
- int kaIdleOption = qEnvironmentVariableIntegerValue(keepAliveIdleOption).value_or(TCP_KEEPIDLE_DEF);
- int kaIntervalOption = qEnvironmentVariableIntegerValue(keepAliveIntervalOption).value_or(TCP_KEEPINTVL_DEF);
- int kaCountOption = qEnvironmentVariableIntegerValue(keepAliveCountOption).value_or(TCP_KEEPCNT_DEF);
+ QTcpKeepAliveConfiguration keepAliveConfig = connection->tcpKeepAliveParameters();
+
+ auto getKeepAliveValue = [](int configValue,
+ const char* envName,
+ int defaultValue) {
+ if (configValue > 0)
+ return configValue;
+ return static_cast<int>(qEnvironmentVariableIntegerValue(envName).value_or(defaultValue));
+ };
+
+ int kaIdleOption = getKeepAliveValue(keepAliveConfig.idleTimeBeforeProbes.count(), keepAliveIdleOption, TCP_KEEPIDLE_DEF);
+ int kaIntervalOption = getKeepAliveValue(keepAliveConfig.intervalBetweenProbes.count(), keepAliveIntervalOption, TCP_KEEPINTVL_DEF);
+ int kaCountOption = getKeepAliveValue(keepAliveConfig.probeCount, keepAliveCountOption, TCP_KEEPCNT_DEF);
absSocket->setSocketOption(QAbstractSocket::KeepAliveIdleOption, kaIdleOption);
absSocket->setSocketOption(QAbstractSocket::KeepAliveIntervalOption, kaIntervalOption);
absSocket->setSocketOption(QAbstractSocket::KeepAliveCountOption, kaCountOption);
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index fbbc55dc4a4..82455e96a81 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -327,6 +327,8 @@ void QHttpThreadDelegate::startRequest()
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
httpConnection->setHttp2Parameters(http2Parameters);
}
+
+ httpConnection->setTcpKeepAliveParameters(tcpKeepAliveParameters);
#ifndef QT_NO_SSL
// Set the QSslConfiguration from this QNetworkRequest.
if (ssl)
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index 2ce64dc9a17..f179d95ac17 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -34,6 +34,7 @@
#include "qnetworkaccessauthenticationmanager_p.h"
#include <QtNetwork/private/http2protocol_p.h>
#include <QtNetwork/qhttpheaders.h>
+#include "qtcpkeepaliveconfiguration_p.h"
#ifndef QT_NO_SSL
#include <memory>
@@ -91,6 +92,7 @@ public:
QString incomingErrorDetail;
QHttp1Configuration http1Parameters;
QHttp2Configuration http2Parameters;
+ QTcpKeepAliveConfiguration tcpKeepAliveParameters;
protected:
// The zerocopy download buffer, if used:
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index f76d79571c3..9a27da00960 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -896,6 +896,9 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
// Propagate Http/2 settings:
delegate->http2Parameters = request.http2Configuration();
delegate->http1Parameters = request.http1Configuration();
+ delegate->tcpKeepAliveParameters.idleTimeBeforeProbes = request.tcpKeepAliveIdleTimeBeforeProbes();
+ delegate->tcpKeepAliveParameters.intervalBetweenProbes = request.tcpKeepAliveIntervalBetweenProbes();
+ delegate->tcpKeepAliveParameters.probeCount = request.tcpKeepAliveProbeCount();
if (request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).isValid())
delegate->connectionCacheExpiryTimeoutSeconds = request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).toInt();
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 5047fc77bd5..d41124f7b14 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -475,6 +475,9 @@ public:
decompressedSafetyCheckThreshold = other.decompressedSafetyCheckThreshold;
#endif
transferTimeout = other.transferTimeout;
+ idleTimeBeforeProbes = other.idleTimeBeforeProbes;
+ intervalBetweenProbes = other.intervalBetweenProbes;
+ probeCount = other.probeCount;
}
inline bool operator==(const QNetworkRequestPrivate &other) const
@@ -491,6 +494,9 @@ public:
#endif
&& transferTimeout == other.transferTimeout
&& QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders)
+ && idleTimeBeforeProbes == other.idleTimeBeforeProbes
+ && intervalBetweenProbes == other.intervalBetweenProbes
+ && probeCount == other.probeCount;
;
// don't compare cookedHeaders
}
@@ -508,6 +514,9 @@ public:
qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll;
#endif
std::chrono::milliseconds transferTimeout = 0ms;
+ std::chrono::duration<int> idleTimeBeforeProbes{0};
+ std::chrono::duration<int> intervalBetweenProbes{0};
+ int probeCount = 0;
};
/*!
@@ -1035,6 +1044,96 @@ void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold)
}
#endif // QT_CONFIG(http)
+/*!
+ \since 6.11
+
+ Returns the time the connection needs to remain idle before TCP
+ starts sending keepalive probes, if the TCP Keepalive functionality has
+ been turned on.
+
+ \sa setIdleTimeBeforeProbes
+*/
+
+std::chrono::seconds QNetworkRequest::tcpKeepAliveIdleTimeBeforeProbes() const
+{
+ return d->idleTimeBeforeProbes;
+}
+
+/*!
+ \fn void QNetworkRequest::setTcpKeepAliveIdleTimeBeforeProbes(std::chrono::seconds idle)
+ \since 6.11
+
+ Sets the time the connection needs to remain idle before TCP starts
+ sending keepalive probes to be \a idle, if the TCP Keepalive
+ functionality has been turned on.
+
+ \sa idleTimeBeforeProbes
+*/
+
+void QNetworkRequest::doSetIdleTimeBeforeProbes(std::chrono::duration<int> seconds) noexcept
+{
+ d->idleTimeBeforeProbes = seconds;
+}
+
+/*!
+ \since 6.11
+
+ Returns the time between individual keepalive probes, if the TCP
+ Keepalive functionality has been turned on.
+
+ \sa setIntervalBetweenProbes
+*/
+
+std::chrono::seconds QNetworkRequest::tcpKeepAliveIntervalBetweenProbes() const
+{
+ return d->intervalBetweenProbes;
+}
+
+/*!
+ \fn void QNetworkRequest::setTcpKeepAliveIntervalBetweenProbes(std::chrono::seconds interval)
+ \since 6.11
+
+ Sets the time between individual keepalive probes to be \a interval,
+ if the TCP Keepalive functionality has been turned on.
+
+ \sa intervalBetweenProbes
+*/
+
+void QNetworkRequest::doSetIntervalBetweenProbes(std::chrono::duration<int> seconds) noexcept
+{
+ d->intervalBetweenProbes = seconds;
+}
+
+/*!
+ \since 6.11
+
+ Returns the maximum number of keepalive probes TCP should send before
+ dropping the connection, if the TCP Keepalive functionality has been
+ turned on.
+
+ \sa setIntervalBetweenProbes
+*/
+
+int QNetworkRequest::tcpKeepAliveProbeCount() const
+{
+ return d->probeCount;
+}
+
+/*!
+ \since 6.11
+
+ Sets the maximum number of keepalive \a probes TCP should send
+ before dropping the connection, if the TCP Keepalive functionality has
+ been turned on.
+
+ \sa probeCount
+*/
+
+void QNetworkRequest::setTcpKeepAliveProbeCount(int probes) noexcept
+{
+ d->probeCount = probes;
+}
+
#if QT_CONFIG(http) || defined (Q_OS_WASM)
/*!
\fn int QNetworkRequest::transferTimeout() const
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index ea70255a718..49aa45233af 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -7,12 +7,15 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qassert.h>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QString>
#include <QtCore/QUrl>
#include <QtCore/QVariant>
#include <QtCore/q26numeric.h>
+#include <QtCore/q20utility.h>
#include <chrono>
@@ -178,6 +181,22 @@ public:
qint64 decompressedSafetyCheckThreshold() const;
void setDecompressedSafetyCheckThreshold(qint64 threshold);
#endif // QT_CONFIG(http)
+ std::chrono::seconds tcpKeepAliveIdleTimeBeforeProbes() const;
+ void setTcpKeepAliveIdleTimeBeforeProbes(std::chrono::seconds idle)
+ {
+ const auto r = q26::saturate_cast<int>(idle.count());
+ Q_PRE(q20::cmp_equal(r, idle.count()));
+ doSetIdleTimeBeforeProbes(std::chrono::duration<int>(r));
+ }
+ std::chrono::seconds tcpKeepAliveIntervalBetweenProbes() const;
+ void setTcpKeepAliveIntervalBetweenProbes(std::chrono::seconds interval)
+ {
+ const auto r = q26::saturate_cast<int>(interval.count());
+ Q_PRE(q20::cmp_equal(r, interval.count()));
+ doSetIntervalBetweenProbes(std::chrono::duration<int>(r));
+ }
+ int tcpKeepAliveProbeCount() const;
+ void setTcpKeepAliveProbeCount(int probes) noexcept;
#if QT_CONFIG(http) || defined (Q_OS_WASM)
QT_NETWORK_INLINE_SINCE(6, 8)
@@ -189,6 +208,8 @@ public:
void setTransferTimeout(std::chrono::milliseconds duration = DefaultTransferTimeout);
#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
private:
+ void doSetIdleTimeBeforeProbes(std::chrono::duration<int> idle) noexcept;
+ void doSetIntervalBetweenProbes(std::chrono::duration<int> interval) noexcept;
QSharedDataPointer<QNetworkRequestPrivate> d;
friend class QNetworkRequestPrivate;
};
diff --git a/src/network/access/qtcpkeepaliveconfiguration_p.h b/src/network/access/qtcpkeepaliveconfiguration_p.h
new file mode 100644
index 00000000000..b8bd96666ef
--- /dev/null
+++ b/src/network/access/qtcpkeepaliveconfiguration_p.h
@@ -0,0 +1,50 @@
+// 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
+
+#ifndef QTCPKEEPALIVECONFIGURATION_P_H
+#define QTCPKEEPALIVECONFIGURATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+struct QTcpKeepAliveConfiguration
+{
+ std::chrono::duration<int> idleTimeBeforeProbes;
+ std::chrono::duration<int> intervalBetweenProbes;
+ int probeCount;
+
+ bool isEqual(const QTcpKeepAliveConfiguration &other) const noexcept
+ {
+ return idleTimeBeforeProbes == other.idleTimeBeforeProbes
+ && intervalBetweenProbes == other.intervalBetweenProbes
+ && probeCount == other.probeCount;
+ }
+
+ friend bool operator==(const QTcpKeepAliveConfiguration &lhs, const QTcpKeepAliveConfiguration &rhs) noexcept
+ { return lhs.isEqual(rhs); }
+ friend bool operator!=(const QTcpKeepAliveConfiguration &lhs, const QTcpKeepAliveConfiguration &rhs) noexcept
+ { return !lhs.isEqual(rhs); }
+
+};
+
+Q_DECLARE_TYPEINFO(QTcpKeepAliveConfiguration, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QTCPKEEPALIVECONFIGURATION_P_H
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 975332a14ab..eb95d891e42 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -1045,7 +1045,7 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
host = addresses.takeFirst();
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), connecting to %s:%i, %d left to try",
- host.toString().toLatin1().constData(), port, addresses.count());
+ host.toString().toLatin1().constData(), port, int(addresses.count()));
#endif
if (cachedSocketDescriptor == -1 && !initSocketLayer(host.protocol())) {
@@ -1247,6 +1247,9 @@ void QAbstractSocketPrivate::emitReadyRead(int channel)
void QAbstractSocketPrivate::emitBytesWritten(qint64 bytes, int channel)
{
Q_Q(QAbstractSocket);
+
+ bytesWrittenEmissionCount++;
+
// Only emit bytesWritten() when not recursing.
if (!emittedBytesWritten && channel == currentWriteChannel) {
QScopedValueRollback<bool> r(emittedBytesWritten);
@@ -2265,6 +2268,8 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
if (d->writeBuffer.isEmpty())
return false;
+ const quint32 bwEmissionCountAtEntry = d->bytesWrittenEmissionCount;
+
QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
@@ -2304,6 +2309,13 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
qDebug("QAbstractSocket::waitForBytesWritten returns true");
#endif
return true;
+ } else if (d->bytesWrittenEmissionCount != bwEmissionCountAtEntry) {
+ // A slot connected to any signal emitted by this method has written data, which
+ // fulfills the condition to return true that at least one byte has been written.
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForBytesWritten returns true (write in signal handler)");
+#endif
+ return true;
}
}
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index 5f33eddbc7b..5a0a6489e2e 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -117,6 +117,8 @@ public:
bool hasPendingData = false;
bool hasPendingDatagram = false;
+ quint32 bytesWrittenEmissionCount = 0;
+
QTimer *connectTimer = nullptr;
int hostLookupId = -1;
diff --git a/src/plugins/platforms/directfb/qdirectfbconvenience.cpp b/src/plugins/platforms/directfb/qdirectfbconvenience.cpp
index 881a233e694..5b86c1e1725 100644
--- a/src/plugins/platforms/directfb/qdirectfbconvenience.cpp
+++ b/src/plugins/platforms/directfb/qdirectfbconvenience.cpp
@@ -254,6 +254,7 @@ QDirectFbKeyMap::QDirectFbKeyMap()
insert(DIKS_FAVORITES , Qt::Key_Favorites);
insert(DIKS_KEYBOARD , Qt::Key_Keyboard);
insert(DIKS_PHONE , Qt::Key_Phone);
+ insert(DIKS_CALL , Qt::Key_Call)
insert(DIKS_PROGRAM , Qt::Key_Guide);
insert(DIKS_TIME , Qt::Key_Time);
diff --git a/src/plugins/platforms/wasm/qwasmdrag.cpp b/src/plugins/platforms/wasm/qwasmdrag.cpp
index b3935b4179e..757959e5694 100644
--- a/src/plugins/platforms/wasm/qwasmdrag.cpp
+++ b/src/plugins/platforms/wasm/qwasmdrag.cpp
@@ -16,6 +16,9 @@
#include <QtCore/qtimer.h>
#include <QFile>
+#include <private/qshapedpixmapdndwindow_p.h>
+#include <private/qdnd_p.h>
+
#include <functional>
#include <string>
#include <utility>
@@ -92,9 +95,8 @@ Qt::DropAction QWasmDrag::drag(QDrag *drag)
Qt::DropAction dragResult = Qt::IgnoreAction;
if (qstdweb::haveJspi()) {
- QEventLoop loop;
- m_dragState = std::make_unique<DragState>(drag, window, [&loop]() { loop.quit(); });
- loop.exec();
+ m_dragState = std::make_unique<DragState>(drag, window, [this]() { QSimpleDrag::cancelDrag(); });
+ QSimpleDrag::drag(drag);
dragResult = m_dragState->dropAction;
m_dragState.reset();
}
@@ -116,6 +118,10 @@ void QWasmDrag::onNativeDragStarted(DragEvent *event)
return;
}
+ // We have our own window
+ if (shapedPixmapWindow())
+ shapedPixmapWindow()->setVisible(false);
+
m_dragState->dragImage = std::make_unique<DragState::DragImage>(
m_dragState->drag->pixmap(), m_dragState->drag->mimeData(), event->targetWindow);
event->dataTransfer.setDragImage(m_dragState->dragImage->htmlElement(),
@@ -168,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);
@@ -193,10 +201,28 @@ void QWasmDrag::onNativeDragFinished(DragEvent *event)
m_dragState->quitEventLoopClosure();
}
+void QWasmDrag::onNativeDragEnter(DragEvent *event)
+{
+ event->webEvent.call<void>("preventDefault");
+
+ // Already dragging
+ if (QDragManager::self() && QDragManager::self()->object())
+ return;
+
+ // Event coming from external browser, start a drag
+ if (m_dragState)
+ m_dragState->dropAction = event->dropAction;
+
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(new QMimeData());
+ drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
+}
+
void QWasmDrag::onNativeDragLeave(DragEvent *event)
{
event->webEvent.call<void>("preventDefault");
- m_dragState->dropAction = event->dropAction;
+ if (m_dragState)
+ m_dragState->dropAction = event->dropAction;
event->dataTransfer.setDropAction(Qt::DropAction::IgnoreAction);
}
diff --git a/src/plugins/platforms/wasm/qwasmdrag.h b/src/plugins/platforms/wasm/qwasmdrag.h
index e821470c913..5bb8ec66a3c 100644
--- a/src/plugins/platforms/wasm/qwasmdrag.h
+++ b/src/plugins/platforms/wasm/qwasmdrag.h
@@ -32,6 +32,7 @@ public:
void onNativeDrop(DragEvent *event);
void onNativeDragStarted(DragEvent *event);
void onNativeDragFinished(DragEvent *event);
+ void onNativeDragEnter(DragEvent *event);
void onNativeDragLeave(DragEvent *event);
// QPlatformDrag:
@@ -40,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/wasm/qwasmevent.h b/src/plugins/platforms/wasm/qwasmevent.h
index ef1b6129e3c..07faee3fe4b 100644
--- a/src/plugins/platforms/wasm/qwasmevent.h
+++ b/src/plugins/platforms/wasm/qwasmevent.h
@@ -23,6 +23,7 @@ enum class EventType {
DragEnd,
DragOver,
DragStart,
+ DragEnter,
DragLeave,
Drop,
KeyDown,
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 264471794bd..6e8bd46ca58 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -203,6 +203,12 @@ void QWasmWindow::registerEventHandlers()
QWasmDrag::instance()->onNativeDragFinished(&dragEvent);
}
);
+ m_dragEnterCallback = QWasmEventHandler(m_window, "dragenter",
+ [this](emscripten::val event) {
+ DragEvent dragEvent(EventType::DragEnter, event, window());
+ QWasmDrag::instance()->onNativeDragEnter(&dragEvent);
+ }
+ );
m_dragLeaveCallback = QWasmEventHandler(m_window, "dragleave",
[this](emscripten::val event) {
DragEvent dragEvent(EventType::DragLeave, event, window());
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index 87f4d6644c7..ca5c9132ca0 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -195,6 +195,7 @@ private:
QWasmEventHandler m_dragStartCallback;
QWasmEventHandler m_dragEndCallback;
QWasmEventHandler m_dropCallback;
+ QWasmEventHandler m_dragEnterCallback;
QWasmEventHandler m_dragLeaveCallback;
QWasmEventHandler m_wheelEventCallback;
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();