diff options
29 files changed, 538 insertions, 288 deletions
diff --git a/examples/widgets/gallery/widgetgallery.cpp b/examples/widgets/gallery/widgetgallery.cpp index 64a83676c02..e2ffec50e81 100644 --- a/examples/widgets/gallery/widgetgallery.cpp +++ b/examples/widgets/gallery/widgetgallery.cpp @@ -419,7 +419,8 @@ void WidgetGallery::updateSystemInfo() QString systemInfo; QTextStream str(&systemInfo); str << "<html><head/><body><h3>Build</h3><p>" << QLibraryInfo::build() << "</p>" - << "<h3>Operating System</h3><p>" << QSysInfo::prettyProductName() << "</p>" + << "<h3>Operating System</h3><p>\"" << QSysInfo::prettyProductName() << "\" / " + << QGuiApplication::platformName() << "</p>" << "<h3>Screens</h3><p>High DPI scale factor rounding policy: " << highDpiScaleFactorRoundingPolicy() << "</p><ol>"; const auto screens = QGuiApplication::screens(); 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/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp index 611f90679d5..ef5d49fb20e 100644 --- a/src/corelib/io/qlockfile_win.cpp +++ b/src/corelib/io/qlockfile_win.cpp @@ -55,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(), @@ -146,7 +147,26 @@ QString QLockFilePrivate::processNameByPid(qint64 pid) int QLockFilePrivate::openNewFileDescriptor(const QString &fileName) { - return _open(fileName.toLocal8Bit().constData(), _O_RDONLY); + // 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() 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/text/qbytearray.h b/src/corelib/text/qbytearray.h index ef30d5e0da1..9fb832545f8 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -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.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/qstring.h b/src/corelib/text/qstring.h index b8acf7c923d..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) 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/qlist.h b/src/corelib/tools/qlist.h index 79b367b199c..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(); } 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/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/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/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/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp index 35e804531bc..6a3c139a060 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; }(); @@ -712,22 +713,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/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/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/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp index 3804f53cae9..0b65673c393 100644 --- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp +++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp @@ -92,6 +92,7 @@ private slots: void qIsNull(); void for_each(); void qassert(); + void qpresume(); void qtry(); void checkptr(); void qstaticassert(); @@ -266,6 +267,18 @@ void tst_QGlobal::qassert() QVERIFY(passed); } +/*non-static*/ Q_NEVER_INLINE char presumedValue(const char *str) +{ + Q_PRESUME(str); + // an optimizing compiler should delete the str? check + return str ? str[0] : '\0'; +} + +void tst_QGlobal::qpresume() +{ + QCOMPARE(presumedValue("Hello World"), 'H'); +} + void tst_QGlobal::qtry() { int i = 0; diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index 9696f97f9f8..5453237cadb 100644 --- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp +++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp @@ -188,6 +188,10 @@ private slots: void testEscapedKeys(); void testUnescapedKeys_data(); void testUnescapedKeys(); + void testEscapedStringList_data(); + void testEscapedStringList(); + void testUnescapedStringList_data(); + void testUnescapedStringList(); void testNormalizedKey_data(); void testNormalizedKey(); void testVariantTypes_data() { populateWithFormats(); } @@ -2836,26 +2840,6 @@ void tst_QSettings::testEscapes() { QSettings settings(QSettings::UserScope, "software.org", "KillerAPP"); -#define testEscapedStringList(plainStrList, escStrList) \ - { \ - QStringList plainList(plainStrList); \ - QByteArray escList(escStrList); \ - QCOMPARE(iniEscapedStringList(plainList), escList); \ - QCOMPARE(iniUnescapedStringList(escList), plainList); \ - } \ - - -#define testUnescapedStringList(escStrList, plainStrList, reescStrList) \ - { \ - QStringList plainList(plainStrList); \ - QByteArray escList(escStrList); \ - QByteArray reescList(reescStrList); \ - QCOMPARE(iniUnescapedStringList(escList), plainList); \ - QCOMPARE(iniEscapedStringList(plainList), reescList); \ - QCOMPARE(iniUnescapedStringList(reescList), plainList); \ - } \ - - #define testVariant(val, escStr, func) \ { \ QVariant v(val); \ @@ -2871,41 +2855,6 @@ void tst_QSettings::testEscapes() QCOMPARE(v.toString(), QString(vStr)); \ } - testEscapedStringList("", ""); - testEscapedStringList(" ", "\" \""); - testEscapedStringList(";", "\";\""); - testEscapedStringList(",", "\",\""); - testEscapedStringList("=", "\"=\""); - testEscapedStringList("abc-def", "abc-def"); - testEscapedStringList(QChar(0) + QString("0"), "\\0\\x30"); - testEscapedStringList("~!@#$%^&*()_+.-/\\=", "\"~!@#$%^&*()_+.-/\\\\=\""); - testEscapedStringList("~!@#$%^&*()_+.-/\\", "~!@#$%^&*()_+.-/\\\\"); - testEscapedStringList(QString("\x7F") + "12aFz", QByteArray("\x7f") + "12aFz"); - testEscapedStringList(QString(" \t\n\\n") + QChar(0x123) + QChar(0x4567), "\" \\t\\n\\\\n\xC4\xA3\xE4\x95\xA7\""); - testEscapedStringList(QString("\a\b\f\n\r\t\v'\"?\001\002\x03\x04"), "\\a\\b\\f\\n\\r\\t\\v'\\\"?\\x1\\x2\\x3\\x4"); - testEscapedStringList(QStringList() << "," << ";" << "a" << "ab, \tc, d ", "\",\", \";\", a, \"ab, \\tc, d \""); - - /* - Test .ini syntax that cannot be generated by QSettings (but can be entered by users). - */ - testUnescapedStringList("", "", ""); - testUnescapedStringList("\"\"", "", ""); - testUnescapedStringList("\"abcdef\"", "abcdef", "abcdef"); - testUnescapedStringList("\"\\?\\'\\\"\"", "?'\"", "?'\\\""); - testUnescapedStringList("\\0\\00\\000\\0000000\\1\\111\\11111\\x\\x0\\xABCDEFGH\\x0123456\\", - QString() + QChar(0) + QChar(0) + QChar(0) + QChar(0) + QChar(1) - + QChar(0111) + QChar(011111) + QChar(0) + QChar(0xCDEF) + "GH" - + QChar(0x3456), - "\\0\\0\\0\\0\\x1I\xE1\x89\x89\\0\xEC\xB7\xAFGH\xE3\x91\x96"); - testUnescapedStringList(QByteArray("\\c\\d\\e\\f\\g\\$\\*\\\0", 16), "\f", "\\f"); - testUnescapedStringList("\"a\", \t\"bc \", \" d\" , \"ef \" ,,g, hi i,,, ,", - QStringList() << "a" << "bc " << " d" << "ef " << "" << "g" << "hi i" - << "" << "" << "" << "", - "a, \"bc \", \" d\", \"ef \", , g, hi i, , , , "); - testUnescapedStringList("a , b , c d , efg ", - QStringList() << "a" << "b" << "c d" << "efg", - "a, b, c d, efg"); - // streaming qvariant into a string testVariant(QString("Hello World!"), QString("Hello World!"), toString); testVariant(QString("Hello, World!"), QString("Hello, World!"), toString); @@ -2994,6 +2943,125 @@ void tst_QSettings::testUnescapedKeys() QCOMPARE(iniUnescapedKey(reescKey), plainKey); } +void tst_QSettings::testEscapedStringList_data() +{ + QTest::addColumn<QStringList>("plainStrList"); + QTest::addColumn<QByteArray>("escapedList"); + + QTest::newRow("empty-string") << QStringList{u""_s} << ""_ba; + QTest::newRow("space") << QStringList{u" "_s} << "\" \""_ba; + QTest::newRow(";") << QStringList{u";"_s} << "\";\""_ba; + QTest::newRow(",") << QStringList{u","_s} << "\",\""_ba; + QTest::newRow("=") << QStringList{u"="_s} << "\"=\""_ba; + QTest::newRow("abc-def") << QStringList{u"abc-def"_s} << "abc-def"_ba; + + QTest::newRow("starts-with-NUL") + << QStringList{QChar(0) + u"0"_s} + << "\\0\\x30"_ba; + + QTest::newRow("special-characters1") + << QStringList{u"~!@#$%^&*()_+.-/\\="_s} + << "\"~!@#$%^&*()_+.-/\\\\=\""_ba; + + QTest::newRow("special-characters2") + << QStringList{u"~!@#$%^&*()_+.-/\\"_s} + << "~!@#$%^&*()_+.-/\\\\"_ba; + + QTest::newRow("DEL-character") + << QStringList{u"\x7F"_s + u"12aFz"_s} + << "\x7f"_ba + "12aFz"_ba; + + QTest::newRow("tab-newline") + << QStringList{u" \t\n\\n"_s + QChar(0x123) + QChar(0x4567)} + << "\" \\t\\n\\\\n\xC4\xA3\xE4\x95\xA7\""_ba; + + QTest::newRow("backslash-espcaped-input") + << QStringList{u"\a\b\f\n\r\t\v'\"?\001\002\x03\x04"_s} + << "\\a\\b\\f\\n\\r\\t\\v'\\\"?\\x1\\x2\\x3\\x4"_ba; + + QTest::newRow("stringlist-with-tab") + << QStringList{u","_s, u";"_s, u"a"_s, u"ab, \tc, d "_s} + << "\",\", \";\", a, \"ab, \\tc, d \""_ba; +} + +void tst_QSettings::testEscapedStringList() +{ + QFETCH(QStringList, plainStrList); + QFETCH(QByteArray, escapedList); + + QSettings settings(QSettings::UserScope, "example.org", "KillerAPP"); + + QCOMPARE(iniEscapedStringList(plainStrList), escapedList); + QCOMPARE(iniUnescapedStringList(escapedList), plainStrList); +} + +void tst_QSettings::testUnescapedStringList_data() +{ + QTest::addColumn<QByteArray>("escStrList"); + QTest::addColumn<QStringList>("plainStrList"); + QTest::addColumn<QByteArray>("reescStrList"); + + /* + Test .ini syntax that cannot be generated by QSettings (but can be entered by users). + */ + QTest::newRow("empty") + << ""_ba + << QStringList{u""_s} + << ""_ba; + + QTest::newRow("empty-double-quotes") + << "\"\""_ba + << QStringList{u""_s} + << ""_ba; + + QTest::newRow("plain-quoted-string") + << "\"abcdef\""_ba + << QStringList{u"abcdef"_s} + << "abcdef"_ba; + + QTest::newRow("backslash-non-letter-characters") + << "\"\\?\\'\\\"\""_ba + << QStringList{u"?'\""_s} + << "?'\\\""_ba; + + const std::array arr = {QChar(0), QChar(0), QChar(0), QChar(0), QChar(1), + QChar(0111), QChar(011111), QChar(0), QChar(0xCDEF), + QChar(u'G'), QChar(u'H'), QChar(0x3456)}; + QTest::newRow("array-of-qchar") + << "\\0\\00\\000\\0000000\\1\\111\\11111\\x\\x0\\xABCDEFGH\\x0123456\\"_ba + << QStringList{QString{arr}} + << "\\0\\0\\0\\0\\x1I\xE1\x89\x89\\0\xEC\xB7\xAFGH\xE3\x91\x96"_ba; + + QTest::newRow("backslash-escapes") + << QByteArray("\\c\\d\\e\\f\\g\\$\\*\\\0", 16) + << QStringList{u"\f"_s} + << "\\f"_ba; + + QTest::newRow("double-quotes-tab-character") + << "\"a\", \t\"bc \", \" d\" , \"ef \" ,,g, hi i,,, ,"_ba + << QStringList{u"a"_s, u"bc "_s, u" d"_s, u"ef "_s, u""_s, u"g"_s, + u"hi i"_s, u""_s, u""_s, u""_s, u""_s} + << "a, \"bc \", \" d\", \"ef \", , g, hi i, , , , "_ba; + + QTest::newRow("abcdefg-extra-whitespaces") + << "a , b , c d , efg "_ba + << QStringList{u"a"_s, u"b"_s, u"c d"_s, u"efg"_s} + << "a, b, c d, efg"_ba; +} + +void tst_QSettings::testUnescapedStringList() +{ + QFETCH(QByteArray, escStrList); + QFETCH(QStringList, plainStrList); + QFETCH(QByteArray, reescStrList); + + QSettings settings(QSettings::UserScope, "example.org", "KillerAPP"); + + QCOMPARE(iniUnescapedStringList(escStrList), plainStrList); + QCOMPARE(iniEscapedStringList(plainStrList), reescStrList); + QCOMPARE(iniUnescapedStringList(reescStrList), plainStrList); +} + #endif void tst_QSettings::testCaseSensitivity() diff --git a/tests/auto/testlib/initmain/tst_initmain.cpp b/tests/auto/testlib/initmain/tst_initmain.cpp index 75a0d9ceb4d..cdaac0c14f4 100644 --- a/tests/auto/testlib/initmain/tst_initmain.cpp +++ b/tests/auto/testlib/initmain/tst_initmain.cpp @@ -19,6 +19,9 @@ private: static bool m_initMainCalled; }; +static_assert(QTest::Internals::HasInitMain<tst_InitMain>::value); +static_assert(!QTest::Internals::HasInitMain<QObject>::value); + bool tst_InitMain::m_initMainCalled = false; void tst_InitMain::testcase() diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index 8ee122ece18..6859f22c044 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -842,6 +842,20 @@ void tst_QComboBox::virtualAutocompletion() QApplication::sendEvent(testWidget, &kp2); QApplication::sendEvent(testWidget, &kr2); QTRY_COMPARE(testWidget->currentIndex(), 3); + + QKeyEvent kp3(QEvent::KeyPress, Qt::Key_R, {}, "r"); + QKeyEvent kr3(QEvent::KeyRelease, Qt::Key_R, {}, "r"); + QTest::qWait(QApplication::keyboardInputInterval()); + QApplication::sendEvent(testWidget, &kp3); + QApplication::sendEvent(testWidget, &kr3); + QTRY_COMPARE(testWidget->currentIndex(), 3); + + QTest::qWait(QApplication::keyboardInputInterval()); + testWidget->view()->setKeyboardSearchFlags(Qt::MatchContains | Qt::MatchWrap); + QApplication::sendEvent(testWidget, &kp3); + QApplication::sendEvent(testWidget, &kr3); + QTRY_COMPARE(testWidget->currentIndex(), 1); + #if defined(Q_PROCESSOR_ARM) || defined(Q_PROCESSOR_MIPS) QApplication::setKeyboardInputInterval(oldInterval); #endif |