diff options
Diffstat (limited to 'src')
123 files changed, 1516 insertions, 703 deletions
diff --git a/src/3rdparty/libjpeg/COPYRIGHT.txt b/src/3rdparty/libjpeg/COPYRIGHT.txt index f5eae868466..ce9d95bfebc 100644 --- a/src/3rdparty/libjpeg/COPYRIGHT.txt +++ b/src/3rdparty/libjpeg/COPYRIGHT.txt @@ -1,4 +1,4 @@ -Copyright (C) 2009-2025 D. R. Commander +Copyright (C) 2009-2024 D. R. Commander Copyright (C) 2015, 2020 Google, Inc. Copyright (C) 2019-2020 Arm Limited Copyright (C) 2015-2016, 2018 Matthieu Darbois diff --git a/src/3rdparty/libjpeg/ChangeLog.md b/src/3rdparty/libjpeg/ChangeLog.md index 4bdbf53dd34..17390d80bd0 100644 --- a/src/3rdparty/libjpeg/ChangeLog.md +++ b/src/3rdparty/libjpeg/ChangeLog.md @@ -1,3 +1,34 @@ +3.1.3 +===== + +### Significant changes relative to 3.1.2: + +1. Hardened the TurboJPEG API against hypothetical applications that may +erroneously call `tj*Compress*()` or `tj*Transform()` with a reused JPEG +destination buffer pointer while specifying a destination buffer size of 0. + +2. Hardened the TurboJPEG API against hypothetical applications that may +erroneously set `TJPARAM_LOSSLESS` or `TJPARAM_COLORSPACE` prior to calling +`tj3EncodeYUV*8()` or `tj3CompressFromYUV*8()`. `tj3EncodeYUV*8()` and +`tj3CompressFromYUV*8()` now ignore `TJPARAM_LOSSLESS` and +`TJPARAM_COLORSPACE`. + +3. Hardened the TurboJPEG Java API against hypothetical applications that may +erroneously pass huge X or Y offsets to one of the compression, YUV encoding, +decompression, or YUV decoding methods, leading to signed integer overflow in +the JNI wrapper's buffer size checks that rendered those checks ineffective. + +4. Fixed an issue in the TurboJPEG Java API whereby +`TJCompressor.getSourceBuf()` sometimes returned the buffer from a previous +invocation of `TJCompressor.loadSourceImage()` if the target data precision was +changed before the most recent invocation. + +5. Fixed an issue in the PPM reader that caused incorrect pixels to be +generated when using `tj3LoadImage*()` or `TJCompressor.loadSourceImage()` to +load a PBMPLUS (PPM/PGM) file into a CMYK buffer with a different data +precision than that of the file. + + 3.1.2 ===== @@ -962,9 +993,9 @@ storage. 64-bit libjpeg-turbo SDK for Visual C++ were installed on the same system, only one of them could be uninstalled. -2. Fixed a signed integer overflow and subsequent segfault that occurred when -attempting to decompress images with more than 715827882 pixels using the -64-bit C version of TJBench. +2. Fixed a signed integer overflow and subsequent segfault (CVE-2019-2201) that +occurred when attempting to decompress images with more than 715827882 pixels +using the 64-bit C version of TJBench. 3. Fixed out-of-bounds write in `tjDecompressToYUV2()` and `tjDecompressToYUVPlanes()` (sometimes manifesting as a double free) that @@ -1016,9 +1047,9 @@ regardless of whether a 4:2:2 JPEG image is rotated or transposed prior to decompression (in the frequency domain) or after decompression (in the spatial domain), the final image will be similar. -4. Fixed an integer overflow and subsequent segfault that occurred when -attempting to compress or decompress images with more than 1 billion pixels -using the TurboJPEG API. +4. Fixed an integer overflow and subsequent segfault (CVE-2019-2201) that +occurred when attempting to compress or decompress images with more than 1 +billion pixels using the TurboJPEG API. 5. Fixed a regression introduced by 2.0 beta1[15] whereby attempting to generate a progressive JPEG image on an SSE2-capable CPU using a scan script diff --git a/src/3rdparty/libjpeg/qt_attribution.json b/src/3rdparty/libjpeg/qt_attribution.json index fe38aec1f68..5ac811a12b4 100644 --- a/src/3rdparty/libjpeg/qt_attribution.json +++ b/src/3rdparty/libjpeg/qt_attribution.json @@ -7,8 +7,8 @@ "Description": "The Independent JPEG Group's JPEG software", "Homepage": "http://libjpeg-turbo.virtualgl.org/", - "Version": "3.1.2", - "DownloadLocation": "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.2/libjpeg-turbo-3.1.2.tar.gz", + "Version": "3.1.3", + "DownloadLocation": "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.3/libjpeg-turbo-3.1.3.tar.gz", "PURL": "pkg:github/libjpeg-turbo/libjpeg-turbo@$<VERSION>", "CPE": "cpe:2.3:a:libjpeg-turbo:libjpeg-turbo:$<VERSION>:*:*:*:*:*:*:*", diff --git a/src/3rdparty/libjpeg/src/jconfig.h b/src/3rdparty/libjpeg/src/jconfig.h index e81574b9a48..85a1509fb68 100644 --- a/src/3rdparty/libjpeg/src/jconfig.h +++ b/src/3rdparty/libjpeg/src/jconfig.h @@ -2,9 +2,9 @@ #define JPEG_LIB_VERSION 80 -#define LIBJPEG_TURBO_VERSION 3.0.3 +#define LIBJPEG_TURBO_VERSION 3.1.3 -#define LIBJPEG_TURBO_VERSION_NUMBER 3000003 +#define LIBJPEG_TURBO_VERSION_NUMBER 3001003 #define C_ARITH_CODING_SUPPORTED 1 diff --git a/src/3rdparty/libjpeg/src/jconfigint.h b/src/3rdparty/libjpeg/src/jconfigint.h index 6e7dbd75a1e..6d5f9633928 100644 --- a/src/3rdparty/libjpeg/src/jconfigint.h +++ b/src/3rdparty/libjpeg/src/jconfigint.h @@ -10,7 +10,7 @@ #define PACKAGE_NAME "libjpeg-turbo" -#define VERSION "3.0.3" +#define VERSION "3.1.3" #if SIZE_MAX == 0xffffffff #define SIZEOF_SIZE_T 4 diff --git a/src/3rdparty/libpng/ANNOUNCE b/src/3rdparty/libpng/ANNOUNCE index 10dee70d834..e9a94e05de3 100644 --- a/src/3rdparty/libpng/ANNOUNCE +++ b/src/3rdparty/libpng/ANNOUNCE @@ -1,4 +1,4 @@ -libpng 1.6.52 - December 3, 2025 +libpng 1.6.53 - December 5, 2025 ================================ This is a public release of libpng, intended for use in production code. @@ -9,10 +9,10 @@ Files available for download Source files: - * 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) + * libpng-1.6.53.tar.xz (LZMA-compressed, recommended) + * libpng-1.6.53.tar.gz (deflate-compressed) + * lpng1653.7z (LZMA-compressed) + * lpng1653.zip (deflate-compressed) Other information: @@ -22,18 +22,14 @@ Other information: * TRADEMARK.md -Changes from version 1.6.51 to version 1.6.52 +Changes from version 1.6.52 to version 1.6.53 --------------------------------------------- - * 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.) + * Fixed a build failure on RISC-V RVV caused by a misspelled intrinsic. + (Contributed by Alexander Smorkalov.) + * Fixed a build failure with CMake 4.1 or newer, on Windows, when using + Visual C++ without MASM installed. + (Reported by Andrew Tribick; fixed by Luis Caro Campos.) 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 f8ad74bbdf3..ea43101538a 100644 --- a/src/3rdparty/libpng/CHANGES +++ b/src/3rdparty/libpng/CHANGES @@ -6315,6 +6315,12 @@ Version 1.6.52 [December 3, 2025] Added allocation failure fuzzing to oss-fuzz. (Contributed by Philippe Antoine.) +Version 1.6.53 [December 5, 2025] + Fixed a build failure on RISC-V RVV caused by a misspelled intrinsic. + (Contributed by Alexander Smorkalov.) + Fixed a build failure with CMake 4.1 or newer, on Windows, when using + Visual C++ without MASM installed. + 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 87e5f8b177e..4041ad86eb3 100644 --- a/src/3rdparty/libpng/README +++ b/src/3rdparty/libpng/README @@ -1,4 +1,4 @@ -README for libpng version 1.6.52 +README for libpng version 1.6.53 ================================ 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 f284d987ba6..750025cfdad 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.52 - December 2025 + libpng version 1.6.36, December 2018, through 1.6.53 - 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 11b65d1f13e..85b49496520 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_52 Your_png_h_is_not_version_1_6_52; +typedef png_libpng_version_1_6_53 Your_png_h_is_not_version_1_6_53; /* 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.52" PNG_STRING_NEWLINE \ + "libpng version 1.6.53" 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 bceb9aa45d7..bdcd243dea2 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.52 + * libpng version 1.6.53 * * 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.52, December 2025: + * libpng versions 1.6.36, December 2018, through 1.6.53, 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.52 16 10651 16.so.16.52[.0] + * 1.6.53 16 10651 16.so.16.53[.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.52" +#define PNG_LIBPNG_VER_STRING "1.6.53" #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 52 +#define PNG_LIBPNG_VER_RELEASE 53 /* 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 10652 /* 1.6.52 */ +#define PNG_LIBPNG_VER 10653 /* 1.6.53 */ /* 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_52; +typedef char* png_libpng_version_1_6_53; /* 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 76b5c20bdff..f4ff19209c6 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.52 + * libpng version 1.6.53 * * 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 f4a993441f7..27fa87045b3 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.52 */ +/* libpng version 1.6.53 */ /* 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 f8ca2b7e31d..13a93deedcb 100644 --- a/src/3rdparty/libpng/pngread.c +++ b/src/3rdparty/libpng/pngread.c @@ -3278,7 +3278,7 @@ png_image_read_composite(png_voidp argument) /* 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.) + * (Belt-and-suspenders for CVE-2025-66293.) */ if (component > 255*65535) component = 255*65535; diff --git a/src/3rdparty/libpng/qt_attribution.json b/src/3rdparty/libpng/qt_attribution.json index 1f942f8f564..132e5e8d4cd 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.52", - "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.52.tar.xz", + "Version": "1.6.53", + "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.53.tar.xz", "PURL": "pkg:github/pnggroup/libpng@v$<VERSION>", "CPE": "cpe:2.3:a:libpng:libpng:$<VERSION>:*:*:*:*:*:*:*", diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java index f0d304f9e7f..c63cd1a77e3 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java @@ -32,6 +32,7 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { private QtWindow m_parentWindow; private GestureDetector m_gestureDetector; private final QtEditText m_editText; + private boolean m_editTextFocusInitialized = false; private final QtInputConnection.QtInputConnectionListener m_inputConnectionListener; private boolean m_firstSafeMarginsDelivered = false; private int m_actionBarHeight = -1; @@ -62,6 +63,8 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { if (!isForeignWindow && context instanceof Activity) { // TODO QTBUG-122552 - Service keyboard input not implemented m_editText = new QtEditText(context, listener); + m_editText.setFocusable(false); + m_editText.setFocusableInTouchMode(false); m_editText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -242,6 +245,14 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { @Override public boolean onTouchEvent(MotionEvent event) { + // Enable focus for the edit text on first touch event to avoid + // early QtInputConnection callbacks from blocking the UI thread. + if (!m_editTextFocusInitialized) { + m_editTextFocusInitialized = true; + m_editText.setFocusable(true); + m_editText.setFocusableInTouchMode(true); + } + windowFocusChanged(true, getId()); if (m_editText != null && m_inputConnectionListener != null) m_inputConnectionListener.onEditTextChanged(m_editText); diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index ea8cf7b9c8e..539ad753ca6 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -590,17 +590,14 @@ if(QT_FEATURE_async_io) DEFINES QT_RANDOMACCESSASYNCFILE_QIORING ) - elseif(QT_FEATURE_thread AND QT_FEATURE_future) - # TODO: This should become the last (fallback) condition later. - # We migth also want to rewrite it so that it does not depend on - # QT_FEATURE_future. - qt_internal_extend_target(Core - SOURCES - io/qrandomaccessasyncfile_threadpool.cpp - DEFINES - QT_RANDOMACCESSASYNCFILE_THREAD - ) endif() + # This is the fallback condition that should be always available. + # TODO: try to rewrite it so that it does not depend on + # QT_FEATURE_future. + qt_internal_extend_target(Core + SOURCES + io/qrandomaccessasyncfile_threadpool.cpp + ) endif() # This needs to be done before one below adds kernel32 because the symbols we use diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index b69d0285de2..c1f9aee0180 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -3677,21 +3677,6 @@ macro(qt6_standard_project_setup) if(NOT DEFINED QT_I18N_SOURCE_LANGUAGE) set(QT_I18N_SOURCE_LANGUAGE ${__qt_sps_arg_I18N_SOURCE_LANGUAGE}) endif() - - if(CMAKE_GENERATOR STREQUAL "Xcode") - # Ensure we always use device SDK for Xcode for single-arch Qt builds - set(qt_osx_arch_count 0) - if(QT_OSX_ARCHITECTURES) - list(LENGTH QT_OSX_ARCHITECTURES qt_osx_arch_count) - endif() - if(NOT qt_osx_arch_count GREATER 1 AND "${CMAKE_OSX_SYSROOT}" MATCHES "^[a-z]+simulator$") - # Xcode expects the base SDK to be the device SDK - set(simulator_sysroot "${CMAKE_OSX_SYSROOT}") - string(REGEX REPLACE "simulator" "os" CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT}") - set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT}" CACHE STRING "" FORCE) - set(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "${simulator_sysroot}") - endif() - endif() endif() endmacro() diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index c3e1ba4010f..8a343b15eeb 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -858,6 +858,7 @@ qint64 QAnimationDriver::elapsed() const */ /*! + \internal The default animation driver just spins the timer... */ QDefaultAnimationDriver::QDefaultAnimationDriver(QUnifiedTimer *timer) diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 7216f2920fe..7274b51cc0a 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -620,6 +620,10 @@ int main(void) HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, 1, 1, &ioRingHandle); if (hr == IORING_E_SUBMISSION_QUEUE_FULL) // not valid, but test that this #define exists return 0; + IORING_HANDLE_REF ref(HANDLE(nullptr)); + IORING_BUFFER_REF bufRef(nullptr); + // The newest API addition that we require: + BuildIoRingWriteFile(ioRingHandle, ref, bufRef, -1, 0, FILE_WRITE_FLAGS_NONE, 0, IOSQE_FLAGS_NONE); /* END TEST: */ return 0; } @@ -806,7 +810,7 @@ qt_feature("winsdkicu" PRIVATE CONDITION TEST_winsdkicu DISABLE QT_FEATURE_icu ) -qt_feature("windows_ioring" PRIVATE +qt_feature("windows-ioring" PRIVATE LABEL "Windows I/O Ring" AUTODETECT WIN32 AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 10.0.22000 CONDITION TEST_windows_ioring @@ -1278,14 +1282,13 @@ qt_feature("permissions" PUBLIC ) qt_feature("openssl-hash" PRIVATE LABEL "OpenSSL based cryptographic hash" - AUTODETECT OFF CONDITION QT_FEATURE_openssl_linked AND QT_FEATURE_opensslv30 PURPOSE "Uses OpenSSL based implementation of cryptographic hash algorithms." ) qt_feature("async-io" PRIVATE LABEL "Async File I/O" PURPOSE "Provides support for asynchronous file I/O." - CONDITION (QT_FEATURE_thread AND QT_FEATURE_future) OR APPLE + CONDITION QT_FEATURE_thread AND QT_FEATURE_future ) qt_configure_add_summary_section(NAME "Qt Core") @@ -1298,7 +1301,7 @@ qt_configure_add_summary_entry(ARGS "glib") qt_configure_add_summary_entry(ARGS "icu") qt_configure_add_summary_entry(ARGS "jemalloc") qt_configure_add_summary_entry(ARGS "liburing") -qt_configure_add_summary_entry(ARGS "windows_ioring") +qt_configure_add_summary_entry(ARGS "windows-ioring") qt_configure_add_summary_entry(ARGS "timezone_tzdb") qt_configure_add_summary_entry(ARGS "system-libb2") qt_configure_add_summary_entry(ARGS "mimetype-database") diff --git a/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc b/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc index 0a0dc0b3c50..7cd3f91b901 100644 --- a/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc +++ b/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc @@ -133,7 +133,6 @@ use, for example, library files referenced from a Qt installation. \summary {Forces or disables release package signing regardless of the build type.} \cmakevariablesince 6.7 -\preliminarycmakevariable \cmakevariableandroidonly When set to \c Release, the \c --release flag is passed to the \c @@ -143,6 +142,10 @@ effectively disables release package signing even in Release or RelWithDebInfo builds. When not set, the default behavior is to use release package signing in build types other than Debug. +This variable is not supposed to be set in CMake project files. Rather set it +when configuring your project on the command line or in the CMake settings of +your IDE. + \sa {androiddeployqt} */ @@ -522,7 +525,6 @@ To prevent this, set \c QT_NO_SET_XCODE_BUNDLE_IDENTIFIER to \c TRUE. \summary {Enables verbose mode of deployment tools.} \cmakevariablesince 6.3 -\preliminarycmakevariable Enables verbose mode of the \l androiddeployqt deployment tool when it is called internally at build time, usually during target finalization. @@ -542,7 +544,6 @@ must be set before the first \c{find_package(Qt6)} call to have that effect. \summary {Name of the file to include for setting up deployment support.} \cmakevariablesince 6.3 -\preliminarycmakevariable \note The value of this variable should never be modified by project code. This configure-phase variable is set by the Core package. It is intended to be diff --git a/src/corelib/doc/src/cmake/cmake-properties.qdoc b/src/corelib/doc/src/cmake/cmake-properties.qdoc index 821862494a2..c7b1a27a4b4 100644 --- a/src/corelib/doc/src/cmake/cmake-properties.qdoc +++ b/src/corelib/doc/src/cmake/cmake-properties.qdoc @@ -665,7 +665,6 @@ UTF-8 input). Use the \l QT_NO_CAST_FROM_ASCII and \summary {Specifies the default Qt resource prefix.} \cmakepropertysince 6.0 -\preliminarycmakeproperty When using \l{qt6_add_resources}{qt_add_resources} without a \c PREFIX argument, then the value of this target property will be used as @@ -773,7 +772,6 @@ CMake properties: \brief Sets the FOLDER property for Qt-internal targets. \cmakepropertysince 6.5 -\preliminarycmakeproperty Name of the \l FOLDER for internal targets that are added by Qt's CMake commands. diff --git a/src/corelib/doc/src/cmake/qt_deploy_translations.qdoc b/src/corelib/doc/src/cmake/qt_deploy_translations.qdoc index 43ff23a35a1..e6d3edbbdc4 100644 --- a/src/corelib/doc/src/cmake/qt_deploy_translations.qdoc +++ b/src/corelib/doc/src/cmake/qt_deploy_translations.qdoc @@ -17,7 +17,6 @@ can only be called from a deployment script. It cannot be called directly by the project during the configure stage. \cmakecommandsince 6.5 -\preliminarycmakecommand \note This command does not usually need to be called directly. It is used internally by other higher level commands, but projects wishing to implement more customized deployment logic may find it useful. diff --git a/src/corelib/doc/src/cmake/qt_extract_metatypes.qdoc b/src/corelib/doc/src/cmake/qt_extract_metatypes.qdoc index 7ec8d90f9b1..24112b1cf87 100644 --- a/src/corelib/doc/src/cmake/qt_extract_metatypes.qdoc +++ b/src/corelib/doc/src/cmake/qt_extract_metatypes.qdoc @@ -11,7 +11,6 @@ \summary {Extracts metatypes from a Qt target and generates an associated metatypes.json file.} \cmakecommandsince 6.0 -\preliminarycmakecommand \section1 Synopsis diff --git a/src/corelib/doc/src/cmake/qt_finalize_project.qdoc b/src/corelib/doc/src/cmake/qt_finalize_project.qdoc index 5506712691e..f60b850fcdb 100644 --- a/src/corelib/doc/src/cmake/qt_finalize_project.qdoc +++ b/src/corelib/doc/src/cmake/qt_finalize_project.qdoc @@ -9,7 +9,6 @@ \keyword qt6_finalize_project \summary {Handles various common platform-specific tasks associated with a Qt project.} -\preliminarycmakecommand \include cmake-find-package-core.qdocinc diff --git a/src/corelib/doc/src/external-resources.qdoc b/src/corelib/doc/src/external-resources.qdoc index 2232b49bf23..5d357c65496 100644 --- a/src/corelib/doc/src/external-resources.qdoc +++ b/src/corelib/doc/src/external-resources.qdoc @@ -28,11 +28,6 @@ */ /*! - \externalpage https://marcmutz.wordpress.com/effective-qt/containers/#containers-qlist - \title Pros and Cons of Using QList -*/ - -/*! \externalpage https://marcmutz.wordpress.com/effective-qt/containers/ \title Understand the Qt Containers */ diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 0b42af7686c..b2d79cca603 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -1404,7 +1404,7 @@ static_assert(!std::is_convertible_v<std::nullptr_t, bool>, #if defined(__cplusplus) #ifdef __cpp_constinit -# if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) +# if defined(Q_CC_MSVC) && _MSC_VER < 1940 && !defined(Q_CC_CLANG) // https://developercommunity.visualstudio.com/t/C:-constinit-for-an-optional-fails-if-/1406069 # define Q_CONSTINIT # else diff --git a/src/corelib/global/qttranslation.qdoc b/src/corelib/global/qttranslation.qdoc index 191b3777db6..c8b3764614e 100644 --- a/src/corelib/global/qttranslation.qdoc +++ b/src/corelib/global/qttranslation.qdoc @@ -179,7 +179,7 @@ \fn QString qTrId(const char *id, int n = -1) \relates <QtTranslation> \reentrant - \since 6.9 + \since 6.11 \brief The qTrId function is an alias for qtTrId. diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 0771c15584b..d3c398f0860 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -249,6 +249,12 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHand } /*! + \class QFSFileEnginePrivate + \inmodule QtCore + \internal +*/ + +/*! Opens the file handle \a fh using the open mode \a flags. */ bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) diff --git a/src/corelib/io/qiooperation_p.h b/src/corelib/io/qiooperation_p.h index 56845167ede..1486719a7e8 100644 --- a/src/corelib/io/qiooperation_p.h +++ b/src/corelib/io/qiooperation_p.h @@ -72,6 +72,9 @@ protected: Q_DECLARE_PRIVATE(QIOOperation) friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOReadWriteOperationBase : public QIOOperation @@ -86,6 +89,11 @@ protected: QIOReadWriteOperationBase() = delete; Q_DISABLE_COPY_MOVE(QIOReadWriteOperationBase) explicit QIOReadWriteOperationBase(QIOOperationPrivate &dd, QObject *parent = nullptr); + + friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOReadOperation : public QIOReadWriteOperationBase @@ -101,6 +109,9 @@ protected: explicit QIOReadOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOWriteOperation : public QIOReadWriteOperationBase @@ -116,6 +127,9 @@ protected: explicit QIOWriteOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOVectoredReadOperation : public QIOReadWriteOperationBase @@ -131,6 +145,9 @@ protected: explicit QIOVectoredReadOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOVectoredWriteOperation : public QIOReadWriteOperationBase @@ -146,6 +163,9 @@ protected: explicit QIOVectoredWriteOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; QT_END_NAMESPACE diff --git a/src/corelib/io/qioring_p.h b/src/corelib/io/qioring_p.h index 0db832bc6bf..8fdaf48f8f6 100644 --- a/src/corelib/io/qioring_p.h +++ b/src/corelib/io/qioring_p.h @@ -91,6 +91,10 @@ enum class Operation : quint8 { // clang-format on Q_ENUM_NS(Operation); #undef DEFINE_ENTRY + +#ifdef Q_OS_WIN +struct IORingApiTable; +#endif }; // namespace QtPrivate template <QtPrivate::Operation Op> @@ -221,9 +225,11 @@ private: std::optional<QWinEventNotifier> notifier; HIORING ioRingHandle = nullptr; HANDLE eventHandle = INVALID_HANDLE_VALUE; + const QtPrivate::IORingApiTable *apiTable; bool initialized = false; bool queueWasFull = false; + [[nodiscard]] RequestPrepResult prepareRequest(GenericRequestType &request); QIORing::ReadWriteStatus handleReadCompletion( diff --git a/src/corelib/io/qioring_win.cpp b/src/corelib/io/qioring_win.cpp index 42c51f428d6..b2f188486d0 100644 --- a/src/corelib/io/qioring_win.cpp +++ b/src/corelib/io/qioring_win.cpp @@ -24,6 +24,59 @@ static_assert(sizeof(qsizetype) > sizeof(UINT32), using namespace Qt::StringLiterals; +namespace QtPrivate { +#define FOREACH_WIN_IORING_FUNCTION(Fn) \ + Fn(BuildIoRingReadFile) \ + Fn(BuildIoRingWriteFile) \ + Fn(BuildIoRingFlushFile) \ + Fn(BuildIoRingCancelRequest) \ + Fn(QueryIoRingCapabilities) \ + Fn(CreateIoRing) \ + Fn(GetIoRingInfo) \ + Fn(SubmitIoRing) \ + Fn(CloseIoRing) \ + Fn(PopIoRingCompletion) \ + Fn(SetIoRingCompletionEvent) \ + /**/ +struct IORingApiTable +{ +#define DefineIORingFunction(Name) \ + using Name##Fn = decltype(&::Name); \ + Name##Fn Name = nullptr; + + FOREACH_WIN_IORING_FUNCTION(DefineIORingFunction) + +#undef DefineIORingFunction +}; + +static const IORingApiTable *getApiTable() +{ + static const IORingApiTable apiTable = []() { + IORingApiTable apiTable; + const HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); + if (Q_UNLIKELY(!kernel32)) // how would this happen + return apiTable; + +#define ResolveFunction(Name) \ + apiTable.Name = IORingApiTable::Name##Fn(QFunctionPointer(GetProcAddress(kernel32, #Name))); + + FOREACH_WIN_IORING_FUNCTION(ResolveFunction) + +#undef ResolveFunction + return apiTable; + }(); + +#define TEST_TABLE_OK(X) \ + apiTable.X && /* chain */ +#define BOOL_CHAIN(...) (__VA_ARGS__ true) + const bool success = BOOL_CHAIN(FOREACH_WIN_IORING_FUNCTION(TEST_TABLE_OK)); +#undef BOOL_CHAIN +#undef TEST_TABLE_OK + + return success ? std::addressof(apiTable) : nullptr; +} +} // namespace QtPrivate + static HRESULT buildReadOperation(HIORING ioRingHandle, qintptr fd, QSpan<std::byte> destination, quint64 offset, quintptr userData) { @@ -32,8 +85,10 @@ static HRESULT buildReadOperation(HIORING ioRingHandle, qintptr fd, QSpan<std::b const IORING_BUFFER_REF bufferRef(destination.data()); const auto maxSize = q26::saturate_cast<UINT32>(destination.size()); Q_ASSERT(maxSize == destination.size()); - return BuildIoRingReadFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, userData, - IOSQE_FLAGS_NONE); + const auto *apiTable = QtPrivate::getApiTable(); + Q_ASSERT(apiTable); // If we got this far it needs to be here + return apiTable->BuildIoRingReadFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, + userData, IOSQE_FLAGS_NONE); } static HRESULT buildWriteOperation(HIORING ioRingHandle, qintptr fd, QSpan<const std::byte> source, @@ -44,16 +99,18 @@ static HRESULT buildWriteOperation(HIORING ioRingHandle, qintptr fd, QSpan<const const IORING_BUFFER_REF bufferRef(const_cast<std::byte *>(source.data())); const auto maxSize = q26::saturate_cast<UINT32>(source.size()); Q_ASSERT(maxSize == source.size()); + const auto *apiTable = QtPrivate::getApiTable(); + Q_ASSERT(apiTable); // If we got this far it needs to be here // @todo: FILE_WRITE_FLAGS can be set to write-through, could be used for Unbuffered mode. - return BuildIoRingWriteFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, - FILE_WRITE_FLAGS_NONE, userData, IOSQE_FLAGS_NONE); + return apiTable->BuildIoRingWriteFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, + FILE_WRITE_FLAGS_NONE, userData, IOSQE_FLAGS_NONE); } QIORing::~QIORing() { if (initialized) { CloseHandle(eventHandle); - CloseIoRing(ioRingHandle); + apiTable->CloseIoRing(ioRingHandle); } } @@ -62,8 +119,13 @@ bool QIORing::initializeIORing() if (initialized) return true; + if (apiTable = QtPrivate::getApiTable(); !apiTable) { + qCWarning(lcQIORing, "Failed to retrieve API table"); + return false; + } + IORING_CAPABILITIES capabilities; - QueryIoRingCapabilities(&capabilities); + apiTable->QueryIoRingCapabilities(&capabilities); if (capabilities.MaxVersion < IORING_VERSION_3) // 3 adds write, flush and drain return false; if ((capabilities.FeatureFlags & IORING_FEATURE_SET_COMPLETION_EVENT) == 0) @@ -75,7 +137,8 @@ bool QIORing::initializeIORing() IORING_CREATE_FLAGS flags; memset(&flags, 0, sizeof(flags)); - HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, sqEntries, cqEntries, &ioRingHandle); + HRESULT hr = apiTable->CreateIoRing(IORING_VERSION_3, flags, sqEntries, cqEntries, + &ioRingHandle); if (FAILED(hr)) { qErrnoWarning(hr, "failed to initialize QIORing"); return false; @@ -83,7 +146,7 @@ bool QIORing::initializeIORing() auto earlyExitCleanup = qScopeGuard([this]() { if (eventHandle != INVALID_HANDLE_VALUE) CloseHandle(eventHandle); - CloseIoRing(ioRingHandle); + apiTable->CloseIoRing(ioRingHandle); }); eventHandle = CreateEvent(nullptr, TRUE, FALSE, nullptr); if (eventHandle == INVALID_HANDLE_VALUE) { @@ -91,13 +154,13 @@ bool QIORing::initializeIORing() return false; } notifier.emplace(eventHandle); - hr = SetIoRingCompletionEvent(ioRingHandle, eventHandle); + hr = apiTable->SetIoRingCompletionEvent(ioRingHandle, eventHandle); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to assign the event handle to QIORing"); return false; } IORING_INFO info; - if (SUCCEEDED(GetIoRingInfo(ioRingHandle, &info))) { + if (SUCCEEDED(apiTable->GetIoRingInfo(ioRingHandle, &info))) { sqEntries = info.SubmissionQueueSize; cqEntries = info.CompletionQueueSize; qCDebug(lcQIORing) << "QIORing configured with capacity for" << sqEntries @@ -274,7 +337,7 @@ void QIORing::completionReady() { ResetEvent(eventHandle); IORING_CQE entry; - while (PopIoRingCompletion(ioRingHandle, &entry) == S_OK) { + while (apiTable->PopIoRingCompletion(ioRingHandle, &entry) == S_OK) { // NOLINTNEXTLINE(performance-no-int-to-ptr) auto *request = reinterpret_cast<GenericRequestType *>(entry.UserData); if (!addrItMap.contains(request)) { @@ -458,7 +521,8 @@ void QIORing::submitRequests() const bool shouldTryWait = std::exchange(queueWasFull, false); const auto submitToRing = [this, &shouldTryWait] { quint32 submittedEntries = 0; - HRESULT hr = SubmitIoRing(ioRingHandle, shouldTryWait ? 1 : 0, 1, &submittedEntries); + HRESULT hr = apiTable->SubmitIoRing(ioRingHandle, shouldTryWait ? 1 : 0, 1, + &submittedEntries); qCDebug(lcQIORing) << "Submitted" << submittedEntries << "requests"; unstagedRequests -= submittedEntries; if (FAILED(hr)) { @@ -558,9 +622,9 @@ auto QIORing::prepareRequest(GenericRequestType &request) -> RequestPrepResult auto *closeRequest = request.requestData<Operation::Close>(); // NOLINTNEXTLINE(performance-no-int-to-ptr) const IORING_HANDLE_REF fileRef(HANDLE(closeRequest->fd)); - hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_MIN_METADATA, - quintptr(std::addressof(request)), - IOSQE_FLAGS_DRAIN_PRECEDING_OPS); + hr = apiTable->BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_MIN_METADATA, + quintptr(std::addressof(request)), + IOSQE_FLAGS_DRAIN_PRECEDING_OPS); break; } case Operation::Read: { @@ -645,9 +709,9 @@ auto QIORing::prepareRequest(GenericRequestType &request) -> RequestPrepResult auto *flushRequest = request.requestData<Operation::Flush>(); // NOLINTNEXTLINE(performance-no-int-to-ptr) const IORING_HANDLE_REF fileRef(HANDLE(flushRequest->fd)); - hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_DEFAULT, - quintptr(std::addressof(request)), - IOSQE_FLAGS_DRAIN_PRECEDING_OPS); + hr = apiTable->BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_DEFAULT, + quintptr(std::addressof(request)), + IOSQE_FLAGS_DRAIN_PRECEDING_OPS); break; } case QtPrivate::Operation::Stat: { @@ -703,8 +767,8 @@ auto QIORing::prepareRequest(GenericRequestType &request) -> RequestPrepResult } // NOLINTNEXTLINE(performance-no-int-to-ptr) const IORING_HANDLE_REF fileRef((HANDLE(fd))); - hr = BuildIoRingCancelRequest(ioRingHandle, fileRef, quintptr(otherOperation), - quintptr(std::addressof(request))); + hr = apiTable->BuildIoRingCancelRequest(ioRingHandle, fileRef, quintptr(otherOperation), + quintptr(std::addressof(request))); break; } case Operation::NumOperations: diff --git a/src/corelib/io/qrandomaccessasyncfile.cpp b/src/corelib/io/qrandomaccessasyncfile.cpp index c544585de9d..34e216efe27 100644 --- a/src/corelib/io/qrandomaccessasyncfile.cpp +++ b/src/corelib/io/qrandomaccessasyncfile.cpp @@ -7,6 +7,31 @@ QT_BEGIN_NAMESPACE +QRandomAccessAsyncFileBackend::QRandomAccessAsyncFileBackend(QRandomAccessAsyncFile *owner) + : m_owner(owner) +{ +} +QRandomAccessAsyncFileBackend::~QRandomAccessAsyncFileBackend() = default; +QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() = default; +QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() = default; + +void QRandomAccessAsyncFilePrivate::init() +{ + Q_Q(QRandomAccessAsyncFile); + +#if defined(QT_RANDOMACCESSASYNCFILE_QIORING) || defined(Q_OS_DARWIN) + m_backend = std::make_unique<QRandomAccessAsyncFileNativeBackend>(q); +#endif + if (!m_backend || !m_backend->init()) { +#if QT_CONFIG(thread) && QT_CONFIG(future) + m_backend = std::make_unique<QRandomAccessAsyncFileThreadPoolBackend>(q); + [[maybe_unused]] + bool result = m_backend->init(); + Q_ASSERT(result); // it always succeeds +#endif + } +} + QRandomAccessAsyncFile::QRandomAccessAsyncFile(QObject *parent) : QObject{*new QRandomAccessAsyncFilePrivate, parent} { diff --git a/src/corelib/io/qrandomaccessasyncfile_darwin.mm b/src/corelib/io/qrandomaccessasyncfile_darwin.mm index 7231d12fe7d..80c1affa642 100644 --- a/src/corelib/io/qrandomaccessasyncfile_darwin.mm +++ b/src/corelib/io/qrandomaccessasyncfile_darwin.mm @@ -27,14 +27,14 @@ static bool isBarrierOperation(QIOOperation::Type type) // only! template <typename Operation, typename ...Args> Operation * -QRandomAccessAsyncFilePrivate::addOperation(QIOOperation::Type type, qint64 offset, Args &&...args) +QRandomAccessAsyncFileNativeBackend::addOperation(QIOOperation::Type type, qint64 offset, Args &&...args) { auto dataStorage = new QtPrivate::QIOOperationDataStorage(std::forward<Args>(args)...); auto *priv = new QIOOperationPrivate(dataStorage); priv->offset = offset; priv->type = type; - Operation *op = new Operation(*priv, q_ptr); + Operation *op = new Operation(*priv, m_owner); auto opId = getNextId(); m_operations.push_back(OperationInfo(opId, op)); startOperationsUntilBarrier(); @@ -42,19 +42,20 @@ QRandomAccessAsyncFilePrivate::addOperation(QIOOperation::Type type, qint64 offs return op; } -QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() - : QObjectPrivate() +QRandomAccessAsyncFileNativeBackend::QRandomAccessAsyncFileNativeBackend(QRandomAccessAsyncFile *owner) + : QRandomAccessAsyncFileBackend(owner) { } -QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() +QRandomAccessAsyncFileNativeBackend::~QRandomAccessAsyncFileNativeBackend() = default; -void QRandomAccessAsyncFilePrivate::init() +bool QRandomAccessAsyncFileNativeBackend::init() { + return true; } -void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) +void QRandomAccessAsyncFileNativeBackend::cancelAndWait(QIOOperation *op) { auto it = std::find_if(m_operations.cbegin(), m_operations.cend(), [op](const auto &opInfo) { @@ -87,7 +88,7 @@ void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) startOperationsUntilBarrier(); } -void QRandomAccessAsyncFilePrivate::close() +void QRandomAccessAsyncFileNativeBackend::close() { if (m_fileState == FileState::Closed) return; @@ -95,10 +96,8 @@ void QRandomAccessAsyncFilePrivate::close() // cancel all operations m_mutex.lock(); m_opToCancel = kAllOperationIds; - m_numChannelsToClose = m_ioChannel ? 1 : 0; for (const auto &op : m_operations) { if (op.channel) { - ++m_numChannelsToClose; closeIoChannel(op.channel); } } @@ -127,7 +126,7 @@ void QRandomAccessAsyncFilePrivate::close() m_fileState = FileState::Closed; } -qint64 QRandomAccessAsyncFilePrivate::size() const +qint64 QRandomAccessAsyncFileNativeBackend::size() const { if (m_fileState != FileState::Opened) return -1; @@ -140,7 +139,7 @@ qint64 QRandomAccessAsyncFilePrivate::size() const } QIOOperation * -QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode) +QRandomAccessAsyncFileNativeBackend::open(const QString &path, QIODeviceBase::OpenMode mode) { if (m_fileState == FileState::Closed) { m_filePath = path; @@ -153,44 +152,44 @@ QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode return addOperation<QIOOperation>(QIOOperation::Type::Open, 0); } -QIOOperation *QRandomAccessAsyncFilePrivate::flush() +QIOOperation *QRandomAccessAsyncFileNativeBackend::flush() { return addOperation<QIOOperation>(QIOOperation::Type::Flush, 0); } -QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize) +QIOReadOperation *QRandomAccessAsyncFileNativeBackend::read(qint64 offset, qint64 maxSize) { QByteArray array(maxSize, Qt::Uninitialized); return addOperation<QIOReadOperation>(QIOOperation::Type::Read, offset, std::move(array)); } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, const QByteArray &data) { QByteArray copy = data; return write(offset, std::move(copy)); } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, QByteArray &&data) { return addOperation<QIOWriteOperation>(QIOOperation::Type::Write, offset, std::move(data)); } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<std::byte> buffer) +QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<std::byte> buffer) { return addOperation<QIOVectoredReadOperation>(QIOOperation::Type::Read, offset, QSpan<const QSpan<std::byte>>{buffer}); } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const std::byte> buffer) +QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const std::byte> buffer) { return addOperation<QIOVectoredWriteOperation>(QIOOperation::Type::Write, offset, QSpan<const QSpan<const std::byte>>{buffer}); } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) { // GCD implementation does not have vectored read. Spawning several read // operations (each with an updated offset), is not ideal, because some @@ -204,32 +203,40 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) { return addOperation<QIOVectoredWriteOperation>(QIOOperation::Type::Write, offset, buffers); } -void QRandomAccessAsyncFilePrivate::notifyIfOperationsAreCompleted() +void QRandomAccessAsyncFileNativeBackend::notifyIfOperationsAreCompleted() { QMutexLocker locker(&m_mutex); + --m_numChannelsToClose; if (m_opToCancel == kAllOperationIds) { - --m_numChannelsToClose; if (m_numChannelsToClose == 0 && m_runningOps.isEmpty()) m_cancellationCondition.wakeOne(); } } -dispatch_io_t QRandomAccessAsyncFilePrivate::createMainChannel(int fd) +dispatch_io_t QRandomAccessAsyncFileNativeBackend::createMainChannel(int fd) { auto sharedThis = this; - return dispatch_io_create(DISPATCH_IO_RANDOM, fd, - dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), - ^(int /*error*/) { - sharedThis->notifyIfOperationsAreCompleted(); - }); + auto channel = + dispatch_io_create(DISPATCH_IO_RANDOM, fd, + dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), + ^(int /*error*/) { + // main I/O channel uses kInvalidOperationId + // as its identifier + sharedThis->notifyIfOperationsAreCompleted(); + }); + if (channel) { + QMutexLocker locker(&m_mutex); + ++m_numChannelsToClose; + } + return channel; } -dispatch_io_t QRandomAccessAsyncFilePrivate::duplicateIoChannel(OperationId opId) +dispatch_io_t QRandomAccessAsyncFileNativeBackend::duplicateIoChannel(OperationId opId) { if (!m_ioChannel) return nullptr; @@ -247,17 +254,18 @@ dispatch_io_t QRandomAccessAsyncFilePrivate::duplicateIoChannel(OperationId opId if (channel) { QMutexLocker locker(&m_mutex); m_runningOps.insert(opId); + ++m_numChannelsToClose; } return channel; } -void QRandomAccessAsyncFilePrivate::closeIoChannel(dispatch_io_t channel) +void QRandomAccessAsyncFileNativeBackend::closeIoChannel(dispatch_io_t channel) { if (channel) dispatch_io_close(channel, DISPATCH_IO_STOP); } -void QRandomAccessAsyncFilePrivate::releaseIoChannel(dispatch_io_t channel) +void QRandomAccessAsyncFileNativeBackend::releaseIoChannel(dispatch_io_t channel) { if (channel) { dispatch_release(channel); @@ -265,7 +273,7 @@ void QRandomAccessAsyncFilePrivate::releaseIoChannel(dispatch_io_t channel) } } -void QRandomAccessAsyncFilePrivate::handleOperationComplete(const OperationResult &opResult) +void QRandomAccessAsyncFileNativeBackend::handleOperationComplete(const OperationResult &opResult) { // try to start next operations on return auto onReturn = qScopeGuard([this] { @@ -372,15 +380,15 @@ void QRandomAccessAsyncFilePrivate::handleOperationComplete(const OperationResul } } -void QRandomAccessAsyncFilePrivate::queueCompletion(OperationId opId, int error) +void QRandomAccessAsyncFileNativeBackend::queueCompletion(OperationId opId, int error) { const OperationResult res = { opId, 0LL, error }; - QMetaObject::invokeMethod(q_ptr, [this, res] { + QMetaObject::invokeMethod(m_owner, [this, res] { handleOperationComplete(res); }, Qt::QueuedConnection); } -void QRandomAccessAsyncFilePrivate::startOperationsUntilBarrier() +void QRandomAccessAsyncFileNativeBackend::startOperationsUntilBarrier() { // starts all operations until barrier, or a barrier operation if it's the // first one @@ -414,7 +422,7 @@ void QRandomAccessAsyncFilePrivate::startOperationsUntilBarrier() } } -void QRandomAccessAsyncFilePrivate::executeRead(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeRead(OperationInfo &opInfo) { opInfo.channel = duplicateIoChannel(opInfo.opId); if (!opInfo.channel) { @@ -445,7 +453,7 @@ void QRandomAccessAsyncFilePrivate::executeRead(OperationInfo &opInfo) } } -void QRandomAccessAsyncFilePrivate::executeWrite(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeWrite(OperationInfo &opInfo) { opInfo.channel = duplicateIoChannel(opInfo.opId); if (!opInfo.channel) { @@ -494,7 +502,7 @@ void QRandomAccessAsyncFilePrivate::executeWrite(OperationInfo &opInfo) } } -void QRandomAccessAsyncFilePrivate::executeFlush(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeFlush(OperationInfo &opInfo) { opInfo.channel = duplicateIoChannel(opInfo.opId); if (!opInfo.channel) { @@ -523,7 +531,7 @@ void QRandomAccessAsyncFilePrivate::executeFlush(OperationInfo &opInfo) } } } else { - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; const OperationResult res = { opId, 0LL, err }; QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { sharedThis->handleOperationComplete(r); @@ -555,7 +563,7 @@ static inline int openModeToOpenFlags(QIODevice::OpenMode mode) return oflags; } -void QRandomAccessAsyncFilePrivate::executeOpen(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeOpen(OperationInfo &opInfo) { if (m_fileState != FileState::OpenPending) { queueCompletion(opInfo.opId, EINVAL); @@ -593,9 +601,10 @@ void QRandomAccessAsyncFilePrivate::executeOpen(OperationInfo &opInfo) // So we need to notify the condition variable in // both cases. Q_ASSERT(sharedThis->m_runningOps.isEmpty()); + Q_ASSERT(sharedThis->m_numChannelsToClose == 0); sharedThis->m_cancellationCondition.wakeOne(); } else { - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; const OperationResult res = { opId, qint64(fd), err }; QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { @@ -605,7 +614,7 @@ void QRandomAccessAsyncFilePrivate::executeOpen(OperationInfo &opInfo) }); } -void QRandomAccessAsyncFilePrivate::readOneBuffer(OperationId opId, qsizetype bufferIdx, +void QRandomAccessAsyncFileNativeBackend::readOneBuffer(OperationId opId, qsizetype bufferIdx, qint64 alreadyRead) { // we need to lookup the operation again, because it could have beed removed @@ -642,7 +651,7 @@ void QRandomAccessAsyncFilePrivate::readOneBuffer(OperationId opId, qsizetype bu readBuffers.size(), alreadyRead); } -void QRandomAccessAsyncFilePrivate::readOneBufferHelper(OperationId opId, dispatch_io_t channel, +void QRandomAccessAsyncFileNativeBackend::readOneBufferHelper(OperationId opId, dispatch_io_t channel, qint64 offset, void *bytesPtr, qint64 maxSize, qsizetype currentBufferIdx, qsizetype totalBuffers, qint64 alreadyRead) @@ -690,7 +699,7 @@ void QRandomAccessAsyncFilePrivate::readOneBufferHelper(OperationId opId, dispat } } else { sharedThis->m_runningOps.remove(opId); - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; // if error, or last buffer, or read less than expected, // report operation completion qint64 totalRead = qint64(readFromBuffer) + alreadyRead; @@ -713,7 +722,7 @@ void QRandomAccessAsyncFilePrivate::readOneBufferHelper(OperationId opId, dispat }); } -void QRandomAccessAsyncFilePrivate::writeHelper(OperationId opId, dispatch_io_t channel, +void QRandomAccessAsyncFileNativeBackend::writeHelper(OperationId opId, dispatch_io_t channel, qint64 offset, dispatch_data_t dataToWrite, qint64 dataSize) { @@ -752,7 +761,7 @@ void QRandomAccessAsyncFilePrivate::writeHelper(OperationId opId, dispatch_io_t const size_t written = dataSize - toBeWritten; [dataToWrite release]; - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; const OperationResult res = { opId, qint64(written), error }; QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { @@ -762,7 +771,7 @@ void QRandomAccessAsyncFilePrivate::writeHelper(OperationId opId, dispatch_io_t }); } -QRandomAccessAsyncFilePrivate::OperationId QRandomAccessAsyncFilePrivate::getNextId() +QRandomAccessAsyncFileNativeBackend::OperationId QRandomAccessAsyncFileNativeBackend::getNextId() { // never return reserved values static OperationId opId = kInvalidOperationId; diff --git a/src/corelib/io/qrandomaccessasyncfile_p_p.h b/src/corelib/io/qrandomaccessasyncfile_p_p.h index 11ad788c884..2eb53058780 100644 --- a/src/corelib/io/qrandomaccessasyncfile_p_p.h +++ b/src/corelib/io/qrandomaccessasyncfile_p_p.h @@ -22,7 +22,7 @@ #include <QtCore/qstring.h> -#ifdef QT_RANDOMACCESSASYNCFILE_THREAD +#if QT_CONFIG(future) && QT_CONFIG(thread) #include <QtCore/private/qfsfileengine_p.h> @@ -30,7 +30,7 @@ #include <QtCore/qmutex.h> #include <QtCore/qqueue.h> -#endif // QT_RANDOMACCESSASYNCFILE_THREAD +#endif // future && thread #ifdef Q_OS_DARWIN @@ -50,41 +50,36 @@ QT_BEGIN_NAMESPACE -class QRandomAccessAsyncFilePrivate : public QObjectPrivate +class QRandomAccessAsyncFileBackend { - Q_DECLARE_PUBLIC(QRandomAccessAsyncFile) - Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFilePrivate) + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileBackend) public: - QRandomAccessAsyncFilePrivate(); - ~QRandomAccessAsyncFilePrivate() override; - - static QRandomAccessAsyncFilePrivate *get(QRandomAccessAsyncFile *file) - { return file->d_func(); } + explicit QRandomAccessAsyncFileBackend(QRandomAccessAsyncFile *owner); + virtual ~QRandomAccessAsyncFileBackend(); - void init(); - void cancelAndWait(QIOOperation *op); + virtual bool init() = 0; + virtual void cancelAndWait(QIOOperation *op) = 0; - void close(); - qint64 size() const; + virtual void close() = 0; + virtual qint64 size() const = 0; - [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode); - [[nodiscard]] QIOOperation *flush(); + [[nodiscard]] virtual QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) = 0; + [[nodiscard]] virtual QIOOperation *flush() = 0; - [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize); - [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data); - [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data); + [[nodiscard]] virtual QIOReadOperation *read(qint64 offset, qint64 maxSize) = 0; + [[nodiscard]] virtual QIOWriteOperation *write(qint64 offset, const QByteArray &data) = 0; + [[nodiscard]] virtual QIOWriteOperation *write(qint64 offset, QByteArray &&data) = 0; - [[nodiscard]] QIOVectoredReadOperation * - readInto(qint64 offset, QSpan<std::byte> buffer); - [[nodiscard]] QIOVectoredWriteOperation * - writeFrom(qint64 offset, QSpan<const std::byte> buffer); - - [[nodiscard]] QIOVectoredReadOperation * - readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers); - [[nodiscard]] QIOVectoredWriteOperation * - writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers); + [[nodiscard]] virtual QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<std::byte> buffer) = 0; + [[nodiscard]] virtual QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const std::byte> buffer) = 0; -private: + [[nodiscard]] virtual QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) = 0; + [[nodiscard]] virtual QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) = 0; +protected: // common for all backends enum class FileState : quint8 { @@ -94,32 +89,131 @@ private: }; QString m_filePath; + QRandomAccessAsyncFile *m_owner = nullptr; QIODeviceBase::OpenMode m_openMode; FileState m_fileState = FileState::Closed; +}; -#ifdef QT_RANDOMACCESSASYNCFILE_THREAD +class QRandomAccessAsyncFilePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QRandomAccessAsyncFile) + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFilePrivate) public: - struct OperationResult + QRandomAccessAsyncFilePrivate(); + ~QRandomAccessAsyncFilePrivate() override; + + static QRandomAccessAsyncFilePrivate *get(QRandomAccessAsyncFile *file) + { return file->d_func(); } + + void init(); + void cancelAndWait(QIOOperation *op) { - qint64 bytesProcessed; // either read or written - QIOOperation::Error error; - }; + checkValid(); + m_backend->cancelAndWait(op); + } + + void close() + { + checkValid(); + m_backend->close(); + } + qint64 size() const + { + checkValid(); + return m_backend->size(); + } + + [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) + { + checkValid(); + return m_backend->open(path, mode); + } + [[nodiscard]] QIOOperation *flush() + { + checkValid(); + return m_backend->flush(); + } + + [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) + { + checkValid(); + return m_backend->read(offset, maxSize); + } + [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) + { + checkValid(); + return m_backend->write(offset, data); + } + [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) + { + checkValid(); + return m_backend->write(offset, std::move(data)); + } + + [[nodiscard]] QIOVectoredReadOperation *readInto(qint64 offset, QSpan<std::byte> buffer) + { + checkValid(); + return m_backend->readInto(offset, buffer); + } + [[nodiscard]] QIOVectoredWriteOperation *writeFrom(qint64 offset, QSpan<const std::byte> buffer) + { + checkValid(); + return m_backend->writeFrom(offset, buffer); + } + + [[nodiscard]] QIOVectoredReadOperation *readInto(qint64 offset, + QSpan<const QSpan<std::byte>> buffers) + { + checkValid(); + return m_backend->readInto(offset, buffers); + } + [[nodiscard]] QIOVectoredWriteOperation *writeFrom(qint64 offset, + QSpan<const QSpan<const std::byte>> buffers) + { + checkValid(); + return m_backend->writeFrom(offset, buffers); + } private: - mutable QBasicMutex m_engineMutex; - std::unique_ptr<QFSFileEngine> m_engine; - QFutureWatcher<OperationResult> m_watcher; + void checkValid() const { Q_ASSERT(m_backend); } + std::unique_ptr<QRandomAccessAsyncFileBackend> m_backend; - QQueue<QPointer<QIOOperation>> m_operations; - QPointer<QIOOperation> m_currentOperation; - qsizetype numProcessedBuffers = 0; +}; - void executeNextOperation(); - void processBufferAt(qsizetype idx); - void processFlush(); - void processOpen(); - void operationComplete(); -#elif defined(QT_RANDOMACCESSASYNCFILE_QIORING) + +#if defined(QT_RANDOMACCESSASYNCFILE_QIORING) || defined(Q_OS_DARWIN) +class QRandomAccessAsyncFileNativeBackend final : public QRandomAccessAsyncFileBackend +{ + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileNativeBackend) +public: + explicit QRandomAccessAsyncFileNativeBackend(QRandomAccessAsyncFile *owner); + ~QRandomAccessAsyncFileNativeBackend(); + + bool init() override; + void cancelAndWait(QIOOperation *op) override; + + void close() override; + qint64 size() const override; + + [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) override; + [[nodiscard]] QIOOperation *flush() override; + + [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<std::byte> buffer) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const std::byte> buffer) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) override; + +private: +#if defined(QT_RANDOMACCESSASYNCFILE_QIORING) void queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error); void startReadIntoSingle(QIOOperation *op, const QSpan<std::byte> &to); void startWriteFromSingle(QIOOperation *op, const QSpan<const std::byte> &from); @@ -210,6 +304,61 @@ private: dispatch_data_t dataToWrite, qint64 dataSize); #endif }; +#endif // QIORing || macOS + +#if QT_CONFIG(future) && QT_CONFIG(thread) +class QRandomAccessAsyncFileThreadPoolBackend : public QRandomAccessAsyncFileBackend +{ + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileThreadPoolBackend) +public: + explicit QRandomAccessAsyncFileThreadPoolBackend(QRandomAccessAsyncFile *owner); + ~QRandomAccessAsyncFileThreadPoolBackend(); + + bool init() override; + void cancelAndWait(QIOOperation *op) override; + + void close() override; + qint64 size() const override; + + [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) override; + [[nodiscard]] QIOOperation *flush() override; + + [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<std::byte> buffer) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const std::byte> buffer) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) override; + + struct OperationResult + { + qint64 bytesProcessed; // either read or written + QIOOperation::Error error; + }; +private: + + mutable QBasicMutex m_engineMutex; + std::unique_ptr<QFSFileEngine> m_engine; + QFutureWatcher<OperationResult> m_watcher; + + QQueue<QPointer<QIOOperation>> m_operations; + QPointer<QIOOperation> m_currentOperation; + qsizetype numProcessedBuffers = 0; + + void executeNextOperation(); + void processBufferAt(qsizetype idx); + void processFlush(); + void processOpen(); + void operationComplete(); +}; +#endif // future && thread QT_END_NAMESPACE diff --git a/src/corelib/io/qrandomaccessasyncfile_qioring.cpp b/src/corelib/io/qrandomaccessasyncfile_qioring.cpp index c9783ea2856..8e1145325d8 100644 --- a/src/corelib/io/qrandomaccessasyncfile_qioring.cpp +++ b/src/corelib/io/qrandomaccessasyncfile_qioring.cpp @@ -18,18 +18,21 @@ QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcQRandomAccessIORing, "qt.core.qrandomaccessasyncfile.ioring", QtCriticalMsg); -QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() = default; +QRandomAccessAsyncFileNativeBackend::QRandomAccessAsyncFileNativeBackend(QRandomAccessAsyncFile *owner) + : QRandomAccessAsyncFileBackend(owner) +{} -QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() = default; +QRandomAccessAsyncFileNativeBackend::~QRandomAccessAsyncFileNativeBackend() = default; -void QRandomAccessAsyncFilePrivate::init() +bool QRandomAccessAsyncFileNativeBackend::init() { m_ioring = QIORing::sharedInstance(); if (!m_ioring) - qCCritical(lcQRandomAccessIORing, "QRandomAccessAsyncFile: ioring failed to initialize"); + qCWarning(lcQRandomAccessIORing, "QRandomAccessAsyncFile: ioring failed to initialize"); + return m_ioring != nullptr; } -QIORing::RequestHandle QRandomAccessAsyncFilePrivate::cancel(QIORing::RequestHandle handle) +QIORing::RequestHandle QRandomAccessAsyncFileNativeBackend::cancel(QIORing::RequestHandle handle) { if (handle) { QIORingRequest<QIORing::Operation::Cancel> cancelRequest; @@ -39,7 +42,7 @@ QIORing::RequestHandle QRandomAccessAsyncFilePrivate::cancel(QIORing::RequestHan return nullptr; } -void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) +void QRandomAccessAsyncFileNativeBackend::cancelAndWait(QIOOperation *op) { auto *opHandle = m_opHandleMap.value(op); if (auto *handle = cancel(opHandle)) { @@ -48,7 +51,7 @@ void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) } } -void QRandomAccessAsyncFilePrivate::queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error) +void QRandomAccessAsyncFileNativeBackend::queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error) { // Remove the handle now in case the user cancels or deletes the io-operation // before operationComplete is called - the null-handle will protect from @@ -61,14 +64,14 @@ void QRandomAccessAsyncFilePrivate::queueCompletion(QIOOperationPrivate *priv, Q }, Qt::QueuedConnection); } -QIOOperation *QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode) +QIOOperation *QRandomAccessAsyncFileNativeBackend::open(const QString &path, QIODeviceBase::OpenMode mode) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(); auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Open; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); if (m_fileState != FileState::Closed) { queueCompletion(priv, QIOOperation::Error::Open); return op; @@ -115,7 +118,7 @@ QIOOperation *QRandomAccessAsyncFilePrivate::open(const QString &path, QIODevice return op; } -void QRandomAccessAsyncFilePrivate::close() +void QRandomAccessAsyncFileNativeBackend::close() { // all the operations should be aborted const auto ops = std::exchange(m_operations, {}); @@ -123,7 +126,7 @@ void QRandomAccessAsyncFilePrivate::close() // Request to cancel all of the in-flight operations: for (const auto &op : ops) { if (op) { - op->d_func()->error = QIOOperation::Error::Aborted; + QIOOperationPrivate::get(op)->error = QIOOperation::Error::Aborted; if (auto *opHandle = m_opHandleMap.value(op)) { tasksToAwait.append(cancel(opHandle)); tasksToAwait.append(opHandle); @@ -142,7 +145,7 @@ void QRandomAccessAsyncFilePrivate::close() m_fd = -1; } -qint64 QRandomAccessAsyncFilePrivate::size() const +qint64 QRandomAccessAsyncFileNativeBackend::size() const { QIORingRequest<QIORing::Operation::Stat> statRequest; statRequest.fd = m_fd; @@ -161,14 +164,14 @@ qint64 QRandomAccessAsyncFilePrivate::size() const return finalSize; } -QIOOperation *QRandomAccessAsyncFilePrivate::flush() +QIOOperation *QRandomAccessAsyncFileNativeBackend::flush() { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(); auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Flush; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); m_operations.append(op); QIORingRequest<QIORing::Operation::Flush> flushRequest; @@ -192,7 +195,7 @@ QIOOperation *QRandomAccessAsyncFilePrivate::flush() return op; } -void QRandomAccessAsyncFilePrivate::startReadIntoSingle(QIOOperation *op, +void QRandomAccessAsyncFileNativeBackend::startReadIntoSingle(QIOOperation *op, const QSpan<std::byte> &to) { QIORingRequest<QIORing::Operation::Read> readRequest; @@ -231,7 +234,7 @@ void QRandomAccessAsyncFilePrivate::startReadIntoSingle(QIOOperation *op, m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(readRequest))); } -QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize) +QIOReadOperation *QRandomAccessAsyncFileNativeBackend::read(qint64 offset, qint64 maxSize) { QByteArray array; array.resizeForOverwrite(maxSize); @@ -241,7 +244,7 @@ QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxS priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOReadOperation(*priv, q_ptr); + auto *op = new QIOReadOperation(*priv, m_owner); m_operations.append(op); startReadIntoSingle(op, as_writable_bytes(QSpan(dataStorage->getByteArray()))); @@ -249,12 +252,12 @@ QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxS return op; } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, const QByteArray &data) { return write(offset, QByteArray(data)); } -void QRandomAccessAsyncFilePrivate::startWriteFromSingle(QIOOperation *op, +void QRandomAccessAsyncFileNativeBackend::startWriteFromSingle(QIOOperation *op, const QSpan<const std::byte> &from) { QIORingRequest<QIORing::Operation::Write> writeRequest; @@ -288,7 +291,7 @@ void QRandomAccessAsyncFilePrivate::startWriteFromSingle(QIOOperation *op, m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(writeRequest))); } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, QByteArray &&data) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(std::move(data)); @@ -296,7 +299,7 @@ QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArra priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOWriteOperation(*priv, q_ptr); + auto *op = new QIOWriteOperation(*priv, m_owner); m_operations.append(op); startWriteFromSingle(op, as_bytes(QSpan(dataStorage->getByteArray()))); @@ -304,7 +307,7 @@ QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArra return op; } -QIOVectoredReadOperation *QRandomAccessAsyncFilePrivate::readInto(qint64 offset, +QIOVectoredReadOperation *QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage( @@ -314,7 +317,7 @@ QIOVectoredReadOperation *QRandomAccessAsyncFilePrivate::readInto(qint64 offset, priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); m_operations.append(op); startReadIntoSingle(op, dataStorage->getReadSpans().first()); @@ -322,7 +325,7 @@ QIOVectoredReadOperation *QRandomAccessAsyncFilePrivate::readInto(qint64 offset, return op; } -QIOVectoredWriteOperation *QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, +QIOVectoredWriteOperation *QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage( @@ -332,7 +335,7 @@ QIOVectoredWriteOperation *QRandomAccessAsyncFilePrivate::writeFrom(qint64 offse priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); m_operations.append(op); startWriteFromSingle(op, dataStorage->getWriteSpans().first()); @@ -341,7 +344,7 @@ QIOVectoredWriteOperation *QRandomAccessAsyncFilePrivate::writeFrom(qint64 offse } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) { if (!QIORing::supportsOperation(QtPrivate::Operation::VectoredRead)) return nullptr; @@ -351,7 +354,7 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now queueCompletion(priv, QIOOperation::Error::IncorrectOffset); return op; @@ -393,7 +396,7 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) { if (!QIORing::supportsOperation(QtPrivate::Operation::VectoredWrite)) return nullptr; @@ -403,7 +406,7 @@ QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now queueCompletion(priv, QIOOperation::Error::IncorrectOffset); return op; diff --git a/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp b/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp index 4ebcf554655..774fbadc5ad 100644 --- a/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp +++ b/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp @@ -64,28 +64,29 @@ static SharedThreadPool asyncFileThreadPool; } // anonymous namespace -QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() : - QObjectPrivate() +QRandomAccessAsyncFileThreadPoolBackend::QRandomAccessAsyncFileThreadPoolBackend(QRandomAccessAsyncFile *owner) : + QRandomAccessAsyncFileBackend(owner) { asyncFileThreadPool.ref(); } -QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() +QRandomAccessAsyncFileThreadPoolBackend::~QRandomAccessAsyncFileThreadPoolBackend() { asyncFileThreadPool.deref(); } -void QRandomAccessAsyncFilePrivate::init() +bool QRandomAccessAsyncFileThreadPoolBackend::init() { - QObject::connect(&m_watcher, &QFutureWatcherBase::finished, q_ptr, [this]{ + QObject::connect(&m_watcher, &QFutureWatcherBase::finished, m_owner, [this]{ operationComplete(); }); - QObject::connect(&m_watcher, &QFutureWatcherBase::canceled, q_ptr, [this]{ + QObject::connect(&m_watcher, &QFutureWatcherBase::canceled, m_owner, [this]{ operationComplete(); }); + return true; } -void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) +void QRandomAccessAsyncFileThreadPoolBackend::cancelAndWait(QIOOperation *op) { if (op == m_currentOperation) { m_currentOperation = nullptr; // to discard the result @@ -97,7 +98,7 @@ void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) } QIOOperation * -QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode) +QRandomAccessAsyncFileThreadPoolBackend::open(const QString &path, QIODeviceBase::OpenMode mode) { // We generate the command in any case. But if the file is already opened, // it will finish with an error @@ -112,14 +113,14 @@ QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Open; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } -void QRandomAccessAsyncFilePrivate::close() +void QRandomAccessAsyncFileThreadPoolBackend::close() { // all the operations should be aborted for (const auto &op : std::as_const(m_operations)) { @@ -148,7 +149,7 @@ void QRandomAccessAsyncFilePrivate::close() m_fileState = FileState::Closed; } -qint64 QRandomAccessAsyncFilePrivate::size() const +qint64 QRandomAccessAsyncFileThreadPoolBackend::size() const { QMutexLocker locker(&m_engineMutex); if (m_engine) @@ -156,20 +157,20 @@ qint64 QRandomAccessAsyncFilePrivate::size() const return -1; } -QIOOperation *QRandomAccessAsyncFilePrivate::flush() +QIOOperation *QRandomAccessAsyncFileThreadPoolBackend::flush() { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(); auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Flush; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } -QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize) +QIOReadOperation *QRandomAccessAsyncFileThreadPoolBackend::read(qint64 offset, qint64 maxSize) { QByteArray array; array.resizeForOverwrite(maxSize); @@ -179,14 +180,14 @@ QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxS priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOReadOperation(*priv, q_ptr); + auto *op = new QIOReadOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOWriteOperation * -QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) +QRandomAccessAsyncFileThreadPoolBackend::write(qint64 offset, const QByteArray &data) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(data); @@ -194,14 +195,14 @@ QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOWriteOperation(*priv, q_ptr); + auto *op = new QIOWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOWriteOperation * -QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) +QRandomAccessAsyncFileThreadPoolBackend::write(qint64 offset, QByteArray &&data) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(std::move(data)); @@ -209,14 +210,14 @@ QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOWriteOperation(*priv, q_ptr); + auto *op = new QIOWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<std::byte> buffer) +QRandomAccessAsyncFileThreadPoolBackend::readInto(qint64 offset, QSpan<std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(QSpan<const QSpan<std::byte>>{buffer}); @@ -225,14 +226,14 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<std::byte> buffer) priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const std::byte> buffer) +QRandomAccessAsyncFileThreadPoolBackend::writeFrom(qint64 offset, QSpan<const std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(QSpan<const QSpan<const std::byte>>{buffer}); @@ -241,14 +242,14 @@ QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const std::byte> b priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) +QRandomAccessAsyncFileThreadPoolBackend::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(buffers); @@ -256,14 +257,14 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) +QRandomAccessAsyncFileThreadPoolBackend::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(buffers); @@ -271,16 +272,16 @@ QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } -static QRandomAccessAsyncFilePrivate::OperationResult +static QRandomAccessAsyncFileThreadPoolBackend::OperationResult executeRead(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, char *buffer, qint64 maxSize) { - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; QMutexLocker locker(mutex); if (engine) { @@ -299,11 +300,11 @@ executeRead(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, char *buff return result; } -static QRandomAccessAsyncFilePrivate::OperationResult +static QRandomAccessAsyncFileThreadPoolBackend::OperationResult executeWrite(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, const char *buffer, qint64 size) { - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; QMutexLocker locker(mutex); if (engine) { @@ -322,7 +323,7 @@ executeWrite(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, return result; } -void QRandomAccessAsyncFilePrivate::executeNextOperation() +void QRandomAccessAsyncFileThreadPoolBackend::executeNextOperation() { if (m_currentOperation.isNull()) { // start next @@ -351,7 +352,7 @@ void QRandomAccessAsyncFilePrivate::executeNextOperation() } } -void QRandomAccessAsyncFilePrivate::processBufferAt(qsizetype idx) +void QRandomAccessAsyncFileThreadPoolBackend::processBufferAt(qsizetype idx) { Q_ASSERT(!m_currentOperation.isNull()); auto *priv = QIOOperationPrivate::get(m_currentOperation.get()); @@ -417,7 +418,7 @@ void QRandomAccessAsyncFilePrivate::processBufferAt(qsizetype idx) } } -void QRandomAccessAsyncFilePrivate::processFlush() +void QRandomAccessAsyncFileThreadPoolBackend::processFlush() { Q_ASSERT(!m_currentOperation.isNull()); auto *priv = QIOOperationPrivate::get(m_currentOperation.get()); @@ -427,7 +428,7 @@ void QRandomAccessAsyncFilePrivate::processFlush() QBasicMutex *mutexPtr = &m_engineMutex; auto op = [engine = m_engine.get(), mutexPtr] { QMutexLocker locker(mutexPtr); - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; if (engine) { if (!engine->flush()) result.error = QIOOperation::Error::Flush; @@ -442,7 +443,7 @@ void QRandomAccessAsyncFilePrivate::processFlush() m_watcher.setFuture(f); } -void QRandomAccessAsyncFilePrivate::processOpen() +void QRandomAccessAsyncFileThreadPoolBackend::processOpen() { Q_ASSERT(!m_currentOperation.isNull()); auto *priv = QIOOperationPrivate::get(m_currentOperation.get()); @@ -457,7 +458,7 @@ void QRandomAccessAsyncFilePrivate::processOpen() m_engineMutex.unlock(); QBasicMutex *mutexPtr = &m_engineMutex; auto op = [engine = m_engine.get(), mutexPtr, mode = m_openMode] { - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; QMutexLocker locker(mutexPtr); const bool res = engine && engine->open(mode | QIODeviceBase::Unbuffered, std::nullopt); @@ -468,13 +469,13 @@ void QRandomAccessAsyncFilePrivate::processOpen() f = QtFuture::makeReadyVoidFuture().then(asyncFileThreadPool(), op); } else { f = QtFuture::makeReadyVoidFuture().then(asyncFileThreadPool(), [] { - return QRandomAccessAsyncFilePrivate::OperationResult{0, QIOOperation::Error::Open}; + return QRandomAccessAsyncFileThreadPoolBackend::OperationResult{0, QIOOperation::Error::Open}; }); } m_watcher.setFuture(f); } -void QRandomAccessAsyncFilePrivate::operationComplete() +void QRandomAccessAsyncFileThreadPoolBackend::operationComplete() { // TODO: if one of the buffers was read/written with an error, // stop processing immediately diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp index 456a209af37..6edf4395887 100644 --- a/src/corelib/io/qwindowspipereader.cpp +++ b/src/corelib/io/qwindowspipereader.cpp @@ -14,6 +14,11 @@ using namespace Qt::StringLiterals; static const DWORD minReadBufferSize = 4096; +/*! + \class QWindowsPipeReader + \inmodule QtCore + \internal +*/ QWindowsPipeReader::QWindowsPipeReader(QObject *parent) : QObject(parent), handle(INVALID_HANDLE_VALUE), diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h index f6b08099fe7..7eca3094a66 100644 --- a/src/corelib/itemmodels/qrangemodel_impl.h +++ b/src/corelib/itemmodels/qrangemodel_impl.h @@ -239,17 +239,15 @@ namespace QRangeModelDetails : std::true_type {}; - // we use std::rotate in moveRows/Columns, which requires std::swap and the - // iterators to be at least a forward iterator - template <typename It, typename = void> - struct test_rotate : std::false_type {}; - + // we use std::rotate in moveRows/Columns, which requires the values (which + // might be const if we only get a const iterator) to be swappable, and the + // iterator type to be at least a forward iterator template <typename It> - struct test_rotate<It, std::void_t<decltype(std::swap(*std::declval<It>(), - *std::declval<It>()))>> - : std::is_base_of<std::forward_iterator_tag, - typename std::iterator_traits<It>::iterator_category> - {}; + using test_rotate = std::conjunction< + std::is_swappable<decltype(*std::declval<It>())>, + std::is_base_of<std::forward_iterator_tag, + typename std::iterator_traits<It>::iterator_category> + >; template <typename C, typename = void> struct test_splice : std::false_type {}; diff --git a/src/corelib/itemmodels/qrangemodeladapter.h b/src/corelib/itemmodels/qrangemodeladapter.h index bd3342e6185..0234c402248 100644 --- a/src/corelib/itemmodels/qrangemodeladapter.h +++ b/src/corelib/itemmodels/qrangemodeladapter.h @@ -19,13 +19,11 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter #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>; +#endif 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>; @@ -78,16 +76,22 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter 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>()))>; + static constexpr bool is_compatible_data = std::is_convertible_v<Data, data_type>; 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< + typename QRangeModelDetails::data_type< + typename QRangeModelDetails::row_traits< decltype(*std::begin(std::declval<C&>())) - >; + >::item_type + >::type + >; template <typename C> - using if_compatible_data_range = std::enable_if_t<is_compatible_data_range<C>, bool>; + using if_compatible_column_data = std::enable_if_t<is_compatible_data<C> + || is_compatible_data_range<C>, bool>; + template <typename C> + using if_compatible_column_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>; @@ -141,6 +145,7 @@ public: } DataReference(const DataReference &other) = default; + DataReference(DataReference &&other) = default; // reference (not std::reference_wrapper) semantics DataReference &operator=(const DataReference &other) @@ -149,29 +154,23 @@ public: return *this; } + DataReference &operator=(DataReference &&other) + { + *this = other.get(); + return *this; + } + ~DataReference() = default; DataReference &operator=(const value_type &value) { - constexpr Qt::ItemDataRole dataRole = Qt::RangeModelAdapterRole; + assign(value); + return *this; + } - 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 - } + DataReference &operator=(value_type &&value) + { + assign(std::move(value)); return *this; } @@ -196,6 +195,33 @@ public: private: QModelIndex m_index; + template <typename Value> + void assign(Value &&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>, QVariant>) { + couldWrite = model->setData(m_index, value, dataRole); + } else { + couldWrite = model->setData(m_index, + QVariant::fromValue(std::forward<Value>(value)), + dataRole); + } +#ifndef QT_NO_DEBUG + if (!couldWrite) { + qWarning() << "Writing value of type" + << QMetaType::fromType<q20::remove_cvref_t<Value>>().name() + << "to role" << dataRole << "at index" << m_index << "failed"; + } + } else { + qCritical("Data reference for invalid index, can't write to model"); +#endif + } + } + friend inline bool comparesEqual(const DataReference &lhs, const DataReference &rhs) { return lhs.m_index == rhs.m_index @@ -1020,60 +1046,55 @@ public: 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); - } - } + setRangeImpl(qsizetype(Impl::size(QRangeModelDetails::refTo(newRange))) - 1, + [&newRange](auto &oldRange) { + oldRange = std::forward<NewRange>(newRange); + }); } - template <typename NewRange = range_type, if_assignable_range<NewRange> = true> + template <typename NewRange = range_type, if_assignable_range<NewRange> = true, + unless_adapter<NewRange> = true> QRangeModelAdapter &operator=(NewRange &&newRange) { setRange(std::forward<NewRange>(newRange)); return *this; } + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + void setRange(std::initializer_list<Row> newRange) + { + setRangeImpl(qsizetype(newRange.size() - 1), [&newRange](auto &oldRange) { + oldRange = newRange; + }); + } + + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + QRangeModelAdapter &operator=(std::initializer_list<Row> newRange) + { + setRange(newRange); + return *this; + } + + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + void assign(std::initializer_list<Row> newRange) + { + setRange(newRange); + } + + template <typename InputIterator, typename Sentinel, typename I = Impl, if_writable<I> = true> + void setRange(InputIterator first, Sentinel last) + { + setRangeImpl(qsizetype(std::distance(first, last) - 1), [first, last](auto &oldRange) { + oldRange.assign(first, last); + }); + } + + template <typename InputIterator, typename Sentinel, typename I = Impl, if_writable<I> = true> + void assign(InputIterator first, Sentinel last) + { + setRange(first, last); + } + // iterator API ConstRowIterator cbegin() const { @@ -1245,12 +1266,12 @@ public: 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) + 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); } + 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> @@ -1428,15 +1449,15 @@ public: return storage.m_model->insertColumn(before); } - template <typename D = row_type, typename I = Impl, - if_canInsertColumns<I> = true, if_compatible_data<D> = true> + template <typename D, typename I = Impl, + if_canInsertColumns<I> = true, if_compatible_column_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> + if_canInsertColumns<I> = true, if_compatible_column_range<C> = true> bool insertColumns(int before, C &&data) { return insertColumnsImpl(before, storage.root(), std::forward<C>(data)); @@ -1503,6 +1524,65 @@ private: Q_EMIT storage.implementation()->dataChanged(topLeft, bottomRight, {}); } + void beginSetRangeImpl(Impl *impl, range_type *oldRange, qsizetype newLastRow) + { + const QModelIndex root = storage.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(QRangeModelDetails::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."); + } + } + + void endSetRangeImpl(Impl *impl, qsizetype newLastRow) + { + const QModelIndex root = storage.root(); + 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(QRangeModelDetails::refTo(impl->childRange(root)), + QRangeModelDetails::pointerTo(impl->rowData(root))); + impl->endInsertRows(); + } + } + } + + template <typename Assigner> + void setRangeImpl(qsizetype newLastRow, Assigner &&assigner) + { + auto *impl = storage.implementation(); + auto *oldRange = impl->childRange(storage.root()); + beginSetRangeImpl(impl, oldRange, newLastRow); + assigner(QRangeModelDetails::refTo(oldRange)); + endSetRangeImpl(impl, newLastRow); + + if constexpr (Impl::itemsAreQObjects) { + if (model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) { + const auto begin = QRangeModelDetails::begin(QRangeModelDetails::refTo(oldRange)); + const auto end = QRangeModelDetails::end(QRangeModelDetails::refTo(oldRange)); + int rowIndex = 0; + for (auto it = begin; it != end; ++it, ++rowIndex) + impl->autoConnectPropertiesInRow(*it, rowIndex, storage.root()); + } + } + } + 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>())) diff --git a/src/corelib/itemmodels/qrangemodeladapter.qdoc b/src/corelib/itemmodels/qrangemodeladapter.qdoc index 263bff0dd0c..88872589299 100644 --- a/src/corelib/itemmodels/qrangemodeladapter.qdoc +++ b/src/corelib/itemmodels/qrangemodeladapter.qdoc @@ -277,14 +277,6 @@ */ /*! - \typedef QRangeModelAdapter::const_row_reference -*/ - -/*! - \typedef QRangeModelAdapter::row_reference -*/ - -/*! \typedef QRangeModelAdapter::range_type */ @@ -329,16 +321,39 @@ /*! \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) + \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>, QRangeModelAdapter<Range, Protocol, Model>::unless_adapter<NewRange>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(NewRange &&newRange) + + Replaces the contents of the model with the rows in \a newRange, possibly + using move semantics. - 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. + assigned to \c Range, but not a QRangeModelAdapter. + + \sa range(), at(), model(), assign() +*/ + +/*! + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(std::initializer_list<Row> newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::assign(std::initializer_list<Row> newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(std::initializer_list<Row> newRange) + + Replaces the contents of the model with the rows in \a newRange. - \sa range(), at(), model() + \constraints \c Range is mutable, and \a newRange can be assigned to \c Range. + + \sa range(), at, model() +*/ + +/*! + \fn template <typename Range, typename Protocol, typename Model> template <typename InputIterator, typename Sentinel, typename I = Impl, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(InputIterator first, Sentinel last) + \fn template <typename Range, typename Protocol, typename Model> template <typename InputIterator, typename Sentinel, typename I = Impl, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> void QRangeModelAdapter<Range, Protocol, Model>::assign(InputIterator first, Sentinel last) + + Replaces the contents of the models with the rows in the range [\a first, \a last). + + \sa range(), at, model() */ /*! @@ -508,8 +523,8 @@ */ /*! - \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 + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> decltype(auto) 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>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) const \return a constant reference to the row at \a row, as stored in \c Range. @@ -517,8 +532,8 @@ */ /*! - \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) + \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>> auto 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>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) \return a mutable reference to the row at \a row, as stored in \c Range. @@ -574,6 +589,16 @@ */ /*! + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path) const + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path) const + + \return a constant reference to the row specified by \a path, as stored in + \c Range. + + \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) \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) @@ -810,7 +835,7 @@ */ /*! - \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) + \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_column_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 @@ -838,7 +863,7 @@ */ /*! - \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) + \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_column_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 diff --git a/src/corelib/kernel/qassociativeiterable.h b/src/corelib/kernel/qassociativeiterable.h index 39f66d45fa0..4f9bbf67bfb 100644 --- a/src/corelib/kernel/qassociativeiterable.h +++ b/src/corelib/kernel/qassociativeiterable.h @@ -156,10 +156,12 @@ inline QVariantRef<QAssociativeIterator>::operator QVariant() const if (!metaType.isValid()) return m_pointer->key(); + return [&] { QVariant v(metaType); metaAssociation.mappedAtIterator(m_pointer->constIterator(), metaType == QMetaType::fromType<QVariant>() ? &v : v.data()); return v; + }(); } template<> diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm index 687fc7e85fa..f5f1f4a8cb8 100644 --- a/src/corelib/kernel/qcore_mac.mm +++ b/src/corelib/kernel/qcore_mac.mm @@ -22,6 +22,10 @@ #include <spawn.h> #include <qdebug.h> +#include <qpoint.h> +#include <qsize.h> +#include <qrect.h> +#include <qmargins.h> #include "qendian.h" #include "qhash.h" @@ -222,6 +226,34 @@ QDebug operator<<(QDebug dbg, CFStringRef stringRef) return dbg; } +QDebug operator<<(QDebug dbg, CGPoint point) +{ + dbg << QPointF::fromCGPoint(point); + return dbg; +} + +QDebug operator<<(QDebug dbg, CGSize size) +{ + dbg << QSizeF::fromCGSize(size); + return dbg; +} + +QDebug operator<<(QDebug dbg, CGRect rect) +{ + dbg << QRectF::fromCGRect(rect); + return dbg; +} + +#if defined(Q_OS_MACOS) +QDebug operator<<(QDebug dbg, NSEdgeInsets insets) +#else +QDebug operator<<(QDebug dbg, UIEdgeInsets insets) +#endif +{ + dbg << QMargins(insets.left, insets.top, insets.right, insets.bottom); + return dbg; +} + // Prevents breaking the ODR in case we introduce support for more types // later on, and lets the user override our default QDebug operators. #define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType) \ diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index 2c4b4c02c55..1e57ee01e1d 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -78,6 +78,15 @@ kern_return_t IOObjectRelease(io_object_t object); Q_FORWARD_DECLARE_OBJC_CLASS(NSObject); Q_FORWARD_DECLARE_OBJC_CLASS(NSString); +struct CGPoint; +struct CGSize; +struct CGRect; +#if defined(Q_OS_MACOS) +struct NSEdgeInsets; +#else +struct UIEdgeInsets; +#endif + // @compatibility_alias doesn't work with categories or their methods #define QtExtras QT_MANGLE_NAMESPACE(QtExtras) @@ -225,6 +234,14 @@ Q_AUTOTEST_EXPORT void qt_mac_ensureResponsible(); #ifndef QT_NO_DEBUG_STREAM Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool); Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QCFString &string); +Q_CORE_EXPORT QDebug operator<<(QDebug, CGPoint); +Q_CORE_EXPORT QDebug operator<<(QDebug, CGSize); +Q_CORE_EXPORT QDebug operator<<(QDebug, CGRect); +#if defined(Q_OS_MACOS) +Q_CORE_EXPORT QDebug operator<<(QDebug, NSEdgeInsets); +#else +Q_CORE_EXPORT QDebug operator<<(QDebug, UIEdgeInsets); +#endif #endif Q_CORE_EXPORT bool qt_apple_isApplicationExtension(); diff --git a/src/corelib/kernel/qeventdispatcher_cf.mm b/src/corelib/kernel/qeventdispatcher_cf.mm index 2dddf147f85..d57c057a21a 100644 --- a/src/corelib/kernel/qeventdispatcher_cf.mm +++ b/src/corelib/kernel/qeventdispatcher_cf.mm @@ -207,6 +207,7 @@ QEventLoop *QEventDispatcherCoreFoundation::currentEventLoop() const } /*! + \internal Processes all pending events that match \a flags until there are no more events to process. Returns \c true if pending events were handled; otherwise returns \c false. diff --git a/src/corelib/kernel/qmetacontainer.h b/src/corelib/kernel/qmetacontainer.h index c9d3a6bf9c6..66047afefd4 100644 --- a/src/corelib/kernel/qmetacontainer.h +++ b/src/corelib/kernel/qmetacontainer.h @@ -971,18 +971,11 @@ public: Iterator mutableEnd(); QVariant at(qsizetype idx) const; - void set(qsizetype idx, const QVariant &value); + void setAt(qsizetype idx, const QVariant &value); void append(const QVariant &value); void prepend(const QVariant &value); void removeLast(); void removeFirst(); - -#if QT_DEPRECATED_SINCE(6, 11) - enum Position: quint8 { Unspecified, AtBegin, AtEnd }; - void addValue(const QVariant &value, Position position = Unspecified); - void removeValue(Position position = Unspecified); - QMetaType valueMetaType() const; -#endif // QT_DEPRECATED_SINCE(6, 11) }; #else using Iterable = QtMetaContainerPrivate::Sequence; diff --git a/src/corelib/kernel/qmetasequence.cpp b/src/corelib/kernel/qmetasequence.cpp index 2a3a923d5ca..018dd610146 100644 --- a/src/corelib/kernel/qmetasequence.cpp +++ b/src/corelib/kernel/qmetasequence.cpp @@ -531,39 +531,6 @@ void QMetaSequence::valueAtConstIterator(const void *iterator, void *result) con */ /*! - \enum QMetaSequence::Iterable::Position - \deprecated [6.11] Use append(), prepend(), removeFirst(), or removeLast() - - Specifies the position at which an element shall be added to or removed from - the iterable. - - \value AtBegin - Add or remove at the beginning of the iterable. - \value AtEnd - Add or remove at the end of the iterable. - \value Unspecified - Add or remove at an unspecified position in the iterable. - */ - -/*! - \fn void QMetaSequence::Iterable::addValue(const QVariant &value, Position position) - \deprecated [6.11] Use append() or prepend() - Adds \a value to the container, at \a position, if possible. - */ - -/*! - \deprecated [6.11] Use removeFirst() or removeLast() - \fn void QMetaSequence::Iterable::removeValue(Position position) - Removes a value from the container, at \a position, if possible. - */ - -/*! - \deprecated [6.11] Use QMetaSequence::valueMetaType() - \fn QMetaType QMetaSequence::Iterable::valueMetaType() const - Returns the meta type for values stored in the underlying container. - */ - -/*! \fn QVariant QMetaSequence::Iterable::at(qsizetype idx) const Returns the value at position \a idx in the container. @@ -574,13 +541,8 @@ void QMetaSequence::valueAtConstIterator(const void *iterator, void *result) con */ /*! - \fn void QMetaSequence::Iterable::set(qsizetype idx, const QVariant &value) + \fn void QMetaSequence::Iterable::setAt(qsizetype idx, const QVariant &value) Sets the element at position \a idx in the container to \a value. - - \note If the underlying container does not provide a native way to assign - an element at an index, this method will synthesize the assignment - using iterators. This behavior is deprecated and will be removed in a - future version of Qt. */ /*! diff --git a/src/corelib/kernel/qmetasequence.h b/src/corelib/kernel/qmetasequence.h index 26156e7924f..f8052476d79 100644 --- a/src/corelib/kernel/qmetasequence.h +++ b/src/corelib/kernel/qmetasequence.h @@ -196,25 +196,11 @@ public: }); } - void set(qsizetype idx, const QVariant &value) + void setAt(qsizetype idx, const QVariant &value) { const QMetaSequence meta = metaContainer(); QtPrivate::QVariantTypeCoercer coercer; - const void *dataPtr = coercer.coerce(value, meta.valueMetaType()); - if (meta.canSetValueAtIndex()) { - meta.setValueAtIndex(mutableIterable(), idx, dataPtr); - return; - } - -#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) - // We shouldn't second-guess the underlying container - QtPrivate::warnSynthesizedAccess( - "set() called on an iterable without native indexed accessors. This is slow"); - void *it = meta.begin(m_iterable.mutablePointer()); - meta.advanceIterator(it, idx); - meta.setValueAtIterator(it, dataPtr); - meta.destroyIterator(it); -#endif + meta.setValueAtIndex(mutableIterable(), idx, coercer.coerce(value, meta.valueMetaType())); } void append(const QVariant &value) @@ -261,6 +247,9 @@ public: QMetaType valueMetaType() const Q_DECL_EQ_DELETE_X("Use QMetaSequence::valueMetaType() instead."); + void set(qsizetype idx, const QVariant &value) + Q_DECL_EQ_DELETE_X("Use setAt() instead."); + QT_WARNING_POP #endif // QT_DEPRECATED_SINCE(6, 11) }; diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp index 6767917e176..69383dafdd9 100644 --- a/src/corelib/kernel/qpermissions.cpp +++ b/src/corelib/kernel/qpermissions.cpp @@ -119,7 +119,7 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg); To ensure the relevant permission backend is included with your application, please \l {QT_ANDROID_PACKAGE_SOURCE_DIR} {point the build system to your custom \c AndroidManifest.xml} - or use \l {qt_add_android_permission()}. + or use \l {qt_add_android_permission}(). The relevant permission names are described in the documentation for each permission type. diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index d538ed7b4e9..9141a8f8bad 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -402,6 +402,16 @@ QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRec } /*! + \class QUntypedPropertyBinding + \inmodule QtCore + \since 6.0 + \ingroup tools + \brief Represents a type-erased property binding. + + \sa QUntypedBindable +*/ + +/*! Constructs a null QUntypedPropertyBinding. \sa isNull() @@ -409,8 +419,8 @@ QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRec QUntypedPropertyBinding::QUntypedPropertyBinding() = default; /*! - \fn template<typename Functor> - QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location) + \fn template<typename Functor> QUntypedPropertyBinding( + QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location) \internal */ @@ -448,7 +458,6 @@ QUntypedPropertyBinding::QUntypedPropertyBinding(const QUntypedPropertyBinding & : d(other.d) { } - /*! Copy-assigns \a other to this QUntypedPropertyBinding. */ @@ -1183,7 +1192,7 @@ QString QPropertyBindingError::description() const \return \c true when the binding was successfully set. - //! \sa QUntypedPropertyBinding::valueMetaType() + \sa QUntypedPropertyBinding::valueMetaType() */ /*! @@ -1199,8 +1208,7 @@ QString QPropertyBindingError::description() const Returns the metatype of the property from which the QUntypedBindable was created. If the bindable is invalid, an invalid metatype will be returned. - \sa isValid() - //! \sa QUntypedPropertyBinding::valueMetaType() + \sa isValid(), QUntypedPropertyBinding::valueMetaType() */ /*! diff --git a/src/corelib/kernel/qsequentialiterable.h b/src/corelib/kernel/qsequentialiterable.h index 92252cb19dd..76908bdae4b 100644 --- a/src/corelib/kernel/qsequentialiterable.h +++ b/src/corelib/kernel/qsequentialiterable.h @@ -142,10 +142,13 @@ inline QVariantRef<QSequentialIterator>::operator QVariant() const if (m_pointer == nullptr) return QVariant(); const QMetaType metaType(m_pointer->metaContainer().valueMetaType()); + + return [&] { QVariant v(metaType); void *dataPtr = metaType == QMetaType::fromType<QVariant>() ? &v : v.data(); m_pointer->metaContainer().valueAtIterator(m_pointer->constIterator(), dataPtr); return v; + }(); } template<> diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 9fdac89f775..6000edaa177 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -392,8 +392,8 @@ public: translation files may contain misleading or malicious translations. \sa QCoreApplication::installTranslator(), QCoreApplication::removeTranslator(), - QObject::tr(), QCoreApplication::translate(), {I18N Example}, - {Hello tr() Example}, {Arrow Pad Example}, {Troll Print Example} + QObject::tr(), QCoreApplication::translate(), + {Localized Clock Example}, {Arrow Pad Example}, {Troll Print Example} */ /*! diff --git a/src/corelib/platform/windows/qbstr_p.h b/src/corelib/platform/windows/qbstr_p.h index 21eecfe2df7..32b1aace76f 100644 --- a/src/corelib/platform/windows/qbstr_p.h +++ b/src/corelib/platform/windows/qbstr_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2024 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 QBSTR_P_H #define QBSTR_P_H @@ -141,4 +142,4 @@ QT_END_NAMESPACE #endif // Q_OS_WIN -#endif // QCOMPTR_P_H +#endif // QBSTR_P_H diff --git a/src/corelib/platform/windows/qcomobject_p.h b/src/corelib/platform/windows/qcomobject_p.h index bd6f81d1c28..b7a9c56555d 100644 --- a/src/corelib/platform/windows/qcomobject_p.h +++ b/src/corelib/platform/windows/qcomobject_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2023 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 QCOMOBJECT_P_H #define QCOMOBJECT_P_H diff --git a/src/corelib/platform/windows/qcomptr_p.h b/src/corelib/platform/windows/qcomptr_p.h index 2a69e7b6038..e640528d3c2 100644 --- a/src/corelib/platform/windows/qcomptr_p.h +++ b/src/corelib/platform/windows/qcomptr_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2024 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 QCOMPTR_P_H #define QCOMPTR_P_H diff --git a/src/corelib/platform/windows/qcomvariant_p.h b/src/corelib/platform/windows/qcomvariant_p.h index 34ce5f179ce..cc4ad104106 100644 --- a/src/corelib/platform/windows/qcomvariant_p.h +++ b/src/corelib/platform/windows/qcomvariant_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2024 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 QCOMVARIANT_P_H #define QCOMVARIANT_P_H diff --git a/src/corelib/platform/windows/qfactorycacheregistration.cpp b/src/corelib/platform/windows/qfactorycacheregistration.cpp index 6bd69c66d14..04c81b7b665 100644 --- a/src/corelib/platform/windows/qfactorycacheregistration.cpp +++ b/src/corelib/platform/windows/qfactorycacheregistration.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #include "qfactorycacheregistration_p.h" diff --git a/src/corelib/platform/windows/qfactorycacheregistration_p.h b/src/corelib/platform/windows/qfactorycacheregistration_p.h index d0b19b995b4..f6e7d9eb064 100644 --- a/src/corelib/platform/windows/qfactorycacheregistration_p.h +++ b/src/corelib/platform/windows/qfactorycacheregistration_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2022 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 QFACTORYCACHEREGISTRATION_P_H #define QFACTORYCACHEREGISTRATION_P_H diff --git a/src/corelib/platform/windows/qt_winrtbase_p.h b/src/corelib/platform/windows/qt_winrtbase_p.h index 79c2bdf6b1c..69a602a59e0 100644 --- a/src/corelib/platform/windows/qt_winrtbase_p.h +++ b/src/corelib/platform/windows/qt_winrtbase_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2022 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 QT_WINRTBASE_P_H #define QT_WINRTBASE_P_H diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 13d74e591d5..0aeb2b07f47 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -975,6 +975,7 @@ QCborContainerPrivate *QCborContainerPrivate::detach(QCborContainerPrivate *d, q } /*! + \internal Prepare for an insertion at position \a index Detaches and ensures there are at least index entries in the array, padding diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp index ae3bed5b751..fd2f7faeee5 100644 --- a/src/corelib/serialization/qdatastream.cpp +++ b/src/corelib/serialization/qdatastream.cpp @@ -552,6 +552,7 @@ void QDataStream::setByteOrder(ByteOrder bo) \value Qt_6_9 \value Qt_6_10 \value Qt_6_11 + \value Qt_6_12 \omitvalue Qt_DefaultCompiledVersion \sa setVersion(), version() diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index 04373fe9c8a..d6fcf17efcd 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -96,8 +96,9 @@ public: Qt_6_9 = Qt_6_7, Qt_6_10 = 23, Qt_6_11 = 24, - Qt_DefaultCompiledVersion = Qt_6_11 -#if QT_VERSION >= QT_VERSION_CHECK(6, 12, 0) + Qt_6_12 = Qt_6_11, + Qt_DefaultCompiledVersion = Qt_6_12 +#if QT_VERSION >= QT_VERSION_CHECK(6, 13, 0) #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion #endif }; diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index 9cd90fa9d65..ff70289013a 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -3678,7 +3678,7 @@ void QXmlStreamWriter::setStopWritingOnError(bool stop) The error status is never reset. Writes happening after the error occurred may be ignored, even if the error condition is cleared. - \sa error(), errorString(), raiseError(const QString &message), + \sa error(), errorString(), raiseError() */ bool QXmlStreamWriter::hasError() const { @@ -3692,7 +3692,7 @@ bool QXmlStreamWriter::hasError() const QXmlStreamWriter::Error::None. \since 6.10 - \sa errorString(), raiseError(const QString &message), hasError() + \sa errorString(), raiseError(), hasError() */ QXmlStreamWriter::Error QXmlStreamWriter::error() const { @@ -3708,7 +3708,7 @@ QXmlStreamWriter::Error QXmlStreamWriter::error() const a null string. \since 6.10 - \sa error(), raiseError(const QString &message), hasError() + \sa error(), raiseError(), hasError() */ QString QXmlStreamWriter::errorString() const { diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index fed09aee45e..18007cacae6 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -301,6 +301,7 @@ bool operator<(LikelyPair lhs, LikelyPair rhs) } // anonymous namespace /*! + \internal Fill in blank fields of a locale ID. An ID in which some fields are zero stands for any locale that agrees with diff --git a/src/corelib/text/qstringconverter.cpp b/src/corelib/text/qstringconverter.cpp index bf6e776ee0e..896142b4837 100644 --- a/src/corelib/text/qstringconverter.cpp +++ b/src/corelib/text/qstringconverter.cpp @@ -2739,7 +2739,7 @@ QStringList QStringConverter::availableCodecs() May also provide data from residual content that was pending decoding. When there is no residual data to account for, the return's \c error - field will be set to \l {QCharConverter::FinalizeResult::Error::} + field will be set to \l {QStringConverter::FinalizeResultChar::error} {NoError}. If \a out is supplied and non-null, it must have space in which up to @@ -2793,7 +2793,7 @@ auto QStringDecoder::finalize(char16_t *out, qsizetype maxlen) -> FinalizeResult May also provide data from residual content that was pending decoding. When there is no residual data to account for, the return's \c error - field will be set to \l {QCharConverter::FinalizeResult::Error::} + field will be set to \l {QStringConverter::FinalizeResultChar::error} {NoError}. If \a out is supplied and non-null, it must have space in which up to diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index 371ee524d72..d6c185cd704 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -845,7 +845,7 @@ struct UnwrapHandler using NestedType = typename QtPrivate::Future<ResultType>::type; QFutureInterface<NestedType> promise(QFutureInterfaceBase::State::Pending); - outer->then([promise](const QFuture<ResultType> &outerFuture) mutable { + auto chain = outer->then([promise](const QFuture<ResultType> &outerFuture) mutable { // We use the .then([](QFuture<ResultType> outerFuture) {...}) version // (where outerFuture == *outer), to propagate the exception if the // outer future has failed. @@ -883,6 +883,13 @@ struct UnwrapHandler promise.reportCanceled(); promise.reportFinished(); }); + + // Inject the promise into the chain. + // We use a fake function as a continuation, since the promise is + // managed by the outer future + chain.d.setContinuation(ContinuationWrapper(std::move([](const QFutureInterfaceBase &) {})), + promise.d, QFutureInterfaceBase::ContinuationType::Then); + return promise.future(); } }; diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h index 0b88013800e..ff17560d3a1 100644 --- a/src/corelib/thread/qfutureinterface.h +++ b/src/corelib/thread/qfutureinterface.h @@ -42,6 +42,8 @@ template<class Function, class ResultType> class FailureHandler; #endif +struct UnwrapHandler; + #if QT_CORE_REMOVED_SINCE(6, 10) void Q_CORE_EXPORT watchContinuationImpl(const QObject *context, QtPrivate::QSlotObjectBase *slotObj, @@ -187,6 +189,8 @@ private: friend class QtPrivate::FailureHandler; #endif + friend struct QtPrivate::UnwrapHandler; + #if QT_CORE_REMOVED_SINCE(6, 10) friend Q_CORE_EXPORT void QtPrivate::watchContinuationImpl( const QObject *context, QtPrivate::QSlotObjectBase *slotObj, QFutureInterfaceBase &fi); diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp index 96e35dcb965..2a1af2315ca 100644 --- a/src/corelib/thread/qreadwritelock.cpp +++ b/src/corelib/thread/qreadwritelock.cpp @@ -234,14 +234,14 @@ QBasicReadWriteLock::contendedTryLockForRead(QDeadlineTimer timeout, void *dd) return d->recursiveLockForRead(timeout); auto lock = qt_unique_lock(d->mutex); - if (d != d_ptr.loadRelaxed()) { + if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { // d_ptr has changed: this QReadWriteLock was unlocked before we had // time to lock d->mutex. // We are holding a lock to a mutex within a QReadWriteLockPrivate // that is already released (or even is already re-used). That's ok // because the QFreeList never frees them. // Just unlock d->mutex (at the end of the scope) and retry. - d = d_ptr.loadAcquire(); + d = dd; continue; } return d->lockForRead(lock, timeout); @@ -340,11 +340,11 @@ QBasicReadWriteLock::contendedTryLockForWrite(QDeadlineTimer timeout, void *dd) return d->recursiveLockForWrite(timeout); auto lock = qt_unique_lock(d->mutex); - if (d != d_ptr.loadRelaxed()) { + if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { // The mutex was unlocked before we had time to lock the mutex. // We are holding to a mutex within a QReadWriteLockPrivate that is already released // (or even is already re-used) but that's ok because the QFreeList never frees them. - d = d_ptr.loadAcquire(); + d = dd; continue; } return d->lockForWrite(lock, timeout); diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp index 10882738a39..05ba3d2beae 100644 --- a/src/corelib/time/qdatetimeparser.cpp +++ b/src/corelib/time/qdatetimeparser.cpp @@ -2389,6 +2389,7 @@ bool operator==(QDateTimeParser::SectionNode s1, QDateTimeParser::SectionNode s2 } /*! + \internal Sets \a cal as the calendar to use. The default is Gregorian. */ diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 1c0371e463e..d173d824e88 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -162,7 +162,7 @@ allocateHelper(QArrayData **dptr, qsizetype objectSize, qsizetype alignment, qsi QArrayData::AllocationOption option) noexcept { *dptr = nullptr; - if (capacity == 0) + if (capacity <= 0) return {}; const qsizetype headerSize = calculateHeaderSize(alignment); diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp index de68a0042ac..1e647a83dc0 100644 --- a/src/corelib/tools/qeasingcurve.cpp +++ b/src/corelib/tools/qeasingcurve.cpp @@ -332,15 +332,13 @@ struct TCBPoint qreal _c; qreal _b; - TCBPoint() {} - TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {} - bool operator==(const TCBPoint &other) const + friend bool operator==(const TCBPoint &lhs, const TCBPoint &rhs) noexcept { - return _point == other._point && - qFuzzyCompare(_t, other._t) && - qFuzzyCompare(_c, other._c) && - qFuzzyCompare(_b, other._b); + return qFuzzyCompare(lhs._point, rhs._point) + && QtPrivate::fuzzyCompare(lhs._t, rhs._t) + && QtPrivate::fuzzyCompare(lhs._c, rhs._c) + && QtPrivate::fuzzyCompare(lhs._b, rhs._b); } }; Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE); @@ -1381,7 +1379,7 @@ void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qre if (!d_ptr->config) d_ptr->config = curveToFunctionObject(d_ptr->type); - d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c, b)); + d_ptr->config->_tcbPoints.append(TCBPoint{nextPoint, t, c, b}); if (nextPoint == QPointF(1.0, 1.0)) { d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints); diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp index 311b53aeaa3..b5dcdca6270 100644 --- a/src/gui/accessible/qaccessiblecache.cpp +++ b/src/gui/accessible/qaccessiblecache.cpp @@ -19,6 +19,7 @@ Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCache, "qt.accessibility.cache"); */ static QAccessibleCache *accessibleCache = nullptr; +static bool inCacheDestructor = false; static void cleanupAccessibleCache() { @@ -32,6 +33,8 @@ QAccessibleObjectDestroyedEvent::~QAccessibleObjectDestroyedEvent() QAccessibleCache::~QAccessibleCache() { + inCacheDestructor = true; + for (QAccessible::Id id: idToInterface.keys()) deleteInterface(id); } @@ -188,10 +191,9 @@ void QAccessibleCache::deleteInterface(QAccessible::Id id, QObject *obj) return; } - // QObjects sends this from their destructor, but - // the object less interfaces calls deleteInterface - // directly - if (!obj && !iface->object()) { + // QObjects send this from their destructors, but the interfaces + // with no associated object call deleteInterface directly. + if (!inCacheDestructor && !obj && !iface->object()) { if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing && QAccessible::isActive()) { QAccessibleObjectDestroyedEvent event(id); QAccessible::updateAccessibility(&event); diff --git a/src/gui/doc/images/coordinatesystem-transformations.png b/src/gui/doc/images/coordinatesystem-transformations.png Binary files differdeleted file mode 100644 index 2736213c072..00000000000 --- a/src/gui/doc/images/coordinatesystem-transformations.png +++ /dev/null diff --git a/src/gui/doc/images/coordinatesystem-transformations.svg b/src/gui/doc/images/coordinatesystem-transformations.svg new file mode 100644 index 00000000000..a3bc17af3ef --- /dev/null +++ b/src/gui/doc/images/coordinatesystem-transformations.svg @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="760" height="300" + viewBox="0 0 760 300" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> + +<style> + svg .line-style { stroke: black; fill: none; stroke-width: 2 } + svg .text-style { font: 14px arial; fill: black } + svg .mono-text-style { font: 14px monospace; fill: black } + svg .heading-style { font: 14px arial; fill: black; font-weight: bold } + svg .shape-style { stroke: none; fill: black } + svg .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: black } + + svg .window-line-style { stroke: red; fill: none; stroke-width: 2 } + svg .window-text-style { font: 14px arial; fill: red } + svg .window-shape-style { stroke: none; fill: red } + svg .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: red } + + svg.dark .line-style { stroke: #f2f2f2; fill: none; stroke-width: 2 } + svg.dark .text-style { font: 14px arial; fill: #f2f2f2 } + svg.dark .mono-text-style { font: 14px monospace; fill: #f2f2f2 } + svg.dark .heading-style { font: 14px arial; fill: #f2f2f2; font-weight: bold } + svg.dark .shape-style { stroke: none; fill: #f2f2f2 } + svg.dark .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: #f2f2f2 } + svg.dark .window-line-style { stroke: yellow; fill: none; stroke-width: 2 } + svg.dark .window-text-style { font: 14px arial; fill: yellow } + svg.dark .window-shape-style { stroke: none; fill: yellow } + svg.dark .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: yellow } + + [data-theme="dark"] svg .line-style { stroke: #f2f2f2; fill: none; stroke-width: 2 } + [data-theme="dark"] svg .text-style { font: 14px arial; fill: #f2f2f2 } + [data-theme="dark"] svg .mono-text-style { font: 14px monospace; fill: #f2f2f2 } + [data-theme="dark"] svg .heading-style { font: 14px arial; fill: #f2f2f2; font-weight: bold } + [data-theme="dark"] svg .shape-style { stroke: none; fill: #f2f2f2 } + [data-theme="dark"] svg .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: #f2f2f2 } + [data-theme="dark"] svg .window-line-style { stroke: yellow; fill: none; stroke-width: 2 } + [data-theme="dark"] svg .window-text-style { font: 14px arial; fill: yellow } + [data-theme="dark"] svg .window-shape-style { stroke: none; fill: yellow } + [data-theme="dark"] svg .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: yellow } + + [data-theme="light"] svg .line-style { stroke: black; fill: none; stroke-width: 2 } + [data-theme="light"] svg .text-style { font: 14px arial; fill: black } + [data-theme="light"] svg .mono-text-style { font: 14px monospace; fill: black } + [data-theme="light"] svg .heading-style { font: 14px arial; fill: black; font-weight: bold } + [data-theme="light"] svg .shape-style { stroke: none; fill: black } + [data-theme="light"] svg .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: black } + [data-theme="light"] svg .window-line-style { stroke: red; fill: none; stroke-width: 2 } + [data-theme="light"] svg .window-text-style { font: 14px arial; fill: red } + [data-theme="light"] svg .window-shape-style { stroke: none; fill: red } + [data-theme="light"] svg .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: red } +</style> + +<text x="50" y="20" fill="black" font-size="14px" font-family="arial" + font-weight="bold" + class="heading-style">World coordinates</text> + +<g transform="translate(20, 30)"> + <rect x="0" y="0" width="200" height="200" stroke="black" fill="none" stroke-dasharray="4,3" stroke-dashoffset="0" + class="dash-style" /> + + <path d="M5,5 L16,10 L10,16 L5,5" class="shape-style" /> + <polyline points="5,5 20,20" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="25" y="30" fill="black" font-size="14px" font-family="arial" + class="text-style">(0, 0)</text> + + <path d="M195,195 L184,190 L190,184 L195,195" class="shape-style" /> + <polyline points="195,195 180,180" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="115" y="175" fill="black" font-size="14px" font-family="arial" + class="text-style">(100, 100)</text> + + <polyline points="-3,-3 3,3" stroke="black" stroke-width="2" class="line-style" /> + <polyline points="-3,3 3,-3" stroke="black" stroke-width="2" class="line-style" /> +</g> + +<g transform="translate(45,235)"> + <path d="M 0,0 c 0,25 10,35 25,35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 25,30 l 10,5 l -10,5 z" class="shape-style" /> + <text x="40" y="40" fill="black" font-size="14px" font-family="monospace" + class="mono-text-style">setWindow(-50, -50, 100, 100)</text> + <path d="M 290,35 c 15,0 25,-10 25,-35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 315,0 l 5,10 l -10,0 z" class="shape-style" /> +</g> + +<text x="300" y="20" fill="black" font-size="14px" font-family="arial" + font-weight="bold" + class="heading-style">"Window" coordinates</text> + +<g transform="translate(280, 30)"> + + <rect x="0" y="0" width="200" height="200" stroke="red" fill="none" stroke-dasharray="4,3" stroke-dashoffset="0" + class="window-dash-style" /> + + <path d="M5,5 L16,10 L10,16 L5,5" fill="red" class="window-shape-style" /> + <polyline points="5,5 20,20" stroke="red" stroke-width="2" fill="none" class="window-line-style" /> + <text x="25" y="30" fill="red" font-size="14px" font-family="arial" + class="window-text-style">(-50, -50)</text> + + <path d="M195,195 L184,190 L190,184 L195,195" fill="red" class="window-shape-style" /> + <polyline points="195,195 180,180" stroke="red" stroke-width="2" fill="none" class="window-line-style" /> + <text x="130" y="175" fill="red" font-size="14px" font-family="arial" + class="window-text-style">(50, 50)</text> + + <polyline points="97,97 103,103" stroke="red" stroke-width="2" class="window-line-style" /> + <polyline points="97,103 103,97" stroke="red" stroke-width="2" class="window-line-style" /> +</g> + +<g transform="translate(395,235)"> + <path d="M 0,0 c 0,25 10,35 25,35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 25,30 l 10,5 l -10,5 z" class="shape-style" /> + <text x="40" y="40" fill="black" font-size="14px" font-family="monospace" + class="mono-text-style">setViewport(45, 25, 50, 50)</text> + <path d="M 270,35 c 15,0 25,-10 25,-35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 295,0 l 5,10 l -10,0 z" class="shape-style" /> +</g> + +<text x="570" y="20" fill="black" font-size="14px" font-family="arial" + font-weight="bold" + class="heading-style">Device coordinates</text> + +<g transform="translate(540, 30)"> + + <rect x="0" y="0" width="200" height="200" stroke="black" stroke-width="2" fill="none" class="line-style" /> + + <path d="M5,5 L16,10 L10,16 L5,5" class="shape-style" /> + <polyline points="5,5 20,20" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="25" y="30" fill="black" font-size="14px" font-family="arial" + class="text-style">(0, 0)</text> + + <path d="M195,195 L184,190 L190,184 L195,195" class="shape-style" /> + <polyline points="195,195 180,180" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="115" y="175" fill="black" font-size="14px" font-family="arial" + class="text-style">(100, 100)</text> + + <rect x="90" y="50" width="100" height="100" stroke="red" fill="none" stroke-dasharray="4,3" stroke-dashoffset="0" + class="window-dash-style" /> + <polyline points="137,97 143,103" stroke="red" stroke-width="2" class="window-line-style" /> + <polyline points="137,103 143,97" stroke="red" stroke-width="2" class="window-line-style" /> +</g> + +</svg> diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf index 24d9d522735..8b7569c1296 100644 --- a/src/gui/doc/qtgui.qdocconf +++ b/src/gui/doc/qtgui.qdocconf @@ -50,7 +50,8 @@ depends += \ qtshadertools \ qttestlib \ qtplatformintegration \ - qthelp + qthelp \ + qtquickcontrols headerdirs += .. diff --git a/src/gui/doc/src/coordsys.qdoc b/src/gui/doc/src/coordsys.qdoc index 3dd064c19bc..22e14121af6 100644 --- a/src/gui/doc/src/coordsys.qdoc +++ b/src/gui/doc/src/coordsys.qdoc @@ -321,7 +321,7 @@ still transformed to the viewport using the same linear algebraic approach. - \image coordinatesystem-transformations.png {Illustration showing + \image coordinatesystem-transformations.svg {Illustration showing how coordinates are mapped using viewport, "window" and transformation matrix} diff --git a/src/gui/doc/src/external-resources.qdoc b/src/gui/doc/src/external-resources.qdoc index 0f356dd5046..14ed0817e62 100644 --- a/src/gui/doc/src/external-resources.qdoc +++ b/src/gui/doc/src/external-resources.qdoc @@ -36,6 +36,7 @@ \externalpage https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html \title Freedesktop Icon Naming Specification */ + /*! \externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout \title Icon Theme Specification - Directory Layout diff --git a/src/gui/itemmodels/qfileinfogatherer.cpp b/src/gui/itemmodels/qfileinfogatherer.cpp index b7ab3dbbc46..ea19db0d20f 100644 --- a/src/gui/itemmodels/qfileinfogatherer.cpp +++ b/src/gui/itemmodels/qfileinfogatherer.cpp @@ -46,6 +46,12 @@ static QString translateDriveName(const QFileInfo &drive) } /*! + \class QFileInfoGatherer + \inmodule QtGui + \internal +*/ + +/*! Creates thread */ QFileInfoGatherer::QFileInfoGatherer(QObject *parent) diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 741b089306e..e8df40f21b2 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -490,6 +490,12 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME */ /*! + \class QGuiApplicationPrivate + \inmodule QtGui + \internal +*/ + +/*! Initializes the window system and constructs an application object with \a argc command line arguments in \a argv. diff --git a/src/gui/kernel/qplatformintegrationfactory.cpp b/src/gui/kernel/qplatformintegrationfactory.cpp index d0a5e8871f8..d113e86090d 100644 --- a/src/gui/kernel/qplatformintegrationfactory.cpp +++ b/src/gui/kernel/qplatformintegrationfactory.cpp @@ -24,6 +24,7 @@ QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platfor } /*! + \internal Returns the list of valid keys, i.e. the keys this factory can create styles for. diff --git a/src/gui/kernel/qplatformthemefactory.cpp b/src/gui/kernel/qplatformthemefactory.cpp index beefa1c2942..3ac462598fd 100644 --- a/src/gui/kernel/qplatformthemefactory.cpp +++ b/src/gui/kernel/qplatformthemefactory.cpp @@ -31,6 +31,7 @@ QPlatformTheme *QPlatformThemeFactory::create(const QString& key, const QString } /*! + \internal Returns the list of valid keys, i.e. the keys this factory can create styles for. diff --git a/src/gui/opengl/platform/egl/qeglplatformcontext.cpp b/src/gui/opengl/platform/egl/qeglplatformcontext.cpp index e56504833d1..350968b87d4 100644 --- a/src/gui/opengl/platform/egl/qeglplatformcontext.cpp +++ b/src/gui/opengl/platform/egl/qeglplatformcontext.cpp @@ -104,6 +104,14 @@ QT_BEGIN_NAMESPACE #define GL_LOSE_CONTEXT_ON_RESET 0x8252 #endif +// Constants from GL_EXT_robustness. +#ifndef GL_RESET_NOTIFICATION_STRATEGY_EXT +#define GL_RESET_NOTIFICATION_STRATEGY_EXT 0x8256 +#endif +#ifndef GL_LOSE_CONTEXT_ON_RESET_EXT +#define GL_LOSE_CONTEXT_ON_RESET_EXT 0x8252 +#endif + // Constants from EGL_NV_robustness_video_memory_purge #ifndef EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV #define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C @@ -452,11 +460,16 @@ void QEGLPlatformContext::updateFormatFromGL() } } } - if (hasExtension("GL_ARB_robustness")) { + if (m_format.renderableType() == QSurfaceFormat::OpenGL && hasExtension("GL_ARB_robustness")) { GLint value = 0; glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY, &value); if (value == GL_LOSE_CONTEXT_ON_RESET) m_format.setOption(QSurfaceFormat::ResetNotification); + } else if (m_format.renderableType() == QSurfaceFormat::OpenGLES && hasExtension("GL_EXT_robustness")) { + GLint value = 0; + glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_EXT, &value); + if (value == GL_LOSE_CONTEXT_ON_RESET_EXT) + m_format.setOption(QSurfaceFormat::ResetNotification); } } runGLChecks(); diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index 6b45d26fb4c..cb927d7c296 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -192,6 +192,12 @@ QOpenGLFunctions::QOpenGLFunctions(QOpenGLContext *context) qWarning("QOpenGLFunctions created with non-current context"); } +/*! + \class QOpenGLExtensions + \inmodule QtGui + \internal +*/ + QOpenGLExtensions::QOpenGLExtensions() { } diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 047be5f1c3d..8e815394849 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -2881,6 +2881,7 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, /*! + * \internal * Returns \c true if the rectangle is completely within the current clip * state of the paint engine. */ diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index cc80641ded9..ab9e7fe312a 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -6,9 +6,11 @@ #include <QtGui/qtguiglobal.h> #include <QtGui/qtransform.h> + #include <QtCore/qglobal.h> #include <QtCore/qline.h> #include <QtCore/qlist.h> +#include <QtCore/qpoint.h> #include <QtCore/qrect.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 908051a477c..b1ea3f240f1 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -3192,6 +3192,7 @@ static inline bool is_monochrome(const QList<QRgb> &colorTable) } /*! + * \internal * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed. */ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless, qint64 serial_no) diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 0d435c95048..08128c30a70 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -145,6 +145,11 @@ static inline qreal adapted_angle_on_x(const QLineF &line) return QLineF(0, 0, 1, 0).angleTo(line); } +/*! + \class QStrokerOps + \inmodule QtGui + \internal +*/ QStrokerOps::QStrokerOps() : m_elements(0) , m_curveThreshold(qt_real_to_fixed(0.25)) @@ -373,6 +378,7 @@ QStroker::LineJoinMode QStroker::joinModeForJoin(Qt::PenJoinStyle joinStyle) /*! + \internal This function is called to stroke the currently built up subpath. The subpath is cleared when the function completes. */ diff --git a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp index fe050461260..00c3192e00a 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp @@ -48,6 +48,7 @@ void QDBusPlatformMenuItem::setIcon(const QIcon &icon) } /*! + \internal Set a submenu under this menu item. */ void QDBusPlatformMenuItem::setMenu(QPlatformMenu *menu) diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index d41296291f6..4df55d5b89c 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -402,7 +402,7 @@ bool QFontEngine::processHheaTable() const const qreal unitsPerEm = emSquareSize().toReal(); // Bail out if values are too large for QFixed - const auto limitForQFixed = qreal(std::numeric_limits<int>::max() / 64) / fontDef.pixelSize; + const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize; if (ascent > limitForQFixed || descent > limitForQFixed || leading > limitForQFixed) return false; m_ascent = QFixed::fromReal(ascent * fontDef.pixelSize / unitsPerEm); @@ -470,7 +470,7 @@ bool QFontEngine::processOS2Table() const if (typoAscent == 0 && typoDescent == 0) return false; // Bail out if values are too large for QFixed - const auto limitForQFixed = qreal(std::numeric_limits<int>::max() / 64) / fontDef.pixelSize; + const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize; if (typoAscent > limitForQFixed || typoDescent > limitForQFixed || typoLineGap > limitForQFixed) return false; @@ -481,7 +481,7 @@ bool QFontEngine::processOS2Table() const // Some fonts may have invalid OS/2 data. We detect this and bail out. if (winAscent == 0 && winDescent == 0) return false; - const auto limitForQFixed = qreal(std::numeric_limits<int>::max() / 64) / fontDef.pixelSize; + const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize; if (winAscent > limitForQFixed || winDescent > limitForQFixed) return false; m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize / unitsPerEm); @@ -1059,6 +1059,7 @@ void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metr } /*! + \internal Returns \c true if the font table idetified by \a tag exists in the font; returns \c false otherwise. diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 4f01d09fed1..d519cd5a5d3 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2385,6 +2385,11 @@ static QString colorValue(QColor color) return result; } +/*! + \class QTextHtmlExporter + \inmodule QtGui + \internal +*/ QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc) : doc(_doc), fragmentMarkers(false) { diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 227cbae2952..85a74d366ac 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -1006,6 +1006,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) } /*! + \internal Appends a custom undo \a item to the undo stack. */ void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item) diff --git a/src/gui/text/qtexttable.cpp b/src/gui/text/qtexttable.cpp index ff8644b5302..337228ff170 100644 --- a/src/gui/text/qtexttable.cpp +++ b/src/gui/text/qtexttable.cpp @@ -394,11 +394,9 @@ void QTextTablePrivate::fragmentRemoved(QChar type, uint fragment) } /*! - /fn void QTextTablePrivate::update() const - + \internal This function is usually called when the table is "dirty". It seems to update all kind of table information. - */ void QTextTablePrivate::update() const { diff --git a/src/gui/text/windows/qwindowsfontdatabase.cpp b/src/gui/text/windows/qwindowsfontdatabase.cpp index 93af1a9600b..0c6d9a31316 100644 --- a/src/gui/text/windows/qwindowsfontdatabase.cpp +++ b/src/gui/text/windows/qwindowsfontdatabase.cpp @@ -1113,17 +1113,21 @@ void QWindowsFontDatabase::removeApplicationFonts() m_eudcFonts.clear(); } +#if QT_CONFIG(directwrite) QWindowsFontDatabase::FontHandle::FontHandle(IDWriteFontFace *face, const QString &name) : fontFace(face), faceName(name) { fontFace->AddRef(); } +#endif // !QT_NO_DIRECTWRITE QWindowsFontDatabase::FontHandle::~FontHandle() { +#if QT_CONFIG(directwrite) if (fontFace != nullptr) fontFace->Release(); +#endif // !QT_NO_DIRECTWRITE } void QWindowsFontDatabase::releaseHandle(void *handle) diff --git a/src/gui/text/windows/qwindowsfontdatabase_p.h b/src/gui/text/windows/qwindowsfontdatabase_p.h index 92e3f04f968..856a5593722 100644 --- a/src/gui/text/windows/qwindowsfontdatabase_p.h +++ b/src/gui/text/windows/qwindowsfontdatabase_p.h @@ -78,10 +78,14 @@ public: struct FontHandle { FontHandle(const QString &name) : faceName(name) {} +#if QT_CONFIG(directwrite) FontHandle(IDWriteFontFace *face, const QString &name); +#endif // !QT_NO_DIRECTWRITE ~FontHandle(); +#if QT_CONFIG(directwrite) IDWriteFontFace *fontFace = nullptr; +#endif // !QT_NO_DIRECTWRITE QString faceName; }; diff --git a/src/gui/util/qlayoutpolicy.cpp b/src/gui/util/qlayoutpolicy.cpp index 4d81a426835..0c0651c1f39 100644 --- a/src/gui/util/qlayoutpolicy.cpp +++ b/src/gui/util/qlayoutpolicy.cpp @@ -8,6 +8,11 @@ QT_BEGIN_NAMESPACE +/*! + \class QLayoutPolicy + \inmodule QtGui + \internal +*/ void QLayoutPolicy::setControlType(ControlType type) { /* diff --git a/src/network/access/qbytedatabuffer_p.h b/src/network/access/qbytedatabuffer_p.h index a119093cf7e..036b562d06a 100644 --- a/src/network/access/qbytedatabuffer_p.h +++ b/src/network/access/qbytedatabuffer_p.h @@ -280,6 +280,8 @@ public: } return false; } + + const QByteArray &last() const { return buffers.last(); } }; QT_END_NAMESPACE diff --git a/src/network/access/qhttp2connection.cpp b/src/network/access/qhttp2connection.cpp index 2895e8335d2..c0b07ddd652 100644 --- a/src/network/access/qhttp2connection.cpp +++ b/src/network/access/qhttp2connection.cpp @@ -34,8 +34,38 @@ using namespace Http2; \sa QHttp2Connection */ -QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept - : QObject(connection), m_streamID(streamID) +/*! + \struct QHttp2Stream::Configuration + \inmodule QtNetwork + \internal + + \brief Configuration options for a QHttp2Stream. + + The Configuration struct holds options that control stream behavior. + + \sa QHttp2Connection::createStream() +*/ + +/*! + \variable QHttp2Stream::Configuration::useDownloadBuffer + + Controls whether incoming DATA frames, from QHttp2Stream::dataReceived(), + are buffered. The default is \c true. + + You may disable buffering for client-initiated streams when the + application processes DATA immediately. + + Buffering must remain enabled for pushed streams. A pushed stream can + receive DATA before the application becomes aware of them and the buffered + DATA is required to deliver the pushed response. + + \sa QHttp2Stream::downloadBuffer(), QHttp2Stream::takeDownloadBuffer(), + QHttp2Configuration::serverPushEnabled(), QHttp2Stream::dataReceived() +*/ + +QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID, + Configuration configuration) noexcept + : QObject(connection), m_streamID(streamID), m_configuration(configuration) { Q_ASSERT(connection); Q_ASSERT(streamID); // stream id 0 is reserved for connection control messages @@ -213,6 +243,12 @@ QHttp2Stream::~QHttp2Stream() noexcept { Returns the buffer containing the data received from the remote peer. */ +/*! + \fn QHttp2Stream::Configuration QHttp2Stream::configuration() const + + Returns the configuration of this stream. +*/ + void QHttp2Stream::finishWithError(Http2::Http2Error errorCode, const QString &message) { qCDebug(qHttp2ConnectionLog, "[%p] stream %u finished with error: %ls (error code: %u)", @@ -697,8 +733,14 @@ void QHttp2Stream::handleDATA(const Frame &inboundFrame) inboundFrame.dataSize()); if (endStream) transitionState(StateTransition::CloseRemote); - emit dataReceived(fragment, endStream); - m_downloadBuffer.append(std::move(fragment)); + const auto shouldBuffer = m_configuration.useDownloadBuffer && !fragment.isEmpty(); + if (shouldBuffer) { + // Only non-empty fragments get appended! + m_downloadBuffer.append(std::move(fragment)); + emit dataReceived(m_downloadBuffer.last(), endStream); + } else { + emit dataReceived(fragment, endStream); + } } if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) { @@ -885,23 +927,35 @@ QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *sock } /*! - Creates a stream on this connection. + \fn QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream() + Creates a stream on this connection, using the default QHttp2Stream::Configuration. + +//! [createStream] Automatically picks the next available stream ID and returns a pointer to the new stream, if possible. Otherwise returns an error. \sa QHttp2Connection::CreateStreamError, QHttp2Stream +//! [createStream] + \sa createStream(QHttp2Stream::Configuration) +*/ + +/*! + Creates a stream with \a configuration on this connection. + + \include qhttp2connection.cpp createStream */ -QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream() +QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> +QHttp2Connection::createStream(QHttp2Stream::Configuration configuration) { Q_ASSERT(m_connectionType == Type::Client); // This overload is just for clients if (m_nextStreamID > lastValidStreamID) return { QHttp2Connection::CreateStreamError::StreamIdsExhausted }; - return createLocalStreamInternal(); + return createLocalStreamInternal(configuration); } QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> -QHttp2Connection::createLocalStreamInternal() +QHttp2Connection::createLocalStreamInternal(QHttp2Stream::Configuration conf) { if (m_goingAway) return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY }; @@ -909,7 +963,7 @@ QHttp2Connection::createLocalStreamInternal() if (size_t(m_peerMaxConcurrentStreams) <= size_t(numActiveLocalStreams())) return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached }; - if (QHttp2Stream *ptr = createStreamInternal_impl(streamID)) { + if (QHttp2Stream *ptr = createStreamInternal_impl(streamID, conf)) { m_nextStreamID += 2; return {ptr}; } @@ -917,7 +971,8 @@ QHttp2Connection::createLocalStreamInternal() return { QHttp2Connection::CreateStreamError::UnknownError }; } -QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID) +QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID, + QHttp2Stream::Configuration conf) { Q_ASSERT(streamID > m_lastIncomingStreamID || streamID >= m_nextStreamID); @@ -930,7 +985,7 @@ QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID) if (!result.inserted) return nullptr; QPointer<QHttp2Stream> &stream = result.iterator.value(); - stream = new QHttp2Stream(this, streamID); + stream = new QHttp2Stream(this, streamID, conf); stream->m_recvWindow = streamInitialReceiveWindowSize; stream->m_sendWindow = streamInitialSendWindowSize; diff --git a/src/network/access/qhttp2connection_p.h b/src/network/access/qhttp2connection_p.h index f3f14145278..e2af7d7ab33 100644 --- a/src/network/access/qhttp2connection_p.h +++ b/src/network/access/qhttp2connection_p.h @@ -101,6 +101,11 @@ public: Q_ENUM(State) constexpr static quint8 DefaultPriority = 127; + struct Configuration + { + bool useDownloadBuffer = true; + }; + ~QHttp2Stream() noexcept; // HTTP2 things @@ -124,6 +129,8 @@ public: QByteDataBuffer takeDownloadBuffer() noexcept { return std::exchange(m_downloadBuffer, {}); } void clearDownloadBuffer() { m_downloadBuffer.clear(); } + Configuration configuration() const { return m_configuration; } + Q_SIGNALS: void headersReceived(const HPack::HttpHeader &headers, bool endStream); void headersUpdated(); @@ -154,7 +161,8 @@ private Q_SLOTS: private: friend class QHttp2Connection; - QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept; + QHttp2Stream(QHttp2Connection *connection, quint32 streamID, + Configuration configuration) noexcept; [[nodiscard]] QHttp2Connection *getConnection() const { @@ -201,6 +209,8 @@ private: bool m_isReserved = false; bool m_owningByteDevice = false; + const Configuration m_configuration; + friend tst_QHttp2Connection; }; @@ -235,7 +245,12 @@ public: createDirectServerConnection(QIODevice *socket, const QHttp2Configuration &config); ~QHttp2Connection(); - [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream(); + [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream() + { + return createStream(QHttp2Stream::Configuration{}); + } + [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> + createStream(QHttp2Stream::Configuration config); QHttp2Stream *getStream(quint32 streamId) const; QHttp2Stream *promisedStream(const QUrl &streamKey) const @@ -278,8 +293,9 @@ private: friend class QHttp2Stream; [[nodiscard]] QIODevice *getSocket() const { return qobject_cast<QIODevice *>(parent()); } - QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> createLocalStreamInternal(); - QHttp2Stream *createStreamInternal_impl(quint32 streamID); + QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> + createLocalStreamInternal(QHttp2Stream::Configuration = {}); + QHttp2Stream *createStreamInternal_impl(quint32 streamID, QHttp2Stream::Configuration = {}); bool isInvalidStream(quint32 streamID) noexcept; bool streamWasResetLocally(quint32 streamID) noexcept; diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp index 6ff70cd546d..1f2bbd321cf 100644 --- a/src/network/access/qnetworkaccesscache.cpp +++ b/src/network/access/qnetworkaccesscache.cpp @@ -176,18 +176,6 @@ void QNetworkAccessCache::updateTimer() timer.start(interval + 10, this); } -bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char *member) -{ - if (!connect(this, SIGNAL(entryReady(QNetworkAccessCache::CacheableObject*)), - target, member, Qt::QueuedConnection)) - return false; - - emit entryReady(node->object); - disconnect(SIGNAL(entryReady(QNetworkAccessCache::CacheableObject*))); - - return true; -} - void QNetworkAccessCache::timerEvent(QTimerEvent *) { while (firstExpiringNode && firstExpiringNode->timer.hasExpired()) { diff --git a/src/network/access/qnetworkaccesscache_p.h b/src/network/access/qnetworkaccesscache_p.h index 4d4ff325f98..5d7dc819461 100644 --- a/src/network/access/qnetworkaccesscache_p.h +++ b/src/network/access/qnetworkaccesscache_p.h @@ -69,9 +69,6 @@ public: void releaseEntry(const QByteArray &key); void removeEntry(const QByteArray &key); -signals: - void entryReady(QNetworkAccessCache::CacheableObject *); - protected: void timerEvent(QTimerEvent *) override; @@ -86,7 +83,6 @@ private: void linkEntry(const QByteArray &key); bool unlinkEntry(const QByteArray &key); void updateTimer(); - bool emitEntryReady(Node *node, QObject *target, const char *member); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkAccessCache::CacheableObject::Options) diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 0905ba3e644..1b34490e358 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -330,6 +330,13 @@ QMargins QCocoaWindow::safeAreaMargins() const // merge them. auto screenRect = m_view.window.screen.frame; auto screenInsets = m_view.window.screen.safeAreaInsets; + auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect( + NSMinX(screenRect) + screenInsets.left, + NSMinY(screenRect) + screenInsets.bottom, // Non-flipped + NSWidth(screenRect) - screenInsets.left - screenInsets.right, + NSHeight(screenRect) - screenInsets.top - screenInsets.bottom + )); + auto screenRelativeViewBounds = QCocoaScreen::mapFromNative( [m_view.window convertRectToScreen: [m_view convertRect:m_view.bounds toView:nil]] @@ -339,20 +346,10 @@ QMargins QCocoaWindow::safeAreaMargins() const // Note that we do not want represent the area outside of the // screen as being outside of the safe area. QMarginsF screenSafeAreaMargins = { - screenInsets.left ? - qMax(0.0f, screenInsets.left - screenRelativeViewBounds.left()) - : 0.0f, - screenInsets.top ? - qMax(0.0f, screenInsets.top - screenRelativeViewBounds.top()) - : 0.0f, - screenInsets.right ? - qMax(0.0f, screenInsets.right - - (screenRect.size.width - screenRelativeViewBounds.right())) - : 0.0f, - screenInsets.bottom ? - qMax(0.0f, screenInsets.bottom - - (screenRect.size.height - screenRelativeViewBounds.bottom())) - : 0.0f + qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left), + qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top), + qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right), + qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom) }; return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins(); diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 0236669d6fb..23bbe409caa 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -450,6 +450,26 @@ QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int hwnd = GetDesktopWindow(); const QRect screenGeometry = geometry(); windowSize = screenGeometry.size(); + // When dpi awareness is not set to PerMonitor, windows reports primary display or dummy + // DPI for all displays, so xIn and yIn and windowSize are calculated with a wrong DPI, + // so we need to recalculate them using the actual screen size we get from + // EnumDisplaySettings api. + const auto dpiAwareness = QWindowsContext::instance()->processDpiAwareness(); + if (dpiAwareness != QtWindows::DpiAwareness::PerMonitor && + dpiAwareness != QtWindows::DpiAwareness::PerMonitorVersion2) { + MONITORINFOEX info = {}; + info.cbSize = sizeof(MONITORINFOEX); + if (GetMonitorInfo(handle(), &info)) { + DEVMODE dm = {}; + dm.dmSize = sizeof(dm); + if (EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &dm)) { + qreal scale = static_cast<qreal>(dm.dmPelsWidth) / windowSize.width(); + x = static_cast<int>(static_cast<qreal>(x) * scale); + y = static_cast<int>(static_cast<qreal>(y) * scale); + windowSize = QSize(dm.dmPelsWidth, dm.dmPelsHeight); + } + } + } x += screenGeometry.x(); y += screenGeometry.y(); } diff --git a/src/plugins/sqldrivers/.cmake.conf b/src/plugins/sqldrivers/.cmake.conf index be788d10f8e..846c4f3b923 100644 --- a/src/plugins/sqldrivers/.cmake.conf +++ b/src/plugins/sqldrivers/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.11.0") +set(QT_REPO_MODULE_VERSION "6.12.0") diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp index 7caa352afe4..98e51397c17 100644 --- a/src/plugins/styles/modernwindows/qwindows11style.cpp +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -596,7 +596,7 @@ void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOpt if (sub & SC_ComboBoxArrow) { QRectF rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); painter->setFont(d->assetFont); - painter->setPen(controlTextColor(option)); + painter->setPen(controlTextColor(option, true)); painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed)); } if (state & State_KeyboardFocusChange && hasFocus) { @@ -887,7 +887,7 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption if (isOn) { painter->setFont(d->assetFont); - painter->setPen(controlTextColor(option, QPalette::Window)); + painter->setPen(controlTextColor(option)); qreal clipWidth = 1.0; const QString str = fluentIcon(Icon::AcceptMedium); QFontMetrics fm(d->assetFont); @@ -907,19 +907,24 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption QFont f(d->assetFont); f.setPointSize(6); painter->setFont(f); - painter->setPen(controlTextColor(option, QPalette::Window)); + painter->setPen(controlTextColor(option)); painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::Dash12)); } } break; case PE_IndicatorBranch: { if (option->state & State_Children) { + const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option); const bool isReverse = option->direction == Qt::RightToLeft; const bool isOpen = option->state & QStyle::State_Open; + const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); QFont f(d->assetFont); f.setPointSize(8); painter->setFont(f); - painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled, + if (view && view->alternatingRowColors() && vopt && vopt->state & State_Selected) + painter->setPen(winUI3Color(textOnAccentPrimary)); + else + painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled, QPalette::WindowText)); const auto ico = isOpen ? Icon::ChevronDownMed : (isReverse ? Icon::ChevronLeftMed @@ -1071,14 +1076,16 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption if (option->state & State_Selected && !highContrastTheme) { // keep in sync with CE_ItemViewItem QListView indicator painting - const auto col = option->palette.accent().color(); - painter->setBrush(col); - painter->setPen(col); - const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f; - const auto yOfs = rect.height() / 4.; - QRectF r(QPointF(xPos, rect.y() + yOfs), - QPointF(xPos + 1, rect.y() + rect.height() - yOfs)); - painter->drawRoundedRect(r, 1, 1); + if (!qobject_cast<const QTableView *>(widget)) { + const auto col = option->palette.accent().color(); + painter->setBrush(col); + painter->setPen(col); + const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f; + const auto yOfs = rect.height() / 4.; + QRectF r(QPointF(xPos, rect.y() + yOfs), + QPointF(xPos + 1, rect.y() + rect.height() - yOfs)); + painter->drawRoundedRect(r, 1, 1); + } } const bool isTreeDecoration = vopt->features.testFlag( @@ -1099,7 +1106,7 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption } const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); - painter->setBrush(view->alternatingRowColors() ? vopt->palette.highlight() : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + painter->setBrush(view->alternatingRowColors() && state & State_Selected ? calculateAccentColor(option) : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); painter->setPen(Qt::NoPen); if (isFirst) { QPainterStateGuard psg(painter); @@ -1207,7 +1214,7 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op case QStyle::CE_ComboBoxLabel: #if QT_CONFIG(combobox) if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { - painter->setPen(controlTextColor(option)); + painter->setPen(controlTextColor(option, true)); QStyleOptionComboBox newOption = *cb; newOption.rect.adjust(4,0,-4,0); QCommonStyle::drawControl(element, &newOption, painter, widget); @@ -1753,13 +1760,13 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op } } const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver); + const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); if (highlightCurrent) { if (highContrastTheme) { painter->setBrush(vopt->palette.highlight()); } else { - const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); - painter->setBrush(view && view->alternatingRowColors() - ? vopt->palette.highlight() + painter->setBrush(view && view->alternatingRowColors() && vopt->state & State_Selected + ? calculateAccentColor(option) : winUI3Color(subtleHighlightColor)); } } else { @@ -1815,8 +1822,13 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); } - painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color() - : vopt->palette.text().color()); + if (highlightCurrent && highContrastTheme) { + painter->setPen(vopt->palette.base().color()); + } else if ((view && view->alternatingRowColors() && highlightCurrent && vopt->state & State_Selected)) { + painter->setPen(winUI3Color(textOnAccentPrimary)); + } else { + painter->setPen(vopt->palette.text().color()); + } d->viewItemDrawText(painter, vopt, textRect); // paint a vertical marker for QListView @@ -2610,6 +2622,7 @@ void QWindows11Style::polish(QPalette& result) d->m_titleBarNormalIcon = QIcon(); d->m_toolbarExtensionButton = QIcon(); d->m_lineEditClearButton = QIcon(); + d->m_tabCloseButton = QIcon(); } QPixmap QWindows11Style::standardPixmap(StandardPixmap standardPixmap, @@ -2650,6 +2663,14 @@ QIcon QWindows11Style::standardIcon(StandardPixmap standardIcon, } return d->m_toolbarExtensionButton; } + case SP_TabCloseButton: { + if (d->m_tabCloseButton.isNull()) { + auto e = new WinFontIconEngine(fluentIcon(Icon::ChromeClose), d->assetFont); + e->setScale(0.6); + d->m_tabCloseButton = QIcon(e); + } + return d->m_tabCloseButton; + } default: break; } @@ -2725,7 +2746,7 @@ QBrush QWindows11Style::inputFillBrush(const QStyleOption *option, const QWidget return winUI3Color(fillControlDefault); } -QColor QWindows11Style::controlTextColor(const QStyleOption *option, QPalette::ColorRole role) const +QColor QWindows11Style::controlTextColor(const QStyleOption *option, bool ignoreIsChecked) const { using namespace StyleOptionHelper; static constexpr WINUI3Color colorEnums[2][4] = { @@ -2738,12 +2759,9 @@ QColor QWindows11Style::controlTextColor(const QStyleOption *option, QPalette::C if (option->palette.isBrushSet(QPalette::Current, QPalette::ButtonText)) return option->palette.buttonText().color(); - const int colorIndex = isChecked(option) ? 1 : 0; + const int colorIndex = !ignoreIsChecked && isChecked(option) ? 1 : 0; const auto state = calcControlState(option); - const auto alpha = winUI3Color(colorEnums[colorIndex][int(state)]); - QColor col = option->palette.color(role); - col.setAlpha(alpha.alpha()); - return col; + return winUI3Color(colorEnums[colorIndex][int(state)]); } void QWindows11Style::drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable) const diff --git a/src/plugins/styles/modernwindows/qwindows11style_p.h b/src/plugins/styles/modernwindows/qwindows11style_p.h index 96c2c4136e0..43a344a6ac9 100644 --- a/src/plugins/styles/modernwindows/qwindows11style_p.h +++ b/src/plugins/styles/modernwindows/qwindows11style_p.h @@ -104,8 +104,7 @@ private: QBrush controlFillBrush(const QStyleOption *option, ControlType controlType) const; QBrush inputFillBrush(const QStyleOption *option, const QWidget *widget) const; // ControlType::ControlAlt can be mapped to QPalette directly - QColor controlTextColor(const QStyleOption *option, - QPalette::ColorRole role = QPalette::ButtonText) const; + QColor controlTextColor(const QStyleOption *option, bool ignoreIsChecked = false) const; void drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable = true) const; inline QColor winUI3Color(enum WINUI3Color col) const; @@ -126,6 +125,7 @@ class QWindows11StylePrivate : public QWindowsVistaStylePrivate { protected: QIcon m_toolbarExtensionButton; QIcon m_lineEditClearButton; + QIcon m_tabCloseButton; }; QT_END_NAMESPACE diff --git a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp index 64ffba2d6f8..36b5d0f0143 100644 --- a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp +++ b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp @@ -16,6 +16,9 @@ #include <private/qapplication_p.h> #include <private/qsystemlibrary_p.h> #include <private/qwindowsthemecache_p.h> +#if QT_CONFIG(tooltip) +#include "private/qtooltip_p.h" +#endif #include "qdrawutil.h" // for now #include <qbackingstore.h> @@ -4676,7 +4679,7 @@ void QWindowsVistaStyle::polish(QWidget *widget) widget->setPalette(pal); } else #endif // QT_CONFIG(commandlinkbutton) - if (widget->inherits("QTipLabel")) { + if (qobject_cast<const QTipLabel *>(widget)) { //note that since tooltips are not reused //we do not have to care about unpolishing widget->setContentsMargins(3, 0, 4, 0); diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp index deb257be01c..d3b7d669ec7 100644 --- a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp +++ b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp @@ -407,8 +407,13 @@ QList<QSslCertificate> systemCaCertificates() for (const QByteArray &directory : directories) { for (const auto &dirEntry : QDirListing(QFile::decodeName(directory), flags)) { // use canonical path here to not load the same certificate twice if symlinked - if (hasMatchingExtension(dirEntry.fileName())) - certFiles.insert(dirEntry.canonicalFilePath()); + if (hasMatchingExtension(dirEntry.fileName())) { + QString canonicalPath = dirEntry.canonicalFilePath(); + // skip broken symlinks to not end up adding "" to the list which will then + // just be rejected by `QSslCertificate::fromFile` + if (!canonicalPath.isEmpty()) + certFiles.insert(canonicalPath); + } } } for (const QString& file : std::as_const(certFiles)) diff --git a/src/testlib/doc/src/qt-webpages.qdoc b/src/testlib/doc/src/qt-webpages.qdoc index 611f3795ba9..b32fd4f750f 100644 --- a/src/testlib/doc/src/qt-webpages.qdoc +++ b/src/testlib/doc/src/qt-webpages.qdoc @@ -15,7 +15,3 @@ \title Googletest Mocking (gMock) Framework */ -/*! - \externalpage https://www.itk.org/Wiki/CMake_Testing_With_CTest - \title CMake/Testing With CTest -*/ diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index 29cafe9aea4..f3db4ece49e 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -305,9 +305,7 @@ namespace QTest { { static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(QTest::maxWarnings); - auto loggerCapture = loggers->allLoggers(); - - if (loggerCapture.isEmpty()) { + if (loggers.isDestroyed() || loggers->allLoggers().isEmpty()) { // the message handler may be called from a worker thread, after the main thread stopped // logging. Forwarding to original message handler to avoid swallowing the message Q_ASSERT(oldMessageHandler); @@ -326,6 +324,8 @@ namespace QTest { return; } + auto loggerCapture = loggers->allLoggers(); + if (type != QtFatalMsg) { if (counter.loadRelaxed() <= 0) return; diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index b517d85c5fb..161d95db49c 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -28,7 +28,16 @@ using namespace Qt::StringLiterals; -#define EXIT_ERROR -1 + +// QTest-based test processes may exit with up to 127 for normal test failures +static constexpr int HIGHEST_QTEST_EXITCODE = 127; +// Something went wrong in androidtestrunner, in general +static constexpr int EXIT_ERROR = 254; +// More specific exit codes for failures in androidtestrunner: +static constexpr int EXIT_NOEXITCODE = 253; // Failed to transfer exit code from device +static constexpr int EXIT_ANR = 252; // Android ANR error (Application Not Responding) +static constexpr int EXIT_NORESULTS = 251; // Failed to transfer result files from device + struct Options { @@ -71,6 +80,13 @@ struct TestInfo static TestInfo g_testInfo; +// QTest-based processes return 0 if all tests PASSed, or the number of FAILs up to 127. +// Other exitcodes signify abnormal termination and are system-dependent. +static bool isTestExitCodeNormal(const int ec) +{ + return (ec >= 0 && ec <= HIGHEST_QTEST_EXITCODE); +} + static bool execCommand(const QString &program, const QStringList &args, QByteArray *output = nullptr, bool verbose = false) { @@ -744,9 +760,9 @@ void printLogcatCrash(const QByteArray &logcat) } if (!crashLogcat.startsWith("********** Crash dump")) - qDebug() << "********** Crash dump: **********"; + qDebug() << "[androidtestrunner] ********** BEGIN crash dump **********"; qDebug().noquote() << crashLogcat.trimmed(); - qDebug() << "********** End crash dump **********"; + qDebug() << "[androidtestrunner] ********** END crash dump **********"; } void analyseLogcat(const QString &timeStamp, int *exitCode) @@ -781,10 +797,13 @@ void analyseLogcat(const QString &timeStamp, int *exitCode) // Check for ANRs const bool anrOccurred = logcat.contains("ANR in %1"_L1.arg(g_options.package).toUtf8()); if (anrOccurred) { - // Treat a found ANR as a test failure. - *exitCode = *exitCode < 1 ? 1 : *exitCode; - qCritical("An ANR has occurred while running the test %s. The logcat will include " - "additional logs from the system_server process.", + // Rather improbable, but if the test managed to return a non-crash exitcode then overwrite + // it to signify that something blew up. Same if we didn't manage to collect an exit code. + // Preserve all other exitcodes, they might be useful crash information from the device. + if (isTestExitCodeNormal(*exitCode) || *exitCode == EXIT_NOEXITCODE) + *exitCode = EXIT_ANR; + qCritical("[androidtestrunner] An ANR has occurred while running the test '%s';" + " consult logcat for additional logs from the system_server process", qPrintable(g_options.package)); } @@ -818,13 +837,14 @@ void analyseLogcat(const QString &timeStamp, int *exitCode) } } - // If we have a crash, attempt to print both logcat and the crash buffer which - // includes the crash stacktrace that is not included in the default logcat. - const bool testCrashed = *exitCode == EXIT_ERROR && !g_testInfo.isTestRunnerInterrupted.load(); + // If we have an unpredictable exitcode, possibly a crash, attempt to print both logcat and the + // crash buffer which includes the crash stacktrace that is not included in the default logcat. + const bool testCrashed = ( !isTestExitCodeNormal(*exitCode) + && !g_testInfo.isTestRunnerInterrupted.load()); if (testCrashed) { - qDebug() << "********** logcat dump **********"; + qDebug() << "[androidtestrunner] ********** BEGIN logcat dump **********"; qDebug().noquote() << testLogcat.join(u'\n').trimmed(); - qDebug() << "********** End logcat dump **********"; + qDebug() << "[androidtestrunner] ********** END logcat dump **********"; if (!crashLogcat.isEmpty()) printLogcatCrash(crashLogcat); @@ -839,7 +859,7 @@ static QString getCurrentTimeString() QStringList dateArgs = { "shell"_L1, "date"_L1, "+'%1'"_L1.arg(timeFormat) }; QByteArray output; if (!execAdbCommand(dateArgs, &output, false)) { - qWarning() << "Date/time adb command failed"; + qWarning() << "[androidtestrunner] ERROR in command: adb shell date"; return {}; } @@ -851,14 +871,15 @@ static int testExitCode() QByteArray exitCodeOutput; const QString exitCodeCmd = "cat files/qtest_last_exit_code 2> /dev/null"_L1; if (!execAdbCommand({ "shell"_L1, runCommandAsUserArgs(exitCodeCmd) }, &exitCodeOutput, false)) { - qCritical() << "Failed to retrieve the test exit code."; - return EXIT_ERROR; + qCritical() << "[androidtestrunner] ERROR in command: adb shell cat files/qtest_last_exit_code"; + return EXIT_NOEXITCODE; } + qDebug() << "[androidtestrunner] Test exitcode: " << exitCodeOutput; bool ok; int exitCode = exitCodeOutput.toInt(&ok); - return ok ? exitCode : EXIT_ERROR; + return ok ? exitCode : EXIT_NOEXITCODE; } static bool uninstallTestPackage() @@ -899,7 +920,7 @@ void sigHandler(int signal) // a main event loop. Since, there's no other alternative to do this, // let's do the cleanup anyway. if (!g_testInfo.isPackageInstalled.load()) - _exit(-1); + _exit(EXIT_ERROR); g_testInfo.isTestRunnerInterrupted.store(true); } @@ -1031,7 +1052,9 @@ int main(int argc, char *argv[]) if (g_options.showLogcatOutput) analyseLogcat(formattedStartTime, &exitCode); - exitCode = pullResults() ? exitCode : EXIT_ERROR; + const bool pullRes = pullResults(); + if (!pullRes && isTestExitCodeNormal(exitCode)) + exitCode = EXIT_NORESULTS; if (!uninstallTestPackage()) return EXIT_ERROR; diff --git a/src/tools/configure.cmake b/src/tools/configure.cmake index 27ea90b89ac..07e11dd935b 100644 --- a/src/tools/configure.cmake +++ b/src/tools/configure.cmake @@ -37,7 +37,7 @@ qt_feature("qmake" PRIVATE QT_FEATURE_datestring AND QT_FEATURE_regularexpression AND QT_FEATURE_temporaryfile) qt_feature("qtwaylandscanner" PRIVATE - CONDITION TARGET Wayland::Scanner + CONDITION TARGET Wayland::Scanner AND NOT INTEGRITY AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT QNX AND NOT VXWORKS ) qt_configure_add_summary_section(NAME "Core tools") diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index d43b6ec4fb2..c47e3bee13c 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -412,7 +412,7 @@ qt_internal_extend_target(Widgets CONDITION QT_FEATURE_shortcut qt_internal_extend_target(Widgets CONDITION QT_FEATURE_tooltip SOURCES - kernel/qtooltip.cpp kernel/qtooltip.h + kernel/qtooltip.cpp kernel/qtooltip.h kernel/qtooltip_p.h ) qt_internal_extend_target(Widgets CONDITION QT_FEATURE_whatsthis diff --git a/src/widgets/accessible/qaccessiblecolorwell.cpp b/src/widgets/accessible/qaccessiblecolorwell.cpp index ca08e511e9a..64fcd2a7fd1 100644 --- a/src/widgets/accessible/qaccessiblecolorwell.cpp +++ b/src/widgets/accessible/qaccessiblecolorwell.cpp @@ -3,6 +3,8 @@ #include "private/qaccessiblecolorwell_p.h" +#include <QtCore/qcoreapplication.h> + QT_REQUIRE_CONFIG(accessibility); #if QT_CONFIG(colordialog) @@ -14,6 +16,7 @@ class QAccessibleColorWellItem : public QAccessibleInterface { QAccessibleColorWell *m_parent; + Q_DECLARE_TR_FUNCTIONS(QAccessibleColorWellItem) public: QAccessibleColorWellItem(QAccessibleColorWell *parent); @@ -79,7 +82,7 @@ QString QAccessibleColorWellItem::text(QAccessible::Text t) const if (t == QAccessible::Name) { QRgb color = m_parent->colorWell()->rgbValues()[m_parent->indexOfChild(this)]; //: Color specified via its 3 RGB components (red, green, blue) - return QObject::tr("RGB %1, %2, %3") + return tr("RGB %1, %2, %3") .arg(QString::number(qRed(color)), QString::number(qGreen(color)), QString::number(qBlue(color))); } diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index d51c408ab5c..ce46170bba5 100644 --- a/src/widgets/dialogs/qcolordialog.cpp +++ b/src/widgets/dialogs/qcolordialog.cpp @@ -662,7 +662,7 @@ private: int val2y(int val); void setVal(int v); - QPixmap *pix; + QPixmap pix; }; @@ -682,14 +682,12 @@ QColorLuminancePicker::QColorLuminancePicker(QWidget* parent) :QWidget(parent) { hue = 100; val = 100; sat = 100; - pix = nullptr; // setAttribute(WA_NoErase, true); setFocusPolicy(Qt::StrongFocus); } QColorLuminancePicker::~QColorLuminancePicker() { - delete pix; } void QColorLuminancePicker::keyPressEvent(QKeyEvent *event) @@ -725,7 +723,7 @@ void QColorLuminancePicker::setVal(int v) if (val == v) return; val = qMax(0, qMin(v,255)); - delete pix; pix=nullptr; + pix = QPixmap(); repaint(); emit newHsv(hue, sat, val); } @@ -744,8 +742,7 @@ void QColorLuminancePicker::paintEvent(QPaintEvent *) QRect r(0, foff, w, height() - 2*foff); int wi = r.width() - 2; int hi = r.height() - 2; - if (!pix || pix->height() != hi || pix->width() != wi) { - delete pix; + if (pix.isNull() || pix.height() != hi || pix.width() != wi) { QImage img(wi, hi, QImage::Format_RGB32); int y; uint *pixel = (uint *) img.scanLine(0); @@ -754,10 +751,10 @@ void QColorLuminancePicker::paintEvent(QPaintEvent *) std::fill(pixel, end, QColor::fromHsv(hue, sat, y2val(y + coff)).rgb()); pixel = end; } - pix = new QPixmap(QPixmap::fromImage(img)); + pix = QPixmap::fromImage(img); } QPainter p(this); - p.drawPixmap(1, coff, *pix); + p.drawPixmap(1, coff, pix); const QPalette &g = palette(); qDrawShadePanel(&p, r, g, true); p.setPen(g.windowText().color()); @@ -773,7 +770,7 @@ void QColorLuminancePicker::setCol(int h, int s , int v) val = v; hue = h; sat = s; - delete pix; pix=nullptr; + pix = QPixmap(); repaint(); } diff --git a/src/widgets/doc/src/external-resources.qdoc b/src/widgets/doc/src/external-resources.qdoc index 96117546a29..0eccc3b19d4 100644 --- a/src/widgets/doc/src/external-resources.qdoc +++ b/src/widgets/doc/src/external-resources.qdoc @@ -1,12 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \externalpage http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html - \title Apple Human Interface Guidelines -*/ - /*! \externalpage https://rk.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm \title Sinclair Spectrum diff --git a/src/widgets/kernel/qtooltip.cpp b/src/widgets/kernel/qtooltip.cpp index fa17c94a23f..97332cd7d5d 100644 --- a/src/widgets/kernel/qtooltip.cpp +++ b/src/widgets/kernel/qtooltip.cpp @@ -6,15 +6,12 @@ #include <qapplication.h> #include <qevent.h> -#include <qpointer.h> #include <qstyle.h> #include <qstyleoption.h> #include <qstylepainter.h> #if QT_CONFIG(effects) #include <private/qeffects_p.h> #endif -#include <qtextdocument.h> -#include <qdebug.h> #include <qpa/qplatformscreen.h> #include <qpa/qplatformcursor.h> #if QT_CONFIG(style_stylesheet) @@ -23,12 +20,10 @@ #include <qpa/qplatformwindow.h> #include <qpa/qplatformwindow_p.h> -#include <qlabel.h> #include <QtWidgets/private/qlabel_p.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <qtooltip.h> - -#include <QtCore/qbasictimer.h> +#include <QtWidgets/private/qtooltip_p.h> QT_BEGIN_NAMESPACE @@ -93,57 +88,6 @@ using namespace Qt::StringLiterals; \sa QWidget::toolTip, QAction::toolTip */ -class QTipLabel : public QLabel -{ - Q_OBJECT -public: - QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime); - ~QTipLabel(); - static QTipLabel *instance; - - void adjustTooltipScreen(const QPoint &pos); - void updateSize(const QPoint &pos); - - bool eventFilter(QObject *, QEvent *) override; - - QBasicTimer hideTimer, expireTimer; - - bool fadingOut; - - void reuseTip(const QString &text, int msecDisplayTime, const QPoint &pos); - void hideTip(); - void hideTipImmediately(); - void setTipRect(QWidget *w, const QRect &r); - void restartExpireTimer(int msecDisplayTime); - bool tipChanged(const QPoint &pos, const QString &text, QObject *o); - void placeTip(const QPoint &pos, QWidget *w); - - static QScreen *getTipScreen(const QPoint &pos, QWidget *w); -protected: - void timerEvent(QTimerEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -#if QT_CONFIG(style_stylesheet) -public slots: - /** \internal - Cleanup the _q_stylesheet_parent property. - */ - void styleSheetParentDestroyed() { - setProperty("_q_stylesheet_parent", QVariant()); - styleSheetParent = nullptr; - } - -private: - QWidget *styleSheetParent; -#endif - -private: - QWidget *widget; - QRect rect; -}; - QTipLabel *QTipLabel::instance = nullptr; QTipLabel::QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime) @@ -152,6 +96,7 @@ QTipLabel::QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int mse , styleSheetParent(nullptr) #endif , widget(nullptr) + , fadingOut(false) { delete instance; instance = this; @@ -166,7 +111,6 @@ QTipLabel::QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int mse qApp->installEventFilter(this); setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, nullptr, this) / 255.0); setMouseTracking(true); - fadingOut = false; reuseTip(text, msecDisplayTime, pos); } @@ -240,10 +184,10 @@ void QTipLabel::resizeEvent(QResizeEvent *e) void QTipLabel::mouseMoveEvent(QMouseEvent *e) { if (!rect.isNull()) { - QPoint pos = e->globalPosition().toPoint(); + QPointF pos = e->globalPosition(); if (widget) pos = widget->mapFromGlobal(pos); - if (!rect.contains(pos)) + if (!rect.contains(pos.toPoint())) hideTip(); } QLabel::mouseMoveEvent(e); @@ -433,6 +377,15 @@ bool QTipLabel::tipChanged(const QPoint &pos, const QString &text, QObject *o) return false; } +/** \internal + Cleanup the _q_stylesheet_parent property. + */ +void QTipLabel::styleSheetParentDestroyed() +{ + setProperty("_q_stylesheet_parent", QVariant()); + styleSheetParent = nullptr; +} + /*! Shows \a text as a tool tip, with the global position \a pos as the point of interest. The tool tip will be shown with a platform @@ -594,4 +547,4 @@ void QToolTip::setFont(const QFont &font) QT_END_NAMESPACE -#include "qtooltip.moc" +#include "moc_qtooltip_p.cpp" diff --git a/src/widgets/kernel/qtooltip_p.h b/src/widgets/kernel/qtooltip_p.h new file mode 100644 index 00000000000..51faaf58c34 --- /dev/null +++ b/src/widgets/kernel/qtooltip_p.h @@ -0,0 +1,74 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default + +#ifndef QTOOLTIP_P_H +#define QTOOLTIP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qlayout*.cpp, and qabstractlayout.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QLabel> +#include <QString> +#include <QRect> +#include <QToolTip> + +QT_REQUIRE_CONFIG(tooltip); +QT_BEGIN_NAMESPACE + +class Q_WIDGETS_EXPORT QTipLabel final : public QLabel +{ + Q_OBJECT +public: + explicit QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime); + ~QTipLabel() override; + + void adjustTooltipScreen(const QPoint &pos); + void updateSize(const QPoint &pos); + + bool eventFilter(QObject *, QEvent *) override; + + void reuseTip(const QString &text, int msecDisplayTime, const QPoint &pos); + void hideTip(); + void hideTipImmediately(); + void setTipRect(QWidget *w, const QRect &r); + void restartExpireTimer(int msecDisplayTime); + bool tipChanged(const QPoint &pos, const QString &text, QObject *o); + void placeTip(const QPoint &pos, QWidget *w); + + static QScreen *getTipScreen(const QPoint &pos, QWidget *w); +protected: + void timerEvent(QTimerEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +#if QT_CONFIG(style_stylesheet) +public Q_SLOTS: + void styleSheetParentDestroyed(); + +private: + QWidget *styleSheetParent; +#endif + +private: + friend class QToolTip; + + static QTipLabel *instance; + QBasicTimer hideTimer, expireTimer; + QWidget *widget; + QRect rect; + bool fadingOut; +}; + +QT_END_NAMESPACE + +#endif // QTOOLTIP_P_H diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 9499c88af12..bd2b5be11aa 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -32,7 +32,7 @@ #include "private/qwidgetwindow_p.h" #include "qpainter.h" #if QT_CONFIG(tooltip) -#include "qtooltip.h" +#include "private/qtooltip_p.h" #endif #if QT_CONFIG(whatsthis) #include "qwhatsthis.h" @@ -1435,7 +1435,9 @@ void QWidgetPrivate::createTLSysExtra() if (extra->topextra->opacity != 255 && q->isWindow()) extra->topextra->window->setOpacity(qreal(extra->topextra->opacity) / qreal(255)); - const bool isTipLabel = q->inherits("QTipLabel"); +#if QT_CONFIG(tooltip) + const bool isTipLabel = qobject_cast<const QTipLabel *>(q) != nullptr; +#endif const bool isAlphaWidget = !isTipLabel && q->inherits("QAlphaWidget"); #ifdef Q_OS_WIN // Pass on native parent handle for Widget embedded into Active X. diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 90c1cfb4b86..592b70ef8ba 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -1994,12 +1994,9 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, tr = proxy()->subElementRect(SE_TabBarTabText, opt, widget); if (!tab->icon.isNull()) { - QPixmap tabIcon = tab->icon.pixmap(tab->iconSize, QStyleHelper::getDpr(p), - (tab->state & State_Enabled) ? QIcon::Normal - : QIcon::Disabled, - (tab->state & State_Selected) ? QIcon::On - : QIcon::Off); - p->drawPixmap(iconRect.x(), iconRect.y(), tabIcon); + const auto mode = (tab->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled; + const auto state = (tab->state & State_Selected) ? QIcon::On : QIcon::Off; + tab->icon.paint(p, iconRect, Qt::AlignCenter, mode, state); } proxy()->drawItemText(p, tr, alignment, tab->palette, tab->state & State_Enabled, tab->text, diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 25b048db65e..e0fdd56e6d8 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -35,7 +35,7 @@ #include <qabstractscrollarea.h> #include "private/qabstractscrollarea_p.h" #if QT_CONFIG(tooltip) -#include <qtooltip.h> +#include "private/qtooltip_p.h" #endif #include <qshareddata.h> #if QT_CONFIG(toolbutton) @@ -950,7 +950,7 @@ QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject * hasFont = v.extractFont(&font, &adj); #if QT_CONFIG(tooltip) - if (object && qstrcmp(object->metaObject()->className(), "QTipLabel") == 0) + if (qobject_cast<const QTipLabel *>(object) != nullptr) palette = QToolTip::palette(); #endif @@ -1495,7 +1495,7 @@ bool QRenderRule::hasModification() const static inline QObject *parentObject(const QObject *obj) { #if QT_CONFIG(tooltip) - if (qobject_cast<const QLabel *>(obj) && qstrcmp(obj->metaObject()->className(), "QTipLabel") == 0) { + if (qobject_cast<const QTipLabel *>(obj) != nullptr) { QObject *p = qvariant_cast<QObject *>(obj->property("_q_stylesheet_parent")); if (p) return p; @@ -1515,7 +1515,7 @@ public: return QStringList(); const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject(); #if QT_CONFIG(tooltip) - if (qstrcmp(metaObject->className(), "QTipLabel") == 0) + if (metaObject == &QTipLabel::staticMetaObject) return QStringList("QToolTip"_L1); #endif QStringList result; @@ -1581,7 +1581,7 @@ public: return false; const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject(); #if QT_CONFIG(tooltip) - if (qstrcmp(metaObject->className(), "QTipLabel") == 0) + if (metaObject == &QTipLabel::staticMetaObject) return nodeName == "QToolTip"_L1; #endif do { @@ -1747,7 +1747,7 @@ int QStyleSheetStyle::nativeFrameWidth(const QWidget *w) } #endif - if (qstrcmp(w->metaObject()->className(), "QTipLabel") == 0) + if (w->metaObject() == &QTipLabel::staticMetaObject) return base->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, w); return base->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, w); diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp index 0f0abb6e1d5..44218d41ded 100644 --- a/src/widgets/widgets/qtabbar.cpp +++ b/src/widgets/widgets/qtabbar.cpp @@ -52,6 +52,14 @@ public: void enterEvent(QEnterEvent *event) override; void leaveEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; + + void setParentClipRect(const QRect &clipRect) + { + m_parentClipRect = clipRect; + } + +protected: + QRect m_parentClipRect; }; } @@ -598,10 +606,11 @@ QRect QTabBarPrivate::normalizedScrollRect(int index) q->initStyleOption(&opt, currentIndex); opt.rect = q->rect(); - QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q); - QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q); - QRect tearLeftRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q); - QRect tearRightRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q); + const auto style = q->style(); + QRect scrollButtonLeftRect = style->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q); + QRect scrollButtonRightRect = style->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q); + QRect tearLeftRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q); + QRect tearRightRect = style->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q); if (verticalTabs(shape)) { int topEdge, bottomEdge; @@ -739,7 +748,7 @@ void QTabBarPrivate::layoutTab(int index) if (tab->leftWidget) { QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q); QPoint p = rect.topLeft(); - if ((index == pressedIndex) || paintWithOffsets) { + if (index == pressedIndex) { if (vertical) p.setY(p.y() + tab->dragOffset); else @@ -750,7 +759,7 @@ void QTabBarPrivate::layoutTab(int index) if (tab->rightWidget) { QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q); QPoint p = rect.topLeft(); - if ((index == pressedIndex) || paintWithOffsets) { + if (index == pressedIndex) { if (vertical) p.setY(p.y() + tab->dragOffset); else @@ -1004,8 +1013,13 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text) } if (isVisible() && tabAt(d->mousePosition) == index) { - d->hoverIndex = index; - d->hoverRect = tabRect(index); + if (d->normalizedScrollRect(index).contains(d->mousePosition)) { + d->hoverIndex = index; + d->hoverRect = tabRect(index); + } else { + d->hoverIndex = -1; + d->hoverRect = QRect(); + } } tabInserted(index); @@ -1096,11 +1110,13 @@ void QTabBar::removeTab(int index) if (d->hoverRect.isValid()) { update(d->hoverRect); d->hoverIndex = tabAt(d->mousePosition); - if (d->validIndex(d->hoverIndex)) { + if (d->validIndex(d->hoverIndex) + && d->normalizedScrollRect(d->hoverIndex).contains(d->mousePosition)) { d->hoverRect = tabRect(d->hoverIndex); update(d->hoverRect); } else { d->hoverRect = QRect(); + d->hoverIndex = -1; } } tabRemoved(index); @@ -1692,15 +1708,18 @@ bool QTabBar::event(QEvent *event) case QEvent::HoverEnter: { QHoverEvent *he = static_cast<QHoverEvent *>(event); d->mousePosition = he->position().toPoint(); - if (!d->hoverRect.contains(d->mousePosition)) { + const auto sr = d->normalizedScrollRect(); + const auto oldHoverRect = d->hoverRect & sr; + if (!oldHoverRect.contains(d->mousePosition)) { if (d->hoverRect.isValid()) update(d->hoverRect); d->hoverIndex = tabAt(d->mousePosition); - if (d->validIndex(d->hoverIndex)) { + if (d->validIndex(d->hoverIndex) && sr.contains(d->mousePosition)) { d->hoverRect = tabRect(d->hoverIndex); update(d->hoverRect); } else { d->hoverRect = QRect(); + d->hoverIndex = -1; } } return true; @@ -1845,10 +1864,14 @@ void QTabBar::paintEvent(QPaintEvent *) QStyleOption opt; opt.initFrom(this); QRegion buttonRegion; - if (d->leftB->isVisible()) - buttonRegion |= style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, this); - if (d->rightB->isVisible()) - buttonRegion |= style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, this); + if (d->leftB->isVisible()) { + const auto r = style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, this); + buttonRegion |= r; + } + if (d->rightB->isVisible()) { + const auto r = style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, this); + buttonRegion |= r; + } if (!buttonRegion.isEmpty()) p.setClipRegion(QRegion(rect()) - buttonRegion); } @@ -1857,9 +1880,13 @@ void QTabBar::paintEvent(QPaintEvent *) const auto tab = d->tabList.at(i); if (!tab->visible) continue; + for (const auto side : { QTabBar::LeftSide, QTabBar::RightSide }) { + if (auto closeButton = qobject_cast<CloseButton *>(tabButton(i, side))) + closeButton->setParentClipRect(scrollRect); + } QStyleOptionTab tabOption; initStyleOption(&tabOption, i); - if (d->paintWithOffsets && tab->dragOffset != 0) { + if (tab->dragOffset != 0) { if (vertical) { tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset); } else { @@ -1901,7 +1928,7 @@ void QTabBar::paintEvent(QPaintEvent *) const auto tab = d->tabList.at(selected); initStyleOption(&tabOption, selected); - if (d->paintWithOffsets && tab->dragOffset != 0) { + if (tab->dragOffset != 0) { // if the drag offset is != 0, a move is in progress (drag or animation) // => set the tab position to Moving to preserve the rect tabOption.position = QStyleOptionTab::TabPosition::Moving; @@ -2934,6 +2961,11 @@ void CloseButton::paintEvent(QPaintEvent *) opt.state |= QStyle::State_Selected; } + if (m_parentClipRect.isValid()) { + auto tl = mapFromParent(m_parentClipRect.topLeft()); + auto br = mapFromParent(m_parentClipRect.bottomRight()); + p.setClipRect(QRect(tl, br)); + } style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this); } diff --git a/src/widgets/widgets/qtabbar_p.h b/src/widgets/widgets/qtabbar_p.h index 38fbde76470..5b31926253f 100644 --- a/src/widgets/widgets/qtabbar_p.h +++ b/src/widgets/widgets/qtabbar_p.h @@ -56,7 +56,7 @@ public: QTabBarPrivate() : layoutDirty(false), drawBase(true), elideModeSetByUser(false), useScrollButtons(false), useScrollButtonsSetByUser(false), expanding(true), closeButtonOnTabs(false), - paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false), + movable(false), dragInProgress(false), documentMode(false), autoHide(false), changeCurrentOnDrag(false) {} ~QTabBarPrivate() @@ -95,7 +95,6 @@ public: bool useScrollButtonsSetByUser : 1; bool expanding : 1; bool closeButtonOnTabs : 1; - bool paintWithOffsets : 1; bool movable : 1; bool dragInProgress : 1; bool documentMode : 1; diff --git a/src/xml/doc/src/external-resources.qdoc b/src/xml/doc/src/external-resources.qdoc index 89c30a84c47..89552477ae8 100644 --- a/src/xml/doc/src/external-resources.qdoc +++ b/src/xml/doc/src/external-resources.qdoc @@ -1,17 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \externalpage http://www.w3.org/2000/xmlns/ - \title http://www.w3.org/2000/xmlns/ -*/ - -/*! - \externalpage http://www.saxproject.org/ - \title SAX2 Java interface -*/ - /*! \externalpage http://www.w3.org/TR/DOM-Level-2-Core/ \title W3C DOM Level 2 |
