diff options
Diffstat (limited to 'src')
77 files changed, 881 insertions, 541 deletions
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index 82179cfd89a..8aad11dfca7 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -1157,6 +1157,18 @@ function(_qt_internal_assign_to_internal_targets_folder target) endif() endfunction() +# Returns the metatypes build dir where the Qt build system places module metatypes json files and +# other supporting metatypes files like ${target}_json_file_list.txt. +# The path is usually the target's BINARY_DIR + "/meta_types" +function(_qt_internal_get_metatypes_build_dir out_var target) + get_target_property(target_binary_dir "${target}" BINARY_DIR) + set(out_dir "${target_binary_dir}/meta_types") + set(${out_var} "${out_dir}" PARENT_SCOPE) +endfunction() + +# The AUTOGEN build dir is the location where all the generated .cpp files are placed, as well +# as the moc_predefs.h, timestamp file and deps files. +# E.g. ${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen/moc_predefs.h function(_qt_internal_get_target_autogen_build_dir target out_var) get_property(target_autogen_build_dir TARGET ${target} PROPERTY AUTOGEN_BUILD_DIR) if(target_autogen_build_dir) @@ -1167,6 +1179,14 @@ function(_qt_internal_get_target_autogen_build_dir target out_var) endif() endfunction() +# The AUTOGEN info dir is the location where AutogenInfo.json and ParseCache.txt files are placed. +# E.g. ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}_autogen.dir/ParseCache.txt +function(_qt_internal_get_target_autogen_info_dir target out_var) + get_target_property(target_binary_dir ${target} BINARY_DIR) + set(autogen_info_dir "${target_binary_dir}/CMakeFiles/${target}_autogen.dir") + set(${out_var} "${autogen_info_dir}" PARENT_SCOPE) +endfunction() + function(_qt_internal_should_install_metatypes target) set(args_option INTERNAL_INSTALL @@ -1354,12 +1374,14 @@ function(qt6_extract_metatypes target) return() endif() - get_target_property(target_binary_dir ${target} BINARY_DIR) - set(type_list_file "${target_binary_dir}/meta_types/${target}_json_file_list.txt") - set(type_list_file_manual "${target_binary_dir}/meta_types/${target}_json_file_list_manual.txt") + _qt_internal_get_metatypes_build_dir(metatypes_dir "${target}") + + set(type_list_file "${metatypes_dir}/${target}_json_file_list.txt") + set(type_list_file_manual "${metatypes_dir}/${target}_json_file_list_manual.txt") set(target_autogen_build_dir "") _qt_internal_get_target_autogen_build_dir(${target} target_autogen_build_dir) + _qt_internal_get_target_autogen_info_dir(${target} target_autogen_info_dir) get_target_property(uses_automoc ${target} AUTOMOC) set(automoc_args) @@ -1373,21 +1395,18 @@ function(qt6_extract_metatypes target) get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(NOT is_multi_config) - set(cmake_autogen_cache_file - "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/ParseCache.txt") + set(cmake_autogen_cache_file "${target_autogen_info_dir}/ParseCache.txt") set(multi_config_args --cmake-autogen-include-dir-path "${target_autogen_build_dir}/include" ) else() - set(cmake_autogen_cache_file - "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/ParseCache_$<CONFIG>.txt") + set(cmake_autogen_cache_file "${target_autogen_info_dir}/ParseCache_$<CONFIG>.txt") set(multi_config_args --cmake-autogen-include-dir-path "${target_autogen_build_dir}/include_$<CONFIG>" "--cmake-multi-config") endif() - set(cmake_autogen_info_file - "${target_binary_dir}/CMakeFiles/${target}_autogen.dir/AutogenInfo.json") + set(cmake_autogen_info_file "${target_autogen_info_dir}/AutogenInfo.json") set (use_dep_files FALSE) if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.17") # Requires automoc changes present only in 3.17 @@ -1502,11 +1521,11 @@ function(qt6_extract_metatypes target) string(TOLOWER ${target} target_lowercase) set(metatypes_file_name "qt6${target_lowercase}_metatypes.json") - set(metatypes_file "${target_binary_dir}/meta_types/${metatypes_file_name}") - set(metatypes_file_gen "${target_binary_dir}/meta_types/${metatypes_file_name}.gen") + set(metatypes_file "${metatypes_dir}/${metatypes_file_name}") + set(metatypes_file_gen "${metatypes_dir}/${metatypes_file_name}.gen") set(metatypes_dep_file_name "qt6${target_lowercase}_metatypes_dep.txt") - set(metatypes_dep_file "${target_binary_dir}/meta_types/${metatypes_dep_file_name}") + set(metatypes_dep_file "${metatypes_dir}/${metatypes_dep_file_name}") # Due to generated source file dependency rules being tied to the directory # scope in which they are created it is not possible for other targets which @@ -1517,7 +1536,7 @@ function(qt6_extract_metatypes target) # file is then replaced with the contents of the generated file during # build. if (NOT EXISTS ${metatypes_file}) - file(MAKE_DIRECTORY "${target_binary_dir}/meta_types") + file(MAKE_DIRECTORY "${metatypes_dir}") file(TOUCH ${metatypes_file}) endif() diff --git a/src/corelib/doc/src/qtcore.qdoc b/src/corelib/doc/src/qtcore.qdoc index ea65d68da58..ec5fa564639 100644 --- a/src/corelib/doc/src/qtcore.qdoc +++ b/src/corelib/doc/src/qtcore.qdoc @@ -19,8 +19,7 @@ \module QtCorePrivate \title Qt Core Private C++ Classes \qtvariable core-private - \qtcmakepackage Core - \qtcmaketargetitem CorePrivate + \qtcmakepackage CorePrivate \preliminary \brief Provides private core functionality. @@ -28,7 +27,7 @@ private Qt Core APIs: \badcode - find_package(Qt6 REQUIRED COMPONENTS Core) + find_package(Qt6 REQUIRED COMPONENTS CorePrivate) target_link_libraries(mytarget PRIVATE Qt6::CorePrivate) \endcode */ diff --git a/src/corelib/global/qalloc.h b/src/corelib/global/qalloc.h index 9d40f4261d3..a05c09ac63c 100644 --- a/src/corelib/global/qalloc.h +++ b/src/corelib/global/qalloc.h @@ -21,6 +21,7 @@ #include <QtCore/qtypeinfo.h> #include <cstddef> +#include <cstdlib> QT_BEGIN_NAMESPACE diff --git a/src/corelib/global/qassert.h b/src/corelib/global/qassert.h index d1d306fd3ed..05210acb2d4 100644 --- a/src/corelib/global/qassert.h +++ b/src/corelib/global/qassert.h @@ -100,6 +100,25 @@ inline bool qt_assume_is_deprecated(bool cond) noexcept { return cond; } Q_ASSUME_IMPL(valueOfExpression);\ }(qt_assume_is_deprecated(Expr)) + +#if __has_builtin(__builtin_assume) +// Clang has this intrinsic and won't warn about its use in C++20 mode +# define Q_PRESUME_IMPL(assumption) __builtin_assume(assumption) +#elif __has_cpp_attribute(assume) +// GCC has implemented this attribute and allows its use in C++20 mode +# define Q_PRESUME_IMPL(assumption) [[assume(assumption)]] +#elif defined(Q_CC_MSVC) +# define Q_PRESUME_IMPL(assumption) __assume(assumption) +#else +# define Q_PRESUME_IMPL(assumption) (void)0 +#endif + +#define Q_PRESUME(assumption) \ + [&] { \ + Q_ASSERT(assumption); \ + Q_PRESUME_IMPL(assumption); \ + }() + // Don't use these in C++ mode, use static_assert directly. // These are here only to keep old code compiling. # define Q_STATIC_ASSERT(Condition) static_assert(bool(Condition), #Condition) diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 700c59ab3c7..df55baf3120 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -1443,6 +1443,10 @@ QT_WARNING_DISABLE_MSVC(4706) /* assignment within conditional expression */ QT_WARNING_DISABLE_MSVC(4355) /* 'this' : used in base member initializer list */ QT_WARNING_DISABLE_MSVC(4710) /* function not inlined */ QT_WARNING_DISABLE_MSVC(4530) /* C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc */ +# elif defined(Q_CC_CLANG_ONLY) +# if Q_CC_CLANG >= 2100 + QT_WARNING_DISABLE_CLANG("-Wcharacter-conversion") /* until https://github.com/llvm/llvm-project/issues/163719 is fixed */ +# endif # elif defined(Q_CC_BOR) # pragma option -w-inl # pragma option -w-aus diff --git a/src/corelib/global/qfloat16.h b/src/corelib/global/qfloat16.h index 01106abf34d..cb8514105a0 100644 --- a/src/corelib/global/qfloat16.h +++ b/src/corelib/global/qfloat16.h @@ -354,15 +354,15 @@ inline int qIntCast(qfloat16 f) noexcept { return int(static_cast<qfloat16::NearestFloat>(f)); } #if !defined(Q_QDOC) && !QFLOAT16_IS_NATIVE -QT_WARNING_PUSH -QT_WARNING_DISABLE_CLANG("-Wc99-extensions") -QT_WARNING_DISABLE_GCC("-Wold-style-cast") inline qfloat16::qfloat16(float f) noexcept { #if defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__) __m128 packsingle = _mm_set_ss(f); + QT_WARNING_PUSH + QT_WARNING_DISABLE_GCC("-Wold-style-cast") // _mm_cvtps_ph() may be a macro using C-style casts __m128i packhalf = _mm_cvtps_ph(packsingle, 0); - b16 = _mm_extract_epi16(packhalf, 0); + QT_WARNING_POP + b16 = quint16(_mm_extract_epi16(packhalf, 0)); #elif defined (__ARM_FP16_FORMAT_IEEE) __fp16 f16 = __fp16(f); memcpy(&b16, &f16, sizeof(quint16)); @@ -393,7 +393,6 @@ inline qfloat16::qfloat16(float f) noexcept b16 = quint16(base + (mantissa >> shift)); #endif } -QT_WARNING_POP inline qfloat16::operator float() const noexcept { diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h index 723a462bae1..6caf3510f8a 100644 --- a/src/corelib/global/qnumeric.h +++ b/src/corelib/global/qnumeric.h @@ -27,6 +27,8 @@ # include <QtCore/qstdlibdetection.h> # if defined(Q_CC_GNU_ONLY) && (defined(Q_STL_LIBCPP) || Q_CC_GNU_ONLY < 1500) // broken - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121811 +# elif defined(Q_OS_FREEBSD) && __FreeBSD_version <= 1500000 +// broken - https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=290299 # else # include <stdckdint.h> # endif diff --git a/src/corelib/io/qlockfile.cpp b/src/corelib/io/qlockfile.cpp index 908db7b9d38..47229c8e6a1 100644 --- a/src/corelib/io/qlockfile.cpp +++ b/src/corelib/io/qlockfile.cpp @@ -24,19 +24,6 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -namespace { -struct LockFileInfo -{ - qint64 pid; - QString appname; - QString hostname; - QByteArray hostid; - QByteArray bootid; -}; -} - -static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info); - static QString machineName() { #ifdef Q_OS_WIN @@ -364,8 +351,8 @@ bool QLockFile::tryLock(std::chrono::milliseconds timeout) bool QLockFile::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const { Q_D(const QLockFile); - LockFileInfo info; - if (!getLockInfo_helper(d->fileName, &info)) + QLockFilePrivate::LockFileInfo info; + if (!QLockFilePrivate::getLockInfo_helper(d->fileName, &info)) return false; if (pid) *pid = info.pid; @@ -399,11 +386,16 @@ QByteArray QLockFilePrivate::lockFileContents() const % QSysInfo::bootUniqueId() % '\n'; } -static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info) +bool QLockFilePrivate::getLockInfo_helper(const QString &fileName, LockFileInfo *info) { - QFile reader(fileName); - if (!reader.open(QIODevice::ReadOnly | QIODevice::Text)) + int fd = openNewFileDescriptor(fileName); + if (fd < 0) + return false; + QFile reader; + if (!reader.open(fd, QFile::ReadOnly | QFile::Text, QFile::AutoCloseHandle)) { + QT_CLOSE(fd); return false; + } QByteArray pidLine = reader.readLine(); pidLine.chop(1); diff --git a/src/corelib/io/qlockfile_p.h b/src/corelib/io/qlockfile_p.h index 2a7ebe1926d..ea9b29e9f57 100644 --- a/src/corelib/io/qlockfile_p.h +++ b/src/corelib/io/qlockfile_p.h @@ -25,6 +25,15 @@ QT_BEGIN_NAMESPACE class QLockFilePrivate { public: + struct LockFileInfo + { + qint64 pid; + QString appname; + QString hostname; + QByteArray hostid; + QByteArray bootid; + }; + explicit QLockFilePrivate(const QString &fn); ~QLockFilePrivate(); @@ -41,6 +50,9 @@ public: QString fileName; + static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info); + static int openNewFileDescriptor(const QString &fileName); + #ifdef Q_OS_WIN Qt::HANDLE fileHandle; #else diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp index 87faac8b33d..34276373a1f 100644 --- a/src/corelib/io/qlockfile_unix.cpp +++ b/src/corelib/io/qlockfile_unix.cpp @@ -285,6 +285,11 @@ QString QLockFilePrivate::processNameByPid(qint64 pid) #endif } +int QLockFilePrivate::openNewFileDescriptor(const QString &fileName) +{ + return QT_OPEN(fileName.toLocal8Bit().constData(), QT_OPEN_RDONLY); +} + void QLockFile::unlock() { Q_D(QLockFile); diff --git a/src/corelib/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp index 12a668def0f..ef5d49fb20e 100644 --- a/src/corelib/io/qlockfile_win.cpp +++ b/src/corelib/io/qlockfile_win.cpp @@ -16,6 +16,8 @@ #include <qt_windows.h> #include <psapi.h> +#include <io.h> +#include <fcntl.h> QT_BEGIN_NAMESPACE @@ -53,9 +55,10 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys() const QFileSystemEntry fileEntry(fileName); // When writing, allow others to read. // When reading, QFile will allow others to read and write, all good. - // Adding FILE_SHARE_DELETE would allow forceful deletion of stale files, - // but Windows doesn't allow recreating it while this handle is open anyway, - // so this would only create confusion (can't lock, but no lock file to read from). + // ### Open the file with DELETE permission and use + // SetFileInformationByHandle to delete the file without needing to close + // the handle first, to avoid someone opening the handle again without the + // FILE_SHARE_DELETE flag in-between closure and deletion. const DWORD dwShareMode = FILE_SHARE_READ; SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE }; HANDLE fh = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(), @@ -142,6 +145,30 @@ QString QLockFilePrivate::processNameByPid(qint64 pid) return name; } +int QLockFilePrivate::openNewFileDescriptor(const QString &fileName) +{ + // We currently open with FILE_SHARE_DELETE, which would allow deletion to + // be requested even while other processes have the file open. We mostly + // want to do this so we can later open the file with the DELETE permission + // to delete the file using SetFileInformationByHandle, avoiding the need + // to close the handle first, where e.g. search indexer or antivirus may + // see their chance to open the file before we can delete it. + // We can't make this change immediately because currently-deployed + // applications will not be using FILE_SHARE_DELETE, so they would suddenly + // be unable to read the lockfile information. + HANDLE handle = CreateFile(reinterpret_cast<const wchar_t *>(fileName.utf16()), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (handle == INVALID_HANDLE_VALUE) + return -1; + int fd = _open_osfhandle(intptr_t(handle), _O_RDONLY); + if (fd == -1) { + CloseHandle(handle); + return -1; + } + return fd; +} + void QLockFile::unlock() { Q_D(QLockFile); diff --git a/src/corelib/io/qrandomaccessasyncfile_p_p.h b/src/corelib/io/qrandomaccessasyncfile_p_p.h index 7b10edc6728..ef996c37f07 100644 --- a/src/corelib/io/qrandomaccessasyncfile_p_p.h +++ b/src/corelib/io/qrandomaccessasyncfile_p_p.h @@ -39,7 +39,7 @@ class QRandomAccessAsyncFilePrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QRandomAccessAsyncFile) Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFilePrivate) public: - QRandomAccessAsyncFilePrivate(decltype(QObjectPrivateVersion) version = QObjectPrivateVersion); + QRandomAccessAsyncFilePrivate(); ~QRandomAccessAsyncFilePrivate() override; static QRandomAccessAsyncFilePrivate *get(QRandomAccessAsyncFile *file) diff --git a/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp b/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp index 42d38cc3adb..4ebcf554655 100644 --- a/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp +++ b/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp @@ -64,8 +64,8 @@ static SharedThreadPool asyncFileThreadPool; } // anonymous namespace -QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate(decltype(QObjectPrivateVersion) version) : - QObjectPrivate(version) +QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() : + QObjectPrivate() { asyncFileThreadPool.ref(); } diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index a2719f97da7..6065bf2baea 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -1339,7 +1339,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, [[maybe_unused]] int flags = prop.flags; - if (isBuiltinType(prop.type)) + if (!isBuiltinType(prop.type)) flags |= EnumOrFlag; if constexpr (mode == Construct) { diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp index afc6bab8559..319ae8bc24e 100644 --- a/src/corelib/kernel/qtimer.cpp +++ b/src/corelib/kernel/qtimer.cpp @@ -15,6 +15,8 @@ #include "qproperty_p.h" #include "qthread.h" +#include <q26numeric.h> // for q26::staturate_cast + using namespace std::chrono_literals; QT_BEGIN_NAMESPACE @@ -248,19 +250,21 @@ void QTimer::start(int msec) start(msec * 1ms); } -static std::chrono::milliseconds +static int checkInterval(const char *caller, std::chrono::milliseconds interval) { - constexpr auto maxInterval = INT_MAX * 1ms; if (interval < 0ms) { qWarning("%s: negative intervals aren't allowed; the interval will be set to 1ms.", caller); - interval = 1ms; - } else if (interval > maxInterval) { + return 1; + } + + const auto msec = interval.count(); + int ret = q26::saturate_cast<int>(msec); + if (ret != msec) { qWarning("%s: interval exceeds maximum allowed interval, it will be clamped to " "INT_MAX ms (about 24 days).", caller); - interval = maxInterval; } - return interval; + return ret; } /*! @@ -288,8 +292,7 @@ void QTimer::start(std::chrono::milliseconds interval) { Q_D(QTimer); - interval = checkInterval("QTimer::start", interval); - const int msec = interval.count(); + const int msec = checkInterval("QTimer::start", interval); const bool intervalChanged = msec != d->inter; d->inter.setValue(msec); start(); @@ -656,8 +659,7 @@ void QTimer::setInterval(std::chrono::milliseconds interval) { Q_D(QTimer); - interval = checkInterval("QTimer::setInterval", interval); - const int msec = interval.count(); + const int msec = checkInterval("QTimer::setInterval", interval); d->inter.removeBindingUnlessInWrapper(); const bool intervalChanged = msec != d->inter.valueBypassingBindings(); d->inter.setValueBypassingBindings(msec); @@ -705,7 +707,10 @@ int QTimer::remainingTime() const if (d->isActive()) { using namespace std::chrono; auto remaining = QAbstractEventDispatcher::instance()->remainingTime(d->id); - return ceil<milliseconds>(remaining).count(); + const auto msec = ceil<milliseconds>(remaining).count(); + const int ret = q26::saturate_cast<int>(msec); + Q_ASSERT(ret == msec); // cannot overflow because the interval is clamped before it's set + return ret; } return -1; diff --git a/src/corelib/kernel/qwinregistry.cpp b/src/corelib/kernel/qwinregistry.cpp index fb315cacb7e..37bf3f99ae1 100644 --- a/src/corelib/kernel/qwinregistry.cpp +++ b/src/corelib/kernel/qwinregistry.cpp @@ -191,7 +191,9 @@ QVariant QWinRegistryKey::value(const QString &subKey) const // Otherwise, the resulting string (which may be empty) is returned. QString QWinRegistryKey::stringValue(const wchar_t *subKey) const { - return value<QString>(subKey).value_or(QString()); + if (auto v = value<QString>(subKey)) + return std::move(*v); + return QString(); } QString QWinRegistryKey::stringValue(const QString &subKey) const diff --git a/src/corelib/serialization/.gitignore b/src/corelib/serialization/.gitignore index 89f9ac04aac..8261c031991 100644 --- a/src/corelib/serialization/.gitignore +++ b/src/corelib/serialization/.gitignore @@ -1 +1,2 @@ +# Qt-Security score:insignificant reason:gitignore out/ diff --git a/src/corelib/serialization/make-xml-parser.sh b/src/corelib/serialization/make-xml-parser.sh index 18898337003..4174949154c 100755 --- a/src/corelib/serialization/make-xml-parser.sh +++ b/src/corelib/serialization/make-xml-parser.sh @@ -1,6 +1,7 @@ #!/bin/sh # Copyright (C) 2016 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# Qt-Security score:insignificant reason:build-tool-containing-no-compiled-source me=$(dirname $0) mkdir -p $me/out diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index f905b97a164..13d74e591d5 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -1102,20 +1102,20 @@ static auto nextUtf32Character(const char16_t *&ptr, const char16_t *end) noexce Q_ASSERT(ptr != end); struct R { char32_t c; - qsizetype len = 1; // in UTF-8 code units (bytes) - } r = { *ptr++ }; - - if (r.c < 0x0800) { - if (r.c >= 0x0080) - ++r.len; - } else if (!QChar::isHighSurrogate(r.c) || ptr == end) { - r.len += 2; + qsizetype len; // in UTF-8 code units (bytes) + }; + + const char16_t c = *ptr++; + + if (c < 0x0800) { + if (c < 0x0080) + return R{c, 1}; + return R{c, 2}; + } else if (!QChar::isHighSurrogate(c) || ptr == end) { + return R{c, 3}; } else { - r.len += 3; - r.c = QChar::surrogateToUcs4(r.c, *ptr++); + return R{QChar::surrogateToUcs4(c, *ptr++), 4}; } - - return r; } static qsizetype stringLengthInUtf8(const char16_t *ptr, const char16_t *end) noexcept diff --git a/src/corelib/serialization/qjsonparseerror.h b/src/corelib/serialization/qjsonparseerror.h index 803b04c53b6..d8fc94448e6 100644 --- a/src/corelib/serialization/qjsonparseerror.h +++ b/src/corelib/serialization/qjsonparseerror.h @@ -7,6 +7,7 @@ #include <QtCore/qtconfigmacros.h> #include <QtCore/qtcoreexports.h> +#include <QtCore/qtypes.h> QT_BEGIN_NAMESPACE @@ -34,7 +35,8 @@ struct Q_CORE_EXPORT QJsonParseError QString errorString() const; - int offset = -1; + std::conditional_t<QT_VERSION_MAJOR < 7, int, qint64> + offset = -1; ParseError error = NoError; }; diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp index df266a76c79..779287adb1d 100644 --- a/src/corelib/serialization/qjsonparser.cpp +++ b/src/corelib/serialization/qjsonparser.cpp @@ -321,7 +321,9 @@ QCborValue Parser::parse(QJsonParseError *error) error: container.reset(); if (error) { - error->offset = json - head; + using OffType = decltype(error->offset); + error->offset = OffType(json - head); + Q_ASSERT(error->offset == json - head); error->error = lastError; } return QCborValue(); diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index 49d9e24d036..9fb832545f8 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -321,7 +321,7 @@ public: { if constexpr (std::is_same_v<InputIterator, iterator> || std::is_same_v<InputIterator, const_iterator>) return assign(QByteArrayView(first, last)); - d.assign(first, last); + d->assign(first, last); if (d.data()) d.data()[d.size] = '\0'; return *this; @@ -512,10 +512,8 @@ public: } constexpr qsizetype size() const noexcept { -#if __has_cpp_attribute(assume) constexpr size_t MaxSize = maxSize(); - [[assume(size_t(d.size) <= MaxSize)]]; -#endif + Q_PRESUME(size_t(d.size) <= MaxSize); return d.size; } #if QT_DEPRECATED_SINCE(6, 4) diff --git a/src/corelib/text/qchar.cpp b/src/corelib/text/qchar.cpp index 684c9fbe23d..6be19473b85 100644 --- a/src/corelib/text/qchar.cpp +++ b/src/corelib/text/qchar.cpp @@ -1678,20 +1678,13 @@ char32_t QChar::toTitleCase(char32_t ucs4) noexcept return convertCase_helper(ucs4, QUnicodeTables::TitleCase); } -static inline char32_t foldCase(const char16_t *ch, const char16_t *start) +static inline char32_t foldCase(const char16_t *cur, const char16_t *start) { - char32_t ucs4 = *ch; - if (QChar::isLowSurrogate(ucs4) && ch > start && QChar::isHighSurrogate(*(ch - 1))) - ucs4 = QChar::surrogateToUcs4(*(ch - 1), ucs4); - return convertCase_helper(ucs4, QUnicodeTables::CaseFold); -} - -static inline char32_t foldCase(char32_t ch, char32_t &last) noexcept -{ - char32_t ucs4 = ch; - if (QChar::isLowSurrogate(ucs4) && QChar::isHighSurrogate(last)) - ucs4 = QChar::surrogateToUcs4(last, ucs4); - last = ch; + char32_t ucs4; + if (QChar::isLowSurrogate(*cur) && cur > start && QChar::isHighSurrogate(cur[-1])) + ucs4 = QChar::surrogateToUcs4(cur[-1], *cur); + else + ucs4 = *cur; return convertCase_helper(ucs4, QUnicodeTables::CaseFold); } @@ -1839,13 +1832,18 @@ static void decomposeHelper(QString *str, bool canonical, QChar::UnicodeVersion const unsigned short *utf16 = reinterpret_cast<unsigned short *>(s.data()); const unsigned short *uc = utf16 + s.size(); while (uc != utf16 + from) { - char32_t ucs4 = *(--uc); - if (QChar(ucs4).isLowSurrogate() && uc != utf16) { + const char16_t c = *(--uc); + char32_t ucs4; + if (QChar::isLowSurrogate(c) && uc != utf16) { ushort high = *(uc - 1); if (QChar(high).isHighSurrogate()) { --uc; - ucs4 = QChar::surrogateToUcs4(high, ucs4); + ucs4 = QChar::surrogateToUcs4(high, c); + } else { + ucs4 = c; // keep lone surrogate } + } else { + ucs4 = c; } if (QChar::unicodeVersion(ucs4) > version) @@ -1943,13 +1941,18 @@ static void composeHelper(QString *str, QChar::UnicodeVersion version, qsizetype qsizetype pos = from; while (pos < s.size()) { qsizetype i = pos; - char32_t uc = s.at(pos).unicode(); - if (QChar(uc).isHighSurrogate() && pos < s.size()-1) { + char32_t uc; + const char16_t c = s.at(pos).unicode(); + if (QChar::isHighSurrogate(c) && pos < s.size() - 1) { ushort low = s.at(pos+1).unicode(); if (QChar(low).isLowSurrogate()) { - uc = QChar::surrogateToUcs4(uc, low); + uc = QChar::surrogateToUcs4(c, low); ++pos; + } else { + uc = c; // keep lone surrogate } + } else { + uc = c; } const QUnicodeTables::Properties *p = qGetProp(uc); @@ -1993,35 +1996,40 @@ static void canonicalOrderHelper(QString *str, QChar::UnicodeVersion version, qs QString &s = *str; const qsizetype l = s.size()-1; - char32_t u1, u2; - char16_t c1, c2; - qsizetype pos = from; while (pos < l) { qsizetype p2 = pos+1; - u1 = s.at(pos).unicode(); - if (QChar::isHighSurrogate(u1)) { + char32_t u1; + if (const char16_t hi = s.at(pos).unicode(); QChar::isHighSurrogate(hi)) { const char16_t low = s.at(p2).unicode(); if (QChar::isLowSurrogate(low)) { - u1 = QChar::surrogateToUcs4(u1, low); + u1 = QChar::surrogateToUcs4(hi, low); if (p2 >= l) break; ++p2; + } else { + u1 = hi; } + } else { + u1 = hi; } - c1 = 0; + ushort c1 = 0; advance: - u2 = s.at(p2).unicode(); - if (QChar::isHighSurrogate(u2) && p2 < l) { + char32_t u2; + if (const char16_t hi = s.at(p2).unicode(); QChar::isHighSurrogate(hi) && p2 < l) { const char16_t low = s.at(p2+1).unicode(); if (QChar::isLowSurrogate(low)) { - u2 = QChar::surrogateToUcs4(u2, low); + u2 = QChar::surrogateToUcs4(hi, low); ++p2; + } else { + u2 = hi; } + } else { + u2 = hi; } - c2 = 0; + ushort c2 = 0; { const QUnicodeTables::Properties *p = qGetProp(u2); if (p->unicodeVersion <= version) @@ -2089,7 +2097,7 @@ static bool normalizationQuickCheckHelper(QString *str, QString::NormalizationFo uchar lastCombining = 0; for (qsizetype i = from; i < length; ++i) { qsizetype pos = i; - char32_t uc = string[i]; + const char16_t uc = string[i]; if (uc < 0x80) { // ASCII characters are stable code points lastCombining = 0; @@ -2097,6 +2105,7 @@ static bool normalizationQuickCheckHelper(QString *str, QString::NormalizationFo continue; } + char32_t ucs4; if (QChar::isHighSurrogate(uc)) { ushort low = string[i + 1]; if (!QChar::isLowSurrogate(low)) { @@ -2106,10 +2115,12 @@ static bool normalizationQuickCheckHelper(QString *str, QString::NormalizationFo continue; } ++i; - uc = QChar::surrogateToUcs4(uc, low); + ucs4 = QChar::surrogateToUcs4(uc, low); + } else { + ucs4 = uc; } - const QUnicodeTables::Properties *p = qGetProp(uc); + const QUnicodeTables::Properties *p = qGetProp(ucs4); if (p->combiningClass < lastCombining && p->combiningClass > 0) return false; diff --git a/src/corelib/text/qchar.h b/src/corelib/text/qchar.h index 008282232fb..4a3aad0ca0c 100644 --- a/src/corelib/text/qchar.h +++ b/src/corelib/text/qchar.h @@ -499,26 +499,26 @@ public: Unicode_16_0, }; - inline Category category() const noexcept { return QChar::category(ucs); } - inline Direction direction() const noexcept { return QChar::direction(ucs); } - inline JoiningType joiningType() const noexcept { return QChar::joiningType(ucs); } - inline unsigned char combiningClass() const noexcept { return QChar::combiningClass(ucs); } + Category category() const noexcept { return QChar::category(char32_t(ucs)); } + Direction direction() const noexcept { return QChar::direction(char32_t(ucs)); } + JoiningType joiningType() const noexcept { return QChar::joiningType(char32_t(ucs)); } + unsigned char combiningClass() const noexcept { return QChar::combiningClass(char32_t(ucs)); } - inline QChar mirroredChar() const noexcept { return QChar(QChar::mirroredChar(ucs)); } - inline bool hasMirrored() const noexcept { return QChar::hasMirrored(ucs); } + QChar mirroredChar() const noexcept { return QChar(QChar::mirroredChar(char32_t(ucs))); } + bool hasMirrored() const noexcept { return QChar::hasMirrored(char32_t(ucs)); } QString decomposition() const; - inline Decomposition decompositionTag() const noexcept { return QChar::decompositionTag(ucs); } + Decomposition decompositionTag() const noexcept { return QChar::decompositionTag(char32_t(ucs)); } - inline int digitValue() const noexcept { return QChar::digitValue(ucs); } - inline QChar toLower() const noexcept { return QChar(QChar::toLower(ucs)); } - inline QChar toUpper() const noexcept { return QChar(QChar::toUpper(ucs)); } - inline QChar toTitleCase() const noexcept { return QChar(QChar::toTitleCase(ucs)); } - inline QChar toCaseFolded() const noexcept { return QChar(QChar::toCaseFolded(ucs)); } + int digitValue() const noexcept { return QChar::digitValue(char32_t(ucs)); } + QChar toLower() const noexcept { return QChar(QChar::toLower(char32_t(ucs))); } + QChar toUpper() const noexcept { return QChar(QChar::toUpper(char32_t(ucs))); } + QChar toTitleCase() const noexcept { return QChar(QChar::toTitleCase(char32_t(ucs))); } + QChar toCaseFolded() const noexcept { return QChar(QChar::toCaseFolded(char32_t(ucs))); } - inline Script script() const noexcept { return QChar::script(ucs); } + Script script() const noexcept { return QChar::script(char32_t(ucs)); } - inline UnicodeVersion unicodeVersion() const noexcept { return QChar::unicodeVersion(ucs); } + UnicodeVersion unicodeVersion() const noexcept { return QChar::unicodeVersion(char32_t(ucs)); } constexpr inline char toLatin1() const noexcept { return ucs > 0xff ? '\0' : char(ucs); } constexpr inline char16_t unicode() const noexcept { return ucs; } @@ -528,23 +528,23 @@ public: constexpr inline bool isNull() const noexcept { return ucs == 0; } - inline bool isPrint() const noexcept { return QChar::isPrint(ucs); } - constexpr inline bool isSpace() const noexcept { return QChar::isSpace(ucs); } - inline bool isMark() const noexcept { return QChar::isMark(ucs); } - inline bool isPunct() const noexcept { return QChar::isPunct(ucs); } - inline bool isSymbol() const noexcept { return QChar::isSymbol(ucs); } - constexpr inline bool isLetter() const noexcept { return QChar::isLetter(ucs); } - constexpr inline bool isNumber() const noexcept { return QChar::isNumber(ucs); } - constexpr inline bool isLetterOrNumber() const noexcept { return QChar::isLetterOrNumber(ucs); } - constexpr inline bool isDigit() const noexcept { return QChar::isDigit(ucs); } - constexpr inline bool isLower() const noexcept { return QChar::isLower(ucs); } - constexpr inline bool isUpper() const noexcept { return QChar::isUpper(ucs); } - constexpr inline bool isTitleCase() const noexcept { return QChar::isTitleCase(ucs); } - - constexpr inline bool isNonCharacter() const noexcept { return QChar::isNonCharacter(ucs); } - constexpr inline bool isHighSurrogate() const noexcept { return QChar::isHighSurrogate(ucs); } - constexpr inline bool isLowSurrogate() const noexcept { return QChar::isLowSurrogate(ucs); } - constexpr inline bool isSurrogate() const noexcept { return QChar::isSurrogate(ucs); } + bool isPrint() const noexcept { return QChar::isPrint(char32_t(ucs)); } + constexpr bool isSpace() const noexcept { return QChar::isSpace(char32_t(ucs)); } + bool isMark() const noexcept { return QChar::isMark(char32_t(ucs)); } + bool isPunct() const noexcept { return QChar::isPunct(char32_t(ucs)); } + bool isSymbol() const noexcept { return QChar::isSymbol(char32_t(ucs)); } + constexpr bool isLetter() const noexcept { return QChar::isLetter(char32_t(ucs)); } + constexpr bool isNumber() const noexcept { return QChar::isNumber(char32_t(ucs)); } + constexpr bool isLetterOrNumber() const noexcept { return QChar::isLetterOrNumber(char32_t(ucs)); } + constexpr bool isDigit() const noexcept { return QChar::isDigit(char32_t(ucs)); } + constexpr bool isLower() const noexcept { return QChar::isLower(char32_t(ucs)); } + constexpr bool isUpper() const noexcept { return QChar::isUpper(char32_t(ucs)); } + constexpr bool isTitleCase() const noexcept { return QChar::isTitleCase(char32_t(ucs)); } + + constexpr bool isNonCharacter() const noexcept { return QChar::isNonCharacter(char32_t(ucs)); } + constexpr bool isHighSurrogate() const noexcept { return QChar::isHighSurrogate(char32_t(ucs)); } + constexpr bool isLowSurrogate() const noexcept { return QChar::isLowSurrogate(char32_t(ucs)); } + constexpr bool isSurrogate() const noexcept { return QChar::isSurrogate(char32_t(ucs)); } constexpr inline uchar cell() const noexcept { return uchar(ucs & 0xff); } constexpr inline uchar row() const noexcept { return uchar((ucs>>8)&0xff); } diff --git a/src/corelib/text/qcollator.cpp b/src/corelib/text/qcollator.cpp index 9ead847843b..6609d17adf4 100644 --- a/src/corelib/text/qcollator.cpp +++ b/src/corelib/text/qcollator.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2013 Aleix Pol Gonzalez <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qcollator_p.h" #include "qstringlist.h" diff --git a/src/corelib/text/qcollator.h b/src/corelib/text/qcollator.h index 870811fc48e..2b1e3963b0d 100644 --- a/src/corelib/text/qcollator.h +++ b/src/corelib/text/qcollator.h @@ -1,6 +1,7 @@ // Copyright (C) 2020 The Qt Company Ltd. // Copyright (C) 2013 Aleix Pol Gonzalez <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:trivial-impl-only #ifndef QCOLLATOR_H #define QCOLLATOR_H diff --git a/src/corelib/text/qcollator_icu.cpp b/src/corelib/text/qcollator_icu.cpp index 84f9c515374..e13e96285ef 100644 --- a/src/corelib/text/qcollator_icu.cpp +++ b/src/corelib/text/qcollator_icu.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2020 The Qt Company Ltd. // Copyright (C) 2013 Aleix Pol Gonzalez <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qcollator_p.h" #include "qlocale_p.h" diff --git a/src/corelib/text/qcollator_macx.cpp b/src/corelib/text/qcollator_macx.cpp index 23c23bd53a2..c0561877dd1 100644 --- a/src/corelib/text/qcollator_macx.cpp +++ b/src/corelib/text/qcollator_macx.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2020 Aleix Pol Gonzalez <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qcollator_p.h" #include "qlocale_p.h" diff --git a/src/corelib/text/qcollator_p.h b/src/corelib/text/qcollator_p.h index b96cdbaa32a..400cafc0c8a 100644 --- a/src/corelib/text/qcollator_p.h +++ b/src/corelib/text/qcollator_p.h @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2013 Aleix Pol Gonzalez <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:trivial-impl-only #ifndef QCOLLATOR_P_H #define QCOLLATOR_P_H diff --git a/src/corelib/text/qcollator_posix.cpp b/src/corelib/text/qcollator_posix.cpp index 5ed80c1b8ea..2712133521c 100644 --- a/src/corelib/text/qcollator_posix.cpp +++ b/src/corelib/text/qcollator_posix.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2013 Aleix Pol Gonzalez <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qcollator_p.h" #include "qstringlist.h" diff --git a/src/corelib/text/qcollator_win.cpp b/src/corelib/text/qcollator_win.cpp index b588f5ff46a..54228b79b31 100644 --- a/src/corelib/text/qcollator_win.cpp +++ b/src/corelib/text/qcollator_win.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2020 Aleix Pol Gonzalez <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #include "qcollator_p.h" #include "qlocale_p.h" diff --git a/src/corelib/text/qlocale.qdoc b/src/corelib/text/qlocale.qdoc index 3980e9d9a6d..bc88b27477d 100644 --- a/src/corelib/text/qlocale.qdoc +++ b/src/corelib/text/qlocale.qdoc @@ -1,5 +1,6 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only +// Qt-Security score:insignificant reason:docs /*! \class QLocale diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index eea66810b0b..46c01bf232a 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -1197,15 +1197,13 @@ Q_NEVER_INLINE static int ucstricmp(qsizetype alen, const char16_t *a, qsizetype if (a == b) return qt_lencmp(alen, blen); - char32_t alast = 0; - char32_t blast = 0; qsizetype l = qMin(alen, blen); qsizetype i; for (i = 0; i < l; ++i) { // qDebug() << Qt::hex << alast << blast; // qDebug() << Qt::hex << "*a=" << *a << "alast=" << alast << "folded=" << foldCase (*a, alast); // qDebug() << Qt::hex << "*b=" << *b << "blast=" << blast << "folded=" << foldCase (*b, blast); - int diff = foldCase(a[i], alast) - foldCase(b[i], blast); + int diff = foldCase(a + i, a) - foldCase(b + i, b); if ((diff)) return diff; } diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index 868a5d5ef03..506d669d356 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -273,10 +273,8 @@ public: } constexpr qsizetype size() const noexcept { -#if __has_cpp_attribute(assume) constexpr size_t MaxSize = maxSize(); - [[assume(size_t(d.size) <= MaxSize)]]; -#endif + Q_PRESUME(size_t(d.size) <= MaxSize); return d.size; } #if QT_DEPRECATED_SINCE(6, 4) @@ -642,7 +640,7 @@ public: d.data()[d.size] = u'\0'; return *this; } else { - d.assign(first, last, [](QChar ch) -> char16_t { return ch.unicode(); }); + d->assign(first, last, [](QChar ch) -> char16_t { return ch.unicode(); }); if (d.constAllocatedCapacity()) d.data()[d.size] = u'\0'; return *this; diff --git a/src/corelib/text/qtliterals.qdoc b/src/corelib/text/qtliterals.qdoc index c4671415ee4..8be03a02236 100644 --- a/src/corelib/text/qtliterals.qdoc +++ b/src/corelib/text/qtliterals.qdoc @@ -1,5 +1,6 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only +// Qt-Security score:insignificant reason:docs /*! \namespace QtLiterals @@ -43,4 +44,8 @@ // in the Qt namespace using namespace Qt; \endcode + + The latter is discouraged, because it doesn't allow you to pick which literal + operators you want in case Qt adds conflicting operators in different + namespaces within Qt::Literals. */ diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp index 0de30d3b9f9..a308c4419e2 100644 --- a/src/corelib/thread/qsemaphore.cpp +++ b/src/corelib/thread/qsemaphore.cpp @@ -12,6 +12,9 @@ #include "qwaitcondition_p.h" #include <chrono> +#if !QT_CONFIG(thread) +#include <limits> +#endif QT_BEGIN_NAMESPACE @@ -684,7 +687,7 @@ bool QSemaphore::tryAcquire(int n, QDeadlineTimer timer) // the calling thread (which is the only thread in the no-thread // configuraton) -QSemaphore::QSemaphore(int n) +QSemaphore::QSemaphore(int) { } @@ -704,6 +707,21 @@ void QSemaphore::release(int) } +int QSemaphore::available() const +{ + return std::numeric_limits<int>::max(); +} + +bool QSemaphore::tryAcquire(int) +{ + return true; +} + +bool QSemaphore::tryAcquire(int, QDeadlineTimer) +{ + return true; +} + #endif QT_END_NAMESPACE diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h index 611c8e4b5e7..2714c67b093 100644 --- a/src/corelib/time/qtimezoneprivate_p.h +++ b/src/corelib/time/qtimezoneprivate_p.h @@ -162,7 +162,7 @@ public: QByteArray ianaId; qsizetype nameLength = 0; QTimeZone::TimeType timeType = QTimeZone::GenericTime; - operator bool() { return nameLength > 0; } + operator bool() const { return nameLength > 0; } }; static NamePrefixMatch findLongNamePrefix(QStringView text, const QLocale &locale, std::optional<qint64> atEpochMillis = std::nullopt); diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index c20abd12c23..c6259354de9 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -9,12 +9,12 @@ #include <QtCore/qcontainertools_impl.h> #include <QtCore/qnamespace.h> -#include <memory> +#include <QtCore/q20functional.h> +#include <QtCore/q20memory.h> #include <new> #include <string.h> #include <utility> #include <iterator> -#include <tuple> #include <type_traits> QT_BEGIN_NAMESPACE @@ -844,7 +844,6 @@ protected: public: // using Base::truncate; // using Base::destroyAll; - // using Base::assign; template<typename It> void appendIteratorRange(It b, It e, QtPrivate::IfIsForwardIterator<It> = true) @@ -910,6 +909,119 @@ public: std::uninitialized_default_construct(b, e); this->size = newSize; } + + using Base::assign; + + template <typename InputIterator, typename Projection = q20::identity> + void assign(InputIterator first, InputIterator last, Projection proj = {}) + { + // This function only provides the basic exception guarantee. + constexpr bool IsFwdIt = std::is_convertible_v< + typename std::iterator_traits<InputIterator>::iterator_category, + std::forward_iterator_tag>; + constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>; + + const qsizetype n = IsFwdIt ? std::distance(first, last) : 0; + bool undoPrependOptimization = true; + bool needCapacity = n > this->constAllocatedCapacity(); + if (needCapacity || this->needsDetach()) { + bool wasLastRef = !this->deref(); + qsizetype newCapacity = this->detachCapacity(n); + if (wasLastRef && needCapacity) { + // free memory we can't reuse + this->destroyAll(); + Data::deallocate(this->d); + } + if (!needCapacity && wasLastRef) { + // we were the last reference and can reuse the storage + this->d->ref_.storeRelaxed(1); + } else { + // we must allocate new memory + std::tie(this->d, this->ptr) = Data::allocate(newCapacity); + this->size = 0; + undoPrependOptimization = false; + } + } + + if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) { + // If construction can throw, and we have freeSpaceAtBegin(), + // it's easiest to just clear the container and start fresh. + // The alternative would be to keep track of two active, disjoint ranges. + if (undoPrependOptimization) { + this->truncate(0); + this->setBegin(Data::dataStart(this->d, alignof(typename Data::AlignmentDummy))); + undoPrependOptimization = false; + } + } + + const auto dend = this->end(); + T *dst = this->begin(); + T *capacityBegin = dst; + qsizetype offset = 0; + if (undoPrependOptimization) { + capacityBegin = Data::dataStart(this->d, alignof(typename Data::AlignmentDummy)); + offset = dst - capacityBegin; + } + if constexpr (!QTypeInfo<T>::isComplex) { + this->setBegin(capacityBegin); // undo prepend optimization + dst = capacityBegin; + + // there's nothing to destroy or overwrite + } else if (offset) { // avoids dead stores + T *prependBufferEnd = dst; + this->setBegin(capacityBegin); // undo prepend optimization + dst = capacityBegin; + + // By construction, the following loop is nothrow! + // (otherwise, we can't reach here) + // Assumes InputIterator operations don't throw. + // (but we can't statically assert that, as these operations + // have preconditons, so typically aren't noexcept) + while (true) { + if (dst == prependBufferEnd) { // ran out of prepend buffer space + this->size += offset; + // we now have a contiguous buffer, continue with the main loop: + break; + } + if (first == last) { // ran out of elements to assign + std::destroy(prependBufferEnd, dend); + this->size = dst - this->begin(); + return; + } + // construct element in prepend buffer + q20::construct_at(dst, std::invoke(proj, *first)); + ++dst; + ++first; + } + } + + while (true) { + if (first == last) { // ran out of elements to assign + std::destroy(dst, dend); + break; + } + if (dst == dend) { // ran out of existing elements to overwrite + if constexpr (IsFwdIt && IsIdentity) { + dst = std::uninitialized_copy(first, last, dst); + break; + } else if constexpr (IsFwdIt && !IsIdentity + && std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) { + for (; first != last; ++dst, ++first) // uninitialized_copy with projection + q20::construct_at(dst, std::invoke(proj, *first)); + break; + } else { + do { + this->emplace(this->size, std::invoke(proj, *first)); + } while (++first != last); + return; // size() is already correct (and dst invalidated)! + } + } + *dst = std::invoke(proj, *first); // overwrite existing element + ++dst; + ++first; + } + this->size = dst - this->begin(); + } }; } // namespace QtPrivate diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 7fa6f2e7dd9..52984e40f31 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -7,9 +7,6 @@ #include <QtCore/qarraydataops.h> #include <QtCore/qcontainertools_impl.h> -#include <QtCore/q20functional.h> -#include <QtCore/q20memory.h> - QT_BEGIN_NAMESPACE template <class T> @@ -320,98 +317,6 @@ public: this->ptr = res; } - template <typename InputIterator, typename Projection = q20::identity> - void assign(InputIterator first, InputIterator last, Projection proj = {}) - { - // This function only provides the basic exception guarantee. - constexpr bool IsFwdIt = std::is_convertible_v< - typename std::iterator_traits<InputIterator>::iterator_category, - std::forward_iterator_tag>; - constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>; - - if constexpr (IsFwdIt) { - const qsizetype n = std::distance(first, last); - if (needsDetach() || n > constAllocatedCapacity()) { - QArrayDataPointer allocated(detachCapacity(n)); - swap(allocated); - } - } else if (needsDetach()) { - QArrayDataPointer allocated(allocatedCapacity()); - swap(allocated); - // We don't want to copy data that we know we'll overwrite - } - - auto offset = freeSpaceAtBegin(); - const auto capacityBegin = begin() - offset; - const auto prependBufferEnd = begin(); - - if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) { - // If construction can throw, and we have freeSpaceAtBegin(), - // it's easiest to just clear the container and start fresh. - // The alternative would be to keep track of two active, disjoint ranges. - if (offset) { - (*this)->truncate(0); - setBegin(capacityBegin); - offset = 0; - } - } - - auto dst = capacityBegin; - const auto dend = end(); - if (offset) { // avoids dead stores - setBegin(capacityBegin); // undo prepend optimization - - // By construction, the following loop is nothrow! - // (otherwise, we can't reach here) - // Assumes InputIterator operations don't throw. - // (but we can't statically assert that, as these operations - // have preconditons, so typically aren't noexcept) - while (true) { - if (dst == prependBufferEnd) { // ran out of prepend buffer space - size += offset; - // we now have a contiguous buffer, continue with the main loop: - break; - } - if (first == last) { // ran out of elements to assign - std::destroy(prependBufferEnd, dend); - size = dst - begin(); - return; - } - // construct element in prepend buffer - q20::construct_at(dst, std::invoke(proj, *first)); - ++dst; - ++first; - } - } - - while (true) { - if (first == last) { // ran out of elements to assign - std::destroy(dst, dend); - break; - } - if (dst == dend) { // ran out of existing elements to overwrite - if constexpr (IsFwdIt && IsIdentity) { - dst = std::uninitialized_copy(first, last, dst); - break; - } else if constexpr (IsFwdIt && !IsIdentity - && std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) { - for (; first != last; ++dst, ++first) // uninitialized_copy with projection - q20::construct_at(dst, std::invoke(proj, *first)); - break; - } else { - do { - (*this)->emplace(size, std::invoke(proj, *first)); - } while (++first != last); - return; // size() is already correct (and dst invalidated)! - } - } - *dst = std::invoke(proj, *first); // overwrite existing element - ++dst; - ++first; - } - size = dst - begin(); - } - QArrayDataPointer sliced(qsizetype pos, qsizetype n) const & { QArrayDataPointer result(n); diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index fea5bdfa906..092ff46b084 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -235,6 +235,8 @@ public: // when not called from the static hash() function, this function needs to be // called with finalizeMutex held (finalize() will do that): void finalizeUnchecked() noexcept; + QSpan<uchar> finalizeUnchecked(QSpan<uchar> buffer) noexcept; + // END functions that need to be called with finalizeMutex held QByteArrayView resultView() const noexcept { return result.toByteArrayView(); } static bool supportsAlgorithm(QCryptographicHash::Algorithm method); @@ -268,7 +270,7 @@ public: explicit EVP(QCryptographicHash::Algorithm method); void reset() noexcept; - void finalizeUnchecked(HashResult &result) noexcept; + void finalizeUnchecked(QSpan<uchar> buffer) noexcept; }; #endif @@ -281,7 +283,7 @@ public: void reset(QCryptographicHash::Algorithm method) noexcept; void addData(QCryptographicHash::Algorithm method, QByteArrayView data) noexcept; - void finalizeUnchecked(QCryptographicHash::Algorithm method, HashResult &result) noexcept; + void finalizeUnchecked(QCryptographicHash::Algorithm method, QSpan<uchar> buffer) noexcept; Sha1State sha1Context; #ifdef USING_OPENSSL30 @@ -297,7 +299,7 @@ public: SHA3Context sha3Context; enum class Sha3Variant { Sha3, Keccak }; - static void sha3Finish(SHA3Context &ctx, HashResult &result, Sha3Variant sha3Variant); + static void sha3Finish(SHA3Context &ctx, QSpan<uchar> result, Sha3Variant sha3Variant); blake2b_state blake2bContext; blake2s_state blake2sContext; } state; @@ -308,7 +310,7 @@ public: const QCryptographicHash::Algorithm method; }; -void QCryptographicHashPrivate::State::sha3Finish(SHA3Context &ctx, HashResult &result, +void QCryptographicHashPrivate::State::sha3Finish(SHA3Context &ctx, QSpan<uchar> result, Sha3Variant sha3Variant) { /* @@ -981,9 +983,23 @@ void QCryptographicHashPrivate::finalizeUnchecked() noexcept state.finalizeUnchecked(method, result); } +/*! + \internal + + Must be called with finalizeMutex held, except when called from the static + hash() function, where no sharing can take place. +*/ +QSpan<uchar> QCryptographicHashPrivate::finalizeUnchecked(QSpan<uchar> buffer) noexcept +{ + buffer = buffer.first(hashLengthInternal(method)); + state.finalizeUnchecked(method, buffer); + Q_ASSERT(result.size() == 0); // internal buffer wasn't used + return buffer; +} + #ifdef USING_OPENSSL30 void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method, - HashResult &result) noexcept + QSpan<uchar> result) noexcept { switch (method) { case QCryptographicHash::Keccak_224: @@ -1030,7 +1046,7 @@ void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Alg } } -void QCryptographicHashPrivate::EVP::finalizeUnchecked(HashResult &result) noexcept +void QCryptographicHashPrivate::EVP::finalizeUnchecked(QSpan<uchar> result) noexcept { if (!initializationFailed) { EVP_MD_CTX_ptr copy = EVP_MD_CTX_ptr(EVP_MD_CTX_new()); @@ -1043,7 +1059,7 @@ void QCryptographicHashPrivate::EVP::finalizeUnchecked(HashResult &result) noexc #else // USING_OPENSSL30 void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method, - HashResult &result) noexcept + QSpan<uchar> result) noexcept { switch (method) { case QCryptographicHash::Sha1: { @@ -1166,12 +1182,8 @@ QByteArrayView QCryptographicHash::hashInto(QSpan<std::byte> buffer, QCryptographicHashPrivate hash(method); for (QByteArrayView part : data) hash.addData(part); - hash.finalizeUnchecked(); // no mutex needed: no-one but us has access to 'hash' - auto result = hash.resultView(); - Q_ASSERT(buffer.size() >= result.size()); - // ### optimize: have the method directly write into `buffer` - memcpy(buffer.data(), result.data(), result.size()); - return buffer.first(result.size()); + auto span = QSpan{reinterpret_cast<uchar *>(buffer.data()), buffer.size()}; + return hash.finalizeUnchecked(span); // no mutex needed: no-one but us has access to 'hash' } /*! diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 7a93ec688ef..93f7ddb9465 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -446,10 +446,8 @@ public: static constexpr qsizetype maxSize() { return Data::maxSize(); } constexpr qsizetype size() const noexcept { -#if __has_cpp_attribute(assume) constexpr size_t MaxSize = maxSize(); - [[assume(size_t(d.size) <= MaxSize)]]; -#endif + Q_PRESUME(size_t(d.size) <= MaxSize); return d.size; } constexpr qsizetype count() const noexcept { return size(); } @@ -578,7 +576,7 @@ public: template <typename InputIterator, if_input_iterator<InputIterator> = true> QList &assign(InputIterator first, InputIterator last) - { d.assign(first, last); return *this; } + { d->assign(first, last); return *this; } QList &assign(std::initializer_list<T> l) { return assign(l.begin(), l.end()); } diff --git a/src/corelib/tools/qshareddata.h b/src/corelib/tools/qshareddata.h index 7d0cec8c899..595efd7e3bf 100644 --- a/src/corelib/tools/qshareddata.h +++ b/src/corelib/tools/qshareddata.h @@ -4,16 +4,35 @@ #ifndef QSHAREDDATA_H #define QSHAREDDATA_H -#include <QtCore/qglobal.h> #include <QtCore/qatomic.h> #include <QtCore/qcompare.h> #include <QtCore/qhashfunctions.h> - QT_BEGIN_NAMESPACE - template <class T> class QSharedDataPointer; +template <class T> class QExplicitlySharedDataPointer; + +namespace QtPrivate { +template <template <typename> class P, typename T> struct QSharedDataPointerTraits; +template <typename T> struct QSharedDataPointerTraits<QSharedDataPointer, T> +{ + static constexpr bool ImplicitlyDetaches = true; + using Type = T; + using pointer = T *; + // for const-qualified functions: + using constT = const T; +}; + +template <typename T> struct QSharedDataPointerTraits<QExplicitlySharedDataPointer, T> +{ + static constexpr bool ImplicitlyDetaches = false; + using Type = T; + using pointer = T *; + // for const-qualified functions: + using constT = T; +}; +} class QSharedData { @@ -30,41 +49,38 @@ public: struct QAdoptSharedDataTag { explicit constexpr QAdoptSharedDataTag() = default; }; -template <typename T> -class QSharedDataPointer +// CRTP common base class for both QSharedDataPointer and QExplicitlySharedDataPointer +template <template <typename> class P, typename T> class QSharedDataPointerBase { +#ifndef Q_QDOC + using Self = P<T>; + using Traits = QtPrivate::QSharedDataPointerTraits<P, T>; + using constT = typename Traits::constT; + +protected: + constexpr QSharedDataPointerBase(T *ptr = nullptr) noexcept : d(ptr) {} + public: - typedef T Type; - typedef T *pointer; + // When adding anything public to this class, make sure to add the doc version to + // both QSharedDataPointer and QExplicitlySharedDataPointer. + + using Type = T; + using pointer = T *; void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } - T &operator*() { detach(); return *(d.get()); } - const T &operator*() const { return *(d.get()); } - T *operator->() { detach(); return d.get(); } - const T *operator->() const noexcept { return d.get(); } - operator T *() { detach(); return d.get(); } + T &operator*() { implicitlyDetach(); return *(d.get()); } + constT &operator*() const { return *(d.get()); } + T *operator->() { implicitlyDetach(); return d.get(); } + constT *operator->() const noexcept { return d.get(); } + operator T *() { implicitlyDetach(); return d.get(); } operator const T *() const noexcept { return d.get(); } - T *data() { detach(); return d.get(); } - T *get() { detach(); return d.get(); } + T *data() { implicitlyDetach(); return d.get(); } + T *get() { implicitlyDetach(); return d.get(); } const T *data() const noexcept { return d.get(); } const T *get() const noexcept { return d.get(); } const T *constData() const noexcept { return d.get(); } T *take() noexcept { return std::exchange(d, nullptr).get(); } - Q_NODISCARD_CTOR - QSharedDataPointer() noexcept : d(nullptr) { } - ~QSharedDataPointer() { if (d && !d->ref.deref()) delete d.get(); } - - Q_NODISCARD_CTOR - explicit QSharedDataPointer(T *data) noexcept : d(data) - { if (d) d->ref.ref(); } - Q_NODISCARD_CTOR - QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) - {} - Q_NODISCARD_CTOR - QSharedDataPointer(const QSharedDataPointer &o) noexcept : d(o.d) - { if (d) d->ref.ref(); } - void reset(T *ptr = nullptr) noexcept { if (ptr != d.get()) { @@ -72,10 +88,97 @@ public: ptr->ref.ref(); T *old = std::exchange(d, Qt::totally_ordered_wrapper(ptr)).get(); if (old && !old->ref.deref()) - delete old; + destroy(old); } } + operator bool () const noexcept { return d != nullptr; } + bool operator!() const noexcept { return d == nullptr; } + + void swap(Self &other) noexcept + { qt_ptr_swap(d, other.d); } + +private: + // The concrete class MUST override these, otherwise we will be calling + // ourselves. + T *clone() { return static_cast<Self *>(this)->clone(); } + template <typename... Args> static T *create(Args &&... args) + { return Self::create(std::forward(args)...); } + static void destroy(T *ptr) { Self::destroy(ptr); } + + void implicitlyDetach() + { + if constexpr (Traits::ImplicitlyDetaches) + static_cast<Self *>(this)->detach(); + } + + friend bool comparesEqual(const QSharedDataPointerBase &lhs, const QSharedDataPointerBase &rhs) noexcept + { return lhs.d == rhs.d; } + friend Qt::strong_ordering + compareThreeWay(const QSharedDataPointerBase &lhs, const QSharedDataPointerBase &rhs) noexcept + { return Qt::compareThreeWay(lhs.d, rhs.d); } + + friend bool comparesEqual(const QSharedDataPointerBase &lhs, const T *rhs) noexcept + { return lhs.d == rhs; } + friend Qt::strong_ordering + compareThreeWay(const QSharedDataPointerBase &lhs, const T *rhs) noexcept + { return Qt::compareThreeWay(lhs.d, rhs); } + + friend bool comparesEqual(const QSharedDataPointerBase &lhs, std::nullptr_t) noexcept + { return lhs.d == nullptr; } + friend Qt::strong_ordering + compareThreeWay(const QSharedDataPointerBase &lhs, std::nullptr_t) noexcept + { return Qt::compareThreeWay(lhs.d, nullptr); } + + friend size_t qHash(const QSharedDataPointerBase &ptr, size_t seed = 0) noexcept + { return qHash(ptr.data(), seed); } + +protected: + void detach_helper(); + + Qt::totally_ordered_wrapper<T *> d; +#endif // !Q_QDOC +}; + +template <typename T> +class QSharedDataPointer : public QSharedDataPointerBase<QSharedDataPointer, T> +{ + using Base = QSharedDataPointerBase<QSharedDataPointer, T>; + friend Base; +public: + typedef T Type; + typedef T *pointer; + + void detach() { Base::detach(); } +#ifdef Q_QDOC + T &operator*(); + const T &operator*() const; + T *operator->(); + const T *operator->() const noexcept; + operator T *(); + operator const T *() const noexcept; + T *data(); + T *get(); + const T *data() const noexcept; + const T *get() const noexcept; + const T *constData() const noexcept; + T *take() noexcept; +#endif + + Q_NODISCARD_CTOR + QSharedDataPointer() noexcept : Base(nullptr) { } + ~QSharedDataPointer() { if (d && !d->ref.deref()) destroy(d.get()); } + + Q_NODISCARD_CTOR + explicit QSharedDataPointer(T *data) noexcept : Base(data) + { if (d) d->ref.ref(); } + Q_NODISCARD_CTOR + QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : Base(data) + {} + Q_NODISCARD_CTOR + QSharedDataPointer(const QSharedDataPointer &o) noexcept : Base(o.d.get()) + { if (d) d->ref.ref(); } + QSharedDataPointer &operator=(const QSharedDataPointer &o) noexcept { reset(o.d.get()); @@ -87,76 +190,80 @@ public: return *this; } Q_NODISCARD_CTOR - QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {} + QSharedDataPointer(QSharedDataPointer &&o) noexcept + : Base(std::exchange(o.d, nullptr).get()) + {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer) - operator bool () const noexcept { return d != nullptr; } - bool operator!() const noexcept { return d == nullptr; } +#ifdef Q_QDOC + void reset(T *ptr = nullptr) noexcept; - void swap(QSharedDataPointer &other) noexcept - { qt_ptr_swap(d, other.d); } + operator bool () const noexcept; + bool operator!() const noexcept; + + void swap(QSharedDataPointer &other) noexcept; +#else + using Base::reset; + using Base::swap; +#endif protected: T *clone(); + template <typename... Args> static T *create(Args &&... args) + { return new T(std::forward(args)...); } + static void destroy(T *ptr) { delete ptr; } private: - friend bool comparesEqual(const QSharedDataPointer &lhs, const QSharedDataPointer &rhs) noexcept - { return lhs.d == rhs.d; } - friend Qt::strong_ordering - compareThreeWay(const QSharedDataPointer &lhs, const QSharedDataPointer &rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs.d); } Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer) - - friend bool comparesEqual(const QSharedDataPointer &lhs, const T *rhs) noexcept - { return lhs.d == rhs; } - friend Qt::strong_ordering - compareThreeWay(const QSharedDataPointer &lhs, const T *rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs); } Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer, T*) - - friend bool comparesEqual(const QSharedDataPointer &lhs, std::nullptr_t) noexcept - { return lhs.d == nullptr; } - friend Qt::strong_ordering - compareThreeWay(const QSharedDataPointer &lhs, std::nullptr_t) noexcept - { return Qt::compareThreeWay(lhs.d, nullptr); } Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer, std::nullptr_t) - void detach_helper(); - - Qt::totally_ordered_wrapper<T *> d; + using Base::d; }; template <typename T> -class QExplicitlySharedDataPointer +class QExplicitlySharedDataPointer : public QSharedDataPointerBase<QExplicitlySharedDataPointer, T> { + using Base = QSharedDataPointerBase<QExplicitlySharedDataPointer, T>; + friend Base; public: typedef T Type; typedef T *pointer; - T &operator*() const { return *(d.get()); } - T *operator->() noexcept { return d.get(); } - T *operator->() const noexcept { return d.get(); } + // override to make explicit. Can use explicit(!ImplicitlyShared) once we + // can depend on C++20. explicit operator T *() { return d.get(); } explicit operator const T *() const noexcept { return d.get(); } + + // override to make const. There is no const(cond), but we could use + // requires(!ImplicitlyShared) T *data() const noexcept { return d.get(); } T *get() const noexcept { return d.get(); } - const T *constData() const noexcept { return d.get(); } - T *take() noexcept { return std::exchange(d, nullptr).get(); } - void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } +#ifdef Q_QDOC + T &operator*() const; + T *operator->() noexcept; + T *operator->() const noexcept; + T *data() const noexcept; + T *get() const noexcept; + const T *constData() const noexcept; + T *take() noexcept; +#endif + + void detach() { Base::detach(); } Q_NODISCARD_CTOR - QExplicitlySharedDataPointer() noexcept : d(nullptr) { } + QExplicitlySharedDataPointer() noexcept : Base(nullptr) { } ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d.get(); } Q_NODISCARD_CTOR - explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data) + explicit QExplicitlySharedDataPointer(T *data) noexcept : Base(data) { if (d) d->ref.ref(); } Q_NODISCARD_CTOR - QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) + QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : Base(data) {} Q_NODISCARD_CTOR - QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : d(o.d) + QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : Base(o.d.get()) { if (d) d->ref.ref(); } template<typename X> @@ -165,20 +272,9 @@ public: #ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST #error This macro has been removed in Qt 6.9. #endif - : d(o.data()) + : Base(o.data()) { if (d) d->ref.ref(); } - void reset(T *ptr = nullptr) noexcept - { - if (ptr != d) { - if (ptr) - ptr->ref.ref(); - T *old = std::exchange(d, Qt::totally_ordered_wrapper(ptr)).get(); - if (old && !old->ref.deref()) - delete old; - } - } - QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept { reset(o.d.get()); @@ -190,72 +286,52 @@ public: return *this; } Q_NODISCARD_CTOR - QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {} + QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept + : Base(std::exchange(o.d, nullptr).get()) + {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer) - operator bool () const noexcept { return d != nullptr; } - bool operator!() const noexcept { return d == nullptr; } +#ifdef Q_QDOC + void reset(T *ptr = nullptr) noexcept; - void swap(QExplicitlySharedDataPointer &other) noexcept - { qt_ptr_swap(d, other.d); } + operator bool () const noexcept; + bool operator!() const noexcept; + + void swap(QExplicitlySharedDataPointer &other) noexcept; +#else + using Base::swap; + using Base::reset; +#endif protected: T *clone(); + template <typename... Args> static T *create(Args &&... args) + { return new T(std::forward(args)...); } + static void destroy(T *ptr) { delete ptr; } private: - friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, - const QExplicitlySharedDataPointer &rhs) noexcept - { return lhs.d == rhs.d; } - friend Qt::strong_ordering - compareThreeWay(const QExplicitlySharedDataPointer &lhs, - const QExplicitlySharedDataPointer &rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs.d); } Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer) - - friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, const T *rhs) noexcept - { return lhs.d == rhs; } - friend Qt::strong_ordering - compareThreeWay(const QExplicitlySharedDataPointer &lhs, const T *rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs); } Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, const T*) - - friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, std::nullptr_t) noexcept - { return lhs.d == nullptr; } - friend Qt::strong_ordering - compareThreeWay(const QExplicitlySharedDataPointer &lhs, std::nullptr_t) noexcept - { return Qt::compareThreeWay(lhs.d, nullptr); } Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, std::nullptr_t) - void detach_helper(); - - Qt::totally_ordered_wrapper<T *> d; + using Base::d; }; // Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time. template <typename T> Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone() { - return new T(*d); -} - -template <typename T> -Q_OUTOFLINE_TEMPLATE void QSharedDataPointer<T>::detach_helper() -{ - T *x = clone(); - x->ref.ref(); - if (!d.get()->ref.deref()) - delete d.get(); - d.reset(x); + return new T(*this->d); } template <typename T> Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone() { - return new T(*d.get()); + return new T(*this->d.get()); } -template <typename T> -Q_OUTOFLINE_TEMPLATE void QExplicitlySharedDataPointer<T>::detach_helper() +template <template <typename> class P, typename T> Q_OUTOFLINE_TEMPLATE void +QSharedDataPointerBase<P, T>::detach_helper() { T *x = clone(); x->ref.ref(); @@ -272,17 +348,6 @@ template <typename T> void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) noexcept { p1.swap(p2); } -template <typename T> -size_t qHash(const QSharedDataPointer<T> &ptr, size_t seed = 0) noexcept -{ - return qHash(ptr.data(), seed); -} -template <typename T> -size_t qHash(const QExplicitlySharedDataPointer<T> &ptr, size_t seed = 0) noexcept -{ - return qHash(ptr.data(), seed); -} - template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_RELOCATABLE_TYPE); template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_RELOCATABLE_TYPE); diff --git a/src/gui/doc/src/qtgui.qdoc b/src/gui/doc/src/qtgui.qdoc index dca2f10bf26..f5d60699deb 100644 --- a/src/gui/doc/src/qtgui.qdoc +++ b/src/gui/doc/src/qtgui.qdoc @@ -17,8 +17,7 @@ /*! \module QtGuiPrivate \title Qt GUI Private C++ Classes - \qtcmakepackage Gui - \qtcmaketargetitem GuiPrivate + \qtcmakepackage GuiPrivate \qtvariable gui-private \brief Provides access to private GUI functionality. @@ -27,7 +26,7 @@ private Qt GUI APIs: \badcode - find_package(Qt6 REQUIRED COMPONENTS Gui) + find_package(Qt6 REQUIRED COMPONENTS GuiPrivate) target_link_libraries(mytarget PRIVATE Qt6::GuiPrivate) \endcode */ diff --git a/src/gui/image/qplatformpixmap.cpp b/src/gui/image/qplatformpixmap.cpp index a2977360951..d1eab7f6ed3 100644 --- a/src/gui/image/qplatformpixmap.cpp +++ b/src/gui/image/qplatformpixmap.cpp @@ -36,7 +36,6 @@ QPlatformPixmap::QPlatformPixmap(PixelType pixelType, int objectId) h(0), d(0), is_null(true), - ref(0), detach_no(0), type(pixelType), id(objectId), diff --git a/src/gui/image/qplatformpixmap.h b/src/gui/image/qplatformpixmap.h index be86bf8850f..5621afa4da5 100644 --- a/src/gui/image/qplatformpixmap.h +++ b/src/gui/image/qplatformpixmap.h @@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE class QImageReader; -class Q_GUI_EXPORT QPlatformPixmap +class Q_GUI_EXPORT QPlatformPixmap : public QSharedData { public: enum PixelType { @@ -113,10 +113,7 @@ private: friend class QPixmap; friend class QX11PlatformPixmap; friend class QImagePixmapCleanupHooks; // Needs to set is_cached - friend class QOpenGLTextureCache; //Needs to check the reference count - friend class QExplicitlySharedDataPointer<QPlatformPixmap>; - QAtomicInt ref; int detach_no; PixelType type; diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 098d0331327..e44d9b1468b 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -2626,6 +2626,7 @@ void QGuiApplicationPrivate::processEnterEvent(QWindowSystemInterfacePrivate::En } currentMouseWindow = e->enter; + lastCursorPosition = e->globalPos; // TODO later: EnterEvent must report _which_ mouse entered the window; for now we assume primaryPointingDevice() QEnterEvent event(e->localPos, e->localPos, e->globalPos); diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index c7b6e4ebff3..bb71f8fb6fc 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -1298,7 +1298,10 @@ QString QKeySequencePrivate::keyName(Qt::Key key, QKeySequence::SequenceFormat f bool nativeText = (format == QKeySequence::NativeText); QString p; - if (key && key < Qt::Key_Escape && key != Qt::Key_Space) { + if (nativeText && (key > 0x00 && key <= 0x1f)) { + // Map C0 control codes to the corresponding Control Pictures + p = QChar::fromUcs2(0x2400 + key); + } else if (key && key < Qt::Key_Escape && key != Qt::Key_Space) { if (!QChar::requiresSurrogates(key)) { p = QChar::fromUcs2(key).toUpper(); } else { diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp index 72853be6e97..d7496845197 100644 --- a/src/gui/painting/qdrawhelper_avx2.cpp +++ b/src/gui/painting/qdrawhelper_avx2.cpp @@ -1525,7 +1525,7 @@ void QT_FASTCALL storeRGBA16FFromARGB32PM_avx2(uchar *dest, const uint *src, int const __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); __m128 vsr = _mm_rcp_ps(vsa); vsr = _mm_sub_ps(_mm_add_ps(vsr, vsr), _mm_mul_ps(vsr, _mm_mul_ps(vsr, vsa))); - vsr = _mm_insert_ps(vsr, _mm_set_ss(1.0f), 0x30); + vsr = _mm_insert_ps(vsr, vf, 0x30); vsf = _mm_mul_ps(vsf, vsr); } _mm_storel_epi64((__m128i *)(d + i), _mm_cvtps_ph(vsf, 0)); diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 79799ca2ece..0d435c95048 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -1154,7 +1154,8 @@ void QDashStroker::processCurrentSubpath() elen -= std::floor(elen * invSumLength) * sumLength; // Update dash offset. while (!done) { - qreal dpos = pos + dashes[idash] - doffset - estart; + // parentheses to avoid float rounding issues: qreal(4) + 0.1 - 0.1 - 4 < 0 + qreal dpos = (pos + dashes[idash]) - (doffset + estart); Q_ASSERT(dpos >= 0); @@ -1189,7 +1190,8 @@ void QDashStroker::processCurrentSubpath() bool has_offset = doffset > 0; bool evenDash = (idash & 1) == 0; - qreal dpos = pos + dashes[idash] - doffset - estart; + // parentheses to avoid float rounding issues: qreal(4) + 0.1 - 0.1 - 4 < 0 + qreal dpos = (pos + dashes[idash]) - (doffset + estart); Q_ASSERT(dpos >= 0); diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 53a984306c6..d722bceb289 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -657,8 +657,8 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextTableCellFormat & \value FontStyleName \value FontPointSize \value FontPixelSize - \value FontSizeAdjustment Specifies the change in size given to the fontsize already set using - FontPointSize or FontPixelSize. + \value FontSizeAdjustment Specifies an integer adjustment added to the base font size set using + \c FontPointSize or \c FontPixelSize. \value FontFixedPitch \omitvalue FontSizeIncrement \value FontWeight diff --git a/src/gui/util/qundostack.cpp b/src/gui/util/qundostack.cpp index 3d1d8a2b788..27b131cd733 100644 --- a/src/gui/util/qundostack.cpp +++ b/src/gui/util/qundostack.cpp @@ -425,16 +425,16 @@ void QUndoStackPrivate::setIndex(int idx, bool clean) emit q->indexChanged(index); } - const ActionState newUndoState{q->canUndo(), q->undoText()}; - if (indexChanged || newUndoState != undoActionState) { - undoActionState = newUndoState; + if (ActionState newUndoState{q->canUndo(), q->undoText()}; + indexChanged || newUndoState != undoActionState) { + undoActionState = std::move(newUndoState); emit q->canUndoChanged(undoActionState.enabled); emit q->undoTextChanged(undoActionState.text); } - const ActionState newRedoState{q->canRedo(), q->redoText()}; - if (indexChanged || newRedoState != redoActionState) { - redoActionState = newRedoState; + if (ActionState newRedoState{q->canRedo(), q->redoText()}; + indexChanged || newRedoState != redoActionState) { + redoActionState = std::move(newRedoState); emit q->canRedoChanged(redoActionState.enabled); emit q->redoTextChanged(redoActionState.text); } diff --git a/src/gui/util/qundostack_p.h b/src/gui/util/qundostack_p.h index fea201ce62d..6bdcf5fb20b 100644 --- a/src/gui/util/qundostack_p.h +++ b/src/gui/util/qundostack_p.h @@ -59,10 +59,17 @@ public: bool enabled = false; QString text; - bool operator!=(const ActionState &other) const noexcept - { - return enabled != other.enabled || text != other.text; - } + friend bool operator==(const ActionState &lhs, const ActionState &rhs) noexcept +#ifdef __cpp_impl_three_way_comparison + = default; +#else + { return lhs.enabled == rhs.enabled && lhs.text == rhs.text; } + friend bool operator!=(const ActionState &lhs, const ActionState &rhs) noexcept + { return !(lhs == rhs); } +#endif + // some compiler's reject seed = 0) = delete, overload instead: + friend void qHash(const ActionState &key, size_t seed) = delete; + friend void qHash(const ActionState &key) = delete; }; QList<QUndoCommand*> command_list; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index bcd9aecdea9..120d2ecbc78 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -326,6 +326,11 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co */ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt, int v) { +#ifdef QNATIVESOCKETENGINE_DEBUG +# define perrorDebug(msg) perror("QNativeSocketEnginePrivate::setOption(): " msg) +#else +# define perrorDebug(msg) (void)0 +#endif Q_Q(QNativeSocketEngine); if (!q->isValid()) return false; @@ -337,25 +342,16 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt #if !defined(Q_OS_VXWORKS) int flags = ::fcntl(socketDescriptor, F_GETFL, 0); if (flags == -1) { -#ifdef QNATIVESOCKETENGINE_DEBUG - perror("QNativeSocketEnginePrivate::setOption(): fcntl(F_GETFL) failed"); -#endif + perrorDebug("fcntl(F_GETFL) failed"); return false; } if (::fcntl(socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) { -#ifdef QNATIVESOCKETENGINE_DEBUG - perror("QNativeSocketEnginePrivate::setOption(): fcntl(F_SETFL) failed"); -#endif + perrorDebug("fcntl(F_SETFL) failed"); return false; } #else // Q_OS_VXWORKS - int onoff = 1; - - if (qt_safe_ioctl(socketDescriptor, FIONBIO, &onoff) < 0) { - -#ifdef QNATIVESOCKETENGINE_DEBUG - perror("QNativeSocketEnginePrivate::setOption(): ioctl(FIONBIO, 1) failed"); -#endif + if (qt_safe_ioctl(socketDescriptor, FIONBIO, &v) < 0) { + perrorDebug("ioctl(FIONBIO, 1) failed"); return false; } #endif // Q_OS_VXWORKS @@ -417,6 +413,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt if (n == -1) return false; return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0; +#undef perrorDebug } bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 port) diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index 028ae6d682a..6154f4121d2 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/androidjniaccessibility.cpp @@ -726,9 +726,18 @@ namespace QtAndroidAccessibility break; } + float min = info.minValue.toFloat(); + float max = info.maxValue.toFloat(); + float current = info.currentValue.toFloat(); + if (info.role == QAccessible::ProgressBar) { + rangeType = 2; // RANGE_TYPE_PERCENT + current = 100 * (current - min) / (max - min); + min = 0.0f; + max = 100.0f; + } + QJniObject rangeInfo("android/view/accessibility/AccessibilityNodeInfo$RangeInfo", - "(IFFF)V", rangeType, info.minValue.toFloat(), - info.maxValue.toFloat(), info.currentValue.toFloat()); + "(IFFF)V", rangeType, min, max, current); if (rangeInfo.isValid()) { env->CallVoidMethod(node, m_setRangeInfoMethodID, rangeInfo.object()); diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index c8555cdc659..f64742ff133 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -291,7 +291,7 @@ void QAndroidPlatformScreen::topVisibleWindowChanged() if (w && w->handle()) { QAndroidPlatformWindow *platformWindow = static_cast<QAndroidPlatformWindow *>(w->handle()); if (platformWindow) { - platformWindow->updateSystemUiVisibility(); + platformWindow->updateSystemUiVisibility(w->windowStates(), w->flags()); platformWindow->updateFocusedEditText(); } } diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp index 96c4bfa06f1..c4245998772 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp @@ -56,15 +56,12 @@ void QAndroidPlatformWindow::initialize() isForeignWindow(), m_nativeParentQtWindow, listener); m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId"); - m_windowFlags = Qt::Widget; - m_windowState = Qt::WindowNoState; // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save // the fact that it's a raster window for now m_isRaster = window->surfaceType() == QSurface::RasterSurface; - setWindowState(window->windowStates()); // the following is in relation to the virtual geometry - const bool forceMaximize = m_windowState & (Qt::WindowMaximized | Qt::WindowFullScreen); + const bool forceMaximize = window->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen); const QRect nativeScreenGeometry = platformScreen()->availableGeometry(); if (forceMaximize) { setGeometry(nativeScreenGeometry); @@ -123,7 +120,7 @@ void QAndroidPlatformWindow::raise() QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason); return; } - updateSystemUiVisibility(); + updateSystemUiVisibility(window()->windowStates(), window()->flags()); platformScreen()->raise(this); } @@ -167,13 +164,13 @@ void QAndroidPlatformWindow::setVisible(bool visible) if (!visible && window() == qGuiApp->focusWindow()) { platformScreen()->topVisibleWindowChanged(); } else { - updateSystemUiVisibility(); - if ((m_windowState & Qt::WindowFullScreen) - || (window()->flags() & Qt::ExpandedClientAreaHint)) { + const Qt::WindowStates states = window()->windowStates(); + const Qt::WindowFlags flags = window()->flags(); + updateSystemUiVisibility(states, flags); + if (states & Qt::WindowFullScreen || flags & Qt::ExpandedClientAreaHint) setGeometry(platformScreen()->geometry()); - } else if (m_windowState & Qt::WindowMaximized) { + else if (states & Qt::WindowMaximized) setGeometry(platformScreen()->availableGeometry()); - } requestActivateWindow(); } } @@ -188,27 +185,18 @@ void QAndroidPlatformWindow::setVisible(bool visible) void QAndroidPlatformWindow::setWindowState(Qt::WindowStates state) { - if (m_windowState == state) - return; - QPlatformWindow::setWindowState(state); - m_windowState = state; if (window()->isVisible()) - updateSystemUiVisibility(); + updateSystemUiVisibility(state, window()->flags()); } void QAndroidPlatformWindow::setWindowFlags(Qt::WindowFlags flags) { - if (m_windowFlags == flags) - return; + QPlatformWindow::setWindowFlags(flags); - m_windowFlags = flags; -} - -Qt::WindowFlags QAndroidPlatformWindow::windowFlags() const -{ - return m_windowFlags; + if (window()->isVisible()) + updateSystemUiVisibility(window()->windowStates(), flags); } void QAndroidPlatformWindow::setParent(const QPlatformWindow *window) @@ -256,16 +244,15 @@ void QAndroidPlatformWindow::requestActivateWindow() raise(); } -void QAndroidPlatformWindow::updateSystemUiVisibility() +void QAndroidPlatformWindow::updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags) { - const int flags = window()->flags(); const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; if (!isNonRegularWindow) { auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>(); iface->runOnAndroidMainThread([=]() { using namespace QtJniTypes; auto activity = iface->context().object<Activity>(); - if (m_windowState & Qt::WindowFullScreen) + if (states & Qt::WindowFullScreen) QtWindowInsetsController::callStaticMethod("showFullScreen", activity); else if (flags & Qt::ExpandedClientAreaHint) QtWindowInsetsController::callStaticMethod("showExpanded", activity); diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h index 07f4e12b35c..826a8d30ade 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.h +++ b/src/plugins/platforms/android/qandroidplatformwindow.h @@ -43,7 +43,6 @@ public: void setWindowState(Qt::WindowStates state) override; void setWindowFlags(Qt::WindowFlags flags) override; - Qt::WindowFlags windowFlags() const; void setParent(const QPlatformWindow *window) override; WId winId() const override; @@ -58,7 +57,7 @@ public: void propagateSizeHints() override; void requestActivateWindow() override; - void updateSystemUiVisibility(); + void updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags); void updateFocusedEditText(); inline bool isRaster() const { return m_isRaster; } bool isExposed() const override; @@ -82,8 +81,6 @@ protected: bool isEmbeddingContainer() const; virtual void clearSurface() {} - Qt::WindowFlags m_windowFlags; - Qt::WindowStates m_windowState; bool m_isRaster; int m_nativeViewId = -1; diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm index aab01a7b439..e9ef769ec4b 100644 --- a/src/plugins/platforms/cocoa/qnsview_keys.mm +++ b/src/plugins/platforms/cocoa/qnsview_keys.mm @@ -114,6 +114,9 @@ static bool sendAsShortcut(const KeyEvent &keyEvent, QWindow *window) qCDebug(lcQpaKeys) << "Interpreting key event for focus object" << focusObject; m_currentlyInterpretedKeyEvent = nsevent; + // Asking the input context to handle the event will involve both + // the current input method, as well as NSKeyBindingManager, which + // may result in action callbacks to doCommandBySelector. if (![self.inputContext handleEvent:nsevent]) { qCDebug(lcQpaKeys) << "Input context did not consume event"; m_sendKeyEvent = true; diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp index 35e804531bc..5fa79482217 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp +++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp @@ -323,8 +323,9 @@ void QWasmAccessibility::setProperty(emscripten::val element, const std::string } -void QWasmAccessibility::addEventListener(emscripten::val element, const char *eventType) +void QWasmAccessibility::addEventListener(QAccessibleInterface *iface, emscripten::val element, const char *eventType) { + element.set("data-qta11yinterface", reinterpret_cast<size_t>(iface)); element.call<void>("addEventListener", emscripten::val(eventType), QWasmSuspendResumeControl::get()->jsEventHandlerAt(m_eventHandlerIndex), true); @@ -352,7 +353,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac case QAccessible::Button: { element = document.call<emscripten::val>("createElement", std::string("button")); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); } break; case QAccessible::CheckBox: { @@ -360,7 +361,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac setAttribute(element, "type", "checkbox"); setAttribute(element, "checked", iface->state().checked); setProperty(element, "indeterminate", iface->state().checkStateMixed); - addEventListener(element, "change"); + addEventListener(iface, element, "change"); } break; case QAccessible::Switch: { @@ -371,7 +372,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac setAttribute(element, "aria-checked", "true"); else setAttribute(element, "aria-checked", "false"); - addEventListener(element, "change"); + addEventListener(iface, element, "change"); } break; case QAccessible::RadioButton: { @@ -379,7 +380,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac setAttribute(element, "type", "radio"); setAttribute(element, "checked", iface->state().checked); setProperty(element, "name", "buttonGroup"); - addEventListener(element, "change"); + addEventListener(iface, element, "change"); } break; case QAccessible::SpinBox: @@ -413,7 +414,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("button")); setAttribute(element, "role", "tab"); setAttribute(element, "title", text.toStdString()); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); } break; case QAccessible::ScrollBar: { @@ -422,7 +423,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("div")); setAttribute(element, "role", "scrollbar"); setAttribute(element, "aria-valuenow", valueString); - addEventListener(element, "change"); + addEventListener(iface, element, "change"); } break; case QAccessible::StaticText: { @@ -436,7 +437,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("div")); setAttribute(element, "role", "toolbar"); setAttribute(element, "title", text.toStdString()); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); }break; case QAccessible::MenuItem: case QAccessible::ButtonMenu: { @@ -444,7 +445,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("button")); setAttribute(element, "role", "menuitem"); setAttribute(element, "title", text.toStdString()); - addEventListener(element, "click"); + addEventListener(iface, element, "click"); }break; case QAccessible::MenuBar: case QAccessible::PopupMenu: { @@ -471,7 +472,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac element = document.call<emscripten::val>("createElement", std::string("div")); } - addEventListener(element, "focus"); + addEventListener(iface, element, "focus"); return element; }(); @@ -542,6 +543,7 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface) { emscripten::val element = getHtmlElement(iface); emscripten::val container = getElementContainer(iface); + if (container.isUndefined() || element.isUndefined()) return; @@ -554,21 +556,21 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface) emscripten::val next = emscripten::val::undefined(); const int thisIndex = iface->parent()->indexOfChild(iface); - Q_ASSERT(thisIndex >= 0 && thisIndex < iface->parent()->childCount()); - for (int i = thisIndex + 1; i < iface->parent()->childCount(); ++i) { - const auto elementI = getHtmlElement(iface->parent()->child(i)); - if (!elementI.isUndefined() && - elementI["parentElement"] == container) { - next = elementI; - break; + if (thisIndex >= 0) { + Q_ASSERT(thisIndex < iface->parent()->childCount()); + for (int i = thisIndex + 1; i < iface->parent()->childCount(); ++i) { + const auto elementI = getHtmlElement(iface->parent()->child(i)); + if (!elementI.isUndefined() && + elementI["parentElement"] == container) { + next = elementI; + break; + } } + if (next.isUndefined()) + container.call<void>("appendChild", element); + else + container.call<void>("insertBefore", element, next); } - if (next.isUndefined()) { - container.call<void>("appendChild", element); - } else { - container.call<void>("insertBefore", element, next); - } - const auto activeElementAfter = emscripten::val::take_ownership( getActiveElement_js(emscripten::val::undefined().as_handle())); if (activeElementBefore != activeElementAfter) { @@ -712,22 +714,26 @@ void QWasmAccessibility::handleLineEditUpdate(QAccessibleEvent *event) void QWasmAccessibility::handleEventFromHtmlElement(const emscripten::val event) { - QAccessibleInterface *iface = m_elements.key(event["target"]); + if (event["target"].isNull() || event["target"].isUndefined()) + return; - if (iface == nullptr) { + if (event["target"]["data-qta11yinterface"].isNull() || event["target"]["data-qta11yinterface"].isUndefined()) return; - } else { - QString eventType = QString::fromStdString(event["type"].as<std::string>()); - const auto& actionNames = QAccessibleBridgeUtils::effectiveActionNames(iface); - - if (eventType == "focus") { - if (actionNames.contains(QAccessibleActionInterface::setFocusAction())) - iface->actionInterface()->doAction(QAccessibleActionInterface::setFocusAction()); - } else if (actionNames.contains(QAccessibleActionInterface::pressAction())) { - iface->actionInterface()->doAction(QAccessibleActionInterface::pressAction()); - } else if (actionNames.contains(QAccessibleActionInterface::toggleAction())) { - iface->actionInterface()->doAction(QAccessibleActionInterface::toggleAction()); - } + + auto iface = reinterpret_cast<QAccessibleInterface *>(event["target"]["data-qta11yinterface"].as<size_t>()); + if (m_elements.find(iface) == m_elements.end()) + return; + + const QString eventType = QString::fromStdString(event["type"].as<std::string>()); + const auto& actionNames = QAccessibleBridgeUtils::effectiveActionNames(iface); + + if (eventType == "focus") { + if (actionNames.contains(QAccessibleActionInterface::setFocusAction())) + iface->actionInterface()->doAction(QAccessibleActionInterface::setFocusAction()); + } else if (actionNames.contains(QAccessibleActionInterface::pressAction())) { + iface->actionInterface()->doAction(QAccessibleActionInterface::pressAction()); + } else if (actionNames.contains(QAccessibleActionInterface::toggleAction())) { + iface->actionInterface()->doAction(QAccessibleActionInterface::toggleAction()); } } diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.h b/src/plugins/platforms/wasm/qwasmaccessibility.h index 2b4716d64e7..26f3e0e9afe 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.h +++ b/src/plugins/platforms/wasm/qwasmaccessibility.h @@ -116,7 +116,7 @@ private: void setProperty(emscripten::val element, const std::string &attr, const char *val); void setProperty(emscripten::val element, const std::string &attr, bool val); - void addEventListener(emscripten::val element, const char *eventType); + void addEventListener(QAccessibleInterface *, emscripten::val element, const char *eventType); private: static QWasmAccessibility *s_instance; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 82a86d6ff3a..01716fba60c 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -4019,9 +4019,11 @@ void QWindowsWindow::requestUpdate() // request or we are waiting for the event loop to process // the Posted event on the GUI thread. if (m_vsyncUpdatePending.testAndSetAcquire(UpdateState::Requested, UpdateState::Posted)) { - QMetaObject::invokeMethod(w, [w] { + QWindowsWindow *oldSelf = this; + QMetaObject::invokeMethod(w, [w, oldSelf] { + // 'oldSelf' is only used for comparison, don't access it directly! auto *self = static_cast<QWindowsWindow *>(w->handle()); - if (self) { + if (self && self == oldSelf) { // The platform window is still alive self->m_vsyncUpdatePending.storeRelease(UpdateState::Ready); self->deliverUpdateRequest(); diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 5ba6f3e1649..0b05a31ca5c 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -5849,6 +5849,9 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const auto aquaSize = d->effectiveAquaSizeConstrain(opt, widget); const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Stepper, aquaSize); NSStepperCell *cell = static_cast<NSStepperCell *>(d->cocoaCell(cw)); + const auto controlSize = cell.controlSize; + if (qt_apple_runningWithLiquidGlass()) + cell.controlSize = NSControlSizeMini; cell.enabled = (sb->state & State_Enabled); const CGRect newRect = [cell drawingRectForBounds:updown.toCGRect()]; @@ -5869,6 +5872,8 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex [cell stopTracking:pressPoint at:pressPoint inView:d->backingStoreNSView mouseIsUp:NO]; d->restoreNSGraphicsContext(cg); + if (qt_apple_runningWithLiquidGlass()) + cell.controlSize = controlSize; } } break; diff --git a/src/plugins/tls/schannel/qtls_schannel.cpp b/src/plugins/tls/schannel/qtls_schannel.cpp index 12c2625f39d..667f2d8a6c3 100644 --- a/src/plugins/tls/schannel/qtls_schannel.cpp +++ b/src/plugins/tls/schannel/qtls_schannel.cpp @@ -1238,9 +1238,10 @@ bool TlsCryptographSchannel::createContext() }; #endif + const QString encodedTargetName = QUrl::fromUserInput(targetName()).host(QUrl::EncodeUnicode); auto status = InitializeSecurityContext(&credentialHandle, // phCredential nullptr, // phContext - const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName + const_reinterpret_cast<SEC_WCHAR *>(encodedTargetName.utf16()), // pszTargetName contextReq, // fContextReq 0, // Reserved1 0, // TargetDataRep (unused) diff --git a/src/testinternal/QtRunCMakeTestWrappers.cmake b/src/testinternal/QtRunCMakeTestWrappers.cmake index fdf93692e1f..cead9e52c82 100644 --- a/src/testinternal/QtRunCMakeTestWrappers.cmake +++ b/src/testinternal/QtRunCMakeTestWrappers.cmake @@ -30,10 +30,24 @@ function(qt_internal_add_RunCMake_test test) string(JOIN "\n" pre_run_code ${_qt_internal_skip_build_test_pre_run}) + set(android_code "") + if(ANDROID) + qt_internal_get_android_cmake_policy_version_minimum_value(version) + string(APPEND android_code " +# Avoid cmake policy deprecation warnings with older android NDKs appearing in stderr, which +# causes test failures if the test doesn't set +# set(RunCMake_TEST_OUTPUT_MERGE 1) +# to avoid stderr being polluted. +if(NOT QT_NO_SET_RUN_CMAKE_TESTS_CMAKE_POLICY_VERSION_MINIMUM) + set(ENV{CMAKE_POLICY_VERSION_MINIMUM} ${version}) +endif()") + endif() + _qt_internal_configure_file(CONFIGURE OUTPUT "${wrapper_file}" CONTENT " ${pre_run_code} +${android_code} include(\"${script_path_to_include}\") ") diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index c749cbd492f..431f91d5474 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -181,29 +181,17 @@ inline bool qCompare(quint32 const &t1, quint64 const &t2, const char *actual, } namespace Internal { -template <typename T> -class HasInitMain // SFINAE test for the presence of initMain() -{ -private: - using YesType = char[1]; - using NoType = char[2]; - - template <typename C> static YesType& test( decltype(&C::initMain) ) ; - template <typename C> static NoType& test(...); - -public: - enum { value = sizeof(test<T>(nullptr)) == sizeof(YesType) }; -}; +template <typename T, typename = void> +struct HasInitMain : std::false_type{}; -template<typename T> -typename std::enable_if<HasInitMain<T>::value, void>::type callInitMain() -{ - T::initMain(); -} +template <typename T> +struct HasInitMain<T, std::void_t<decltype(&T::initMain)>> : std::true_type {}; template<typename T> -typename std::enable_if<!HasInitMain<T>::value, void>::type callInitMain() +void callInitMain() { + if constexpr (HasInitMain<T>::value) + T::initMain(); } } // namespace Internal diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 6c7e71294ed..784e69d2486 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -393,6 +393,7 @@ static QString mainSourcePath; static bool inTestFunction = false; #if defined(Q_OS_MACOS) +static std::optional<QTestPrivate::AppNapDisabler> appNapDisabler; static IOPMAssertionID macPowerSavingDisabled = 0; #endif @@ -1881,13 +1882,12 @@ void QTest::qInit(QObject *testObject, int argc, char **argv) QTestPrivate::disableWindowRestore(); // Disable App Nap which may cause tests to stall - QTestPrivate::AppNapDisabler appNapDisabler; + if (!appNapDisabler) + appNapDisabler.emplace(); - if (qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0)) { - IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, - kIOPMAssertionLevelOn, CFSTR("QtTest running tests"), - &macPowerSavingDisabled); - } + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, CFSTR("QtTest running tests"), + &macPowerSavingDisabled); #endif QTestPrivate::parseBlackList(); @@ -2041,6 +2041,7 @@ void QTest::qCleanup() #if defined(Q_OS_MACOS) IOPMAssertionRelease(macPowerSavingDisabled); + appNapDisabler = std::nullopt; #endif } diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index 0e04d10e692..b517d85c5fb 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -328,6 +328,53 @@ static bool processAndroidManifest() return true; } +static QStringList queryDangerousPermissions() +{ + QByteArray output; + const QStringList args({ "shell"_L1, "dumpsys"_L1, "package"_L1, "permissions"_L1 }); + if (!execAdbCommand(args, &output, false)) { + qWarning("Failed to query permissions via dumpsys"); + return {}; + } + + /* + * Permissions section from this command look like: + * + * Permission [android.permission.INTERNET] (c8cafdc): + * sourcePackage=android + * uid=1000 gids=[3003] type=0 prot=normal|instant + * perm=PermissionInfo{5f5bfbb android.permission.INTERNET} + * flags=0x0 + */ + const static QRegularExpression regex("^\\s*Permission\\s+\\[([^\\]]+)\\]\\s+\\(([^)]+)\\):"_L1); + QStringList dangerousPermissions; + QString currentPerm; + + const QStringList lines = QString::fromUtf8(output).split(u'\n'); + for (const QString &line : lines) { + QRegularExpressionMatch match = regex.match(line); + if (match.hasMatch()) { + currentPerm = match.captured(1); + continue; + } + + if (currentPerm.isEmpty()) + continue; + + int protIndex = line.indexOf("prot="_L1); + if (protIndex == -1) + continue; + + QString protectionTypes = line.mid(protIndex + 5).trimmed(); + if (protectionTypes.contains("dangerous"_L1, Qt::CaseInsensitive)) { + dangerousPermissions.append(currentPerm); + currentPerm.clear(); + } + } + + return dangerousPermissions; +} + static void setOutputFile(QString file, QString format) { if (format.isEmpty()) @@ -938,7 +985,11 @@ int main(int argc, char *argv[]) return EXIT_ERROR; } + const QStringList dangerousPermisisons = queryDangerousPermissions(); for (const auto &permission : g_options.permissions) { + if (!dangerousPermisisons.contains(permission)) + continue; + if (!execAdbCommand({ "shell"_L1, "pm"_L1, "grant"_L1, g_options.package, permission }, nullptr)) { qWarning("Unable to grant '%s' to '%s'. Probably the Android version mismatch.", diff --git a/src/widgets/doc/src/external-resources.qdoc b/src/widgets/doc/src/external-resources.qdoc index 17459b6a5bc..96117546a29 100644 --- a/src/widgets/doc/src/external-resources.qdoc +++ b/src/widgets/doc/src/external-resources.qdoc @@ -8,7 +8,7 @@ */ /*! - \externalpage http://www.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm + \externalpage https://rk.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm \title Sinclair Spectrum */ /*! diff --git a/src/widgets/doc/src/qtwidgets-examples.qdoc b/src/widgets/doc/src/qtwidgets-examples.qdoc index 45677c471ba..364c985b310 100644 --- a/src/widgets/doc/src/qtwidgets-examples.qdoc +++ b/src/widgets/doc/src/qtwidgets-examples.qdoc @@ -164,3 +164,15 @@ regular expressions for the Widget-based applications. */ +/*! + \group examples-user-input + \ingroup all-examples + \title User Input Examples + \brief Using user input in Qt Widgets applications. + + \image imagegestures-example.png {Application handling touch gestures} + + Qt provides the functionality for handling user input and drag-and-drop in + widget-based applications. + +*/ diff --git a/src/widgets/doc/src/qtwidgets-toc.qdoc b/src/widgets/doc/src/qtwidgets-toc.qdoc index bc447b8bd58..beddf853a22 100644 --- a/src/widgets/doc/src/qtwidgets-toc.qdoc +++ b/src/widgets/doc/src/qtwidgets-toc.qdoc @@ -53,6 +53,7 @@ \li \l{Rich Text Examples} \li \l{Graphics View Examples} \li \l{Widget Tools Examples} + \li \l{User Input Examples} \endlist \endlist diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 51aea4079a1..6288aae096a 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -3108,7 +3108,8 @@ void QAbstractItemView::keyboardSearch(const QString &search) QModelIndex startMatch; QModelIndexList previous; do { - match = d->model->match(current, Qt::DisplayRole, d->keyboardInput); + match = d->model->match(current, Qt::DisplayRole, d->keyboardInput, 1, + d->keyboardSearchFlags); if (match == previous) break; firstMatch = match.value(0); @@ -3251,6 +3252,30 @@ void QAbstractItemView::setUpdateThreshold(int threshold) } /*! + \property QAbstractItemView::keyboardSearchFlags + \since 6.11 + This property determines how the default implementation of + keyboardSearch() matches the given string against the model's data. + + The default value is \c{Qt::MatchStartsWith|Qt::MatchWrap}. + + \sa keyboardSearch() + \sa QAbstractItemModel::match() +*/ + +Qt::MatchFlags QAbstractItemView::keyboardSearchFlags() const +{ + Q_D(const QAbstractItemView); + return d->keyboardSearchFlags; +} + +void QAbstractItemView::setKeyboardSearchFlags(Qt::MatchFlags searchFlags) +{ + Q_D(QAbstractItemView); + d->keyboardSearchFlags = searchFlags; +} + +/*! Opens a persistent editor on the item at the given \a index. If no editor exists, the delegate will create a new editor. diff --git a/src/widgets/itemviews/qabstractitemview.h b/src/widgets/itemviews/qabstractitemview.h index 63adac8d6f2..973a9b044cb 100644 --- a/src/widgets/itemviews/qabstractitemview.h +++ b/src/widgets/itemviews/qabstractitemview.h @@ -48,6 +48,8 @@ class Q_WIDGETS_EXPORT QAbstractItemView : public QAbstractScrollArea Q_PROPERTY(ScrollMode horizontalScrollMode READ horizontalScrollMode WRITE setHorizontalScrollMode RESET resetHorizontalScrollMode) Q_PROPERTY(int updateThreshold READ updateThreshold WRITE setUpdateThreshold) + Q_PROPERTY(Qt::MatchFlags keyboardSearchFlags READ keyboardSearchFlags + WRITE setKeyboardSearchFlags) public: enum SelectionMode { @@ -182,6 +184,9 @@ public: int updateThreshold() const; void setUpdateThreshold(int threshold); + Qt::MatchFlags keyboardSearchFlags() const; + void setKeyboardSearchFlags(Qt::MatchFlags searchFlags); + void openPersistentEditor(const QModelIndex &index); void closePersistentEditor(const QModelIndex &index); bool isPersistentEditorOpen(const QModelIndex &index) const; diff --git a/src/widgets/itemviews/qabstractitemview_p.h b/src/widgets/itemviews/qabstractitemview_p.h index b24b2d21c33..60799fb8a50 100644 --- a/src/widgets/itemviews/qabstractitemview_p.h +++ b/src/widgets/itemviews/qabstractitemview_p.h @@ -383,6 +383,7 @@ public: QString keyboardInput; QElapsedTimer keyboardInputTime; + Qt::MatchFlags keyboardSearchFlags = Qt::MatchStartsWith | Qt::MatchWrap; bool autoScroll; QBasicTimer autoScrollTimer; diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index da1fbbd60df..84ff04c9f34 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -1030,7 +1030,8 @@ void QTreeView::keyboardSearch(const QString &search) searchFrom = searchFrom.sibling(searchFrom.row(), start.column()); if (searchFrom.parent() == start.parent()) searchFrom = start; - QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString); + QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString, 1, + keyboardSearchFlags()); if (match.size()) { int hitIndex = d->viewIndex(match.at(0)); if (hitIndex >= 0 && hitIndex < startIndex) diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 53ce4dd8211..fa95a1d2538 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1508,11 +1508,15 @@ void QApplicationPrivate::setFocusWidget(QWidget *focus, Qt::FocusReason reason) return; } - if (focus && (reason == Qt::BacktabFocusReason || reason == Qt::TabFocusReason) - && qt_in_tab_key_event) - focus->window()->setAttribute(Qt::WA_KeyboardFocusChange); - else if (focus && reason == Qt::ShortcutFocusReason) { - focus->window()->setAttribute(Qt::WA_KeyboardFocusChange); + if (focus) { + if ((reason == Qt::BacktabFocusReason || reason == Qt::TabFocusReason) + && qt_in_tab_key_event) + focus->window()->setAttribute(Qt::WA_KeyboardFocusChange); + else if (reason == Qt::ShortcutFocusReason) { + focus->window()->setAttribute(Qt::WA_KeyboardFocusChange); + } else { + focus->window()->setAttribute(Qt::WA_KeyboardFocusChange, false); + } } QWidget *prev = focus_widget; focus_widget = focus; diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 7d4228709be..69548c4e17e 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -771,7 +771,8 @@ void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason #endif hideMenu(hideActiveMenu); } else if (!currentAction || !currentAction->menu()) { - sloppyState.startTimerIfNotRunning(); + if (reason != SelectionReason::SelectedFromAPI) + sloppyState.startTimerIfNotRunning(); } } } @@ -2172,7 +2173,7 @@ void QMenu::hideTearOffMenu() void QMenu::setActiveAction(QAction *act) { Q_D(QMenu); - d->setCurrentAction(act, 0); + d->setCurrentAction(act, 0, QMenuPrivate::SelectionReason::SelectedFromAPI); if (d->scroll && act) d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollCenter); } @@ -2971,7 +2972,7 @@ void QMenu::mouseReleaseEvent(QMouseEvent *e) #endif d->activateAction(action, QAction::Trigger); } - } else if (!action || action->isEnabled()) { + } else if ((!action || action->isEnabled()) && !action->isSeparator()) { d->hideUpToMenuBar(); } } diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h index dd1f058a288..d9dcd7d0362 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -362,7 +362,8 @@ public: } delayState; enum SelectionReason { SelectedFromKeyboard, - SelectedFromElsewhere + SelectedFromAPI, + SelectedFromElsewhere, }; enum class SelectionDirection { Up, |