diff options
156 files changed, 2513 insertions, 1509 deletions
diff --git a/cmake/QtAndroidHelpers.cmake b/cmake/QtAndroidHelpers.cmake index b473c2c331b..59a469b1683 100644 --- a/cmake/QtAndroidHelpers.cmake +++ b/cmake/QtAndroidHelpers.cmake @@ -442,8 +442,9 @@ function(qt_internal_create_source_jar) add_dependencies(android_source_jars ${jar_target}) if(QT_WILL_INSTALL) + qt_path_join(destination "${INSTALL_DATADIR}" "android" "${module}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${jar_name}-${PROJECT_VERSION}.jar" - DESTINATION "${INSTALL_DATADIR}/android/${module}" + DESTINATION "${destination}" COMPONENT _install_android_source_jar_${module} EXCLUDE_FROM_ALL ) diff --git a/cmake/QtAutoDetectHelpers.cmake b/cmake/QtAutoDetectHelpers.cmake index fe6087748d6..6e6473da641 100644 --- a/cmake/QtAutoDetectHelpers.cmake +++ b/cmake/QtAutoDetectHelpers.cmake @@ -64,6 +64,41 @@ function(qt_auto_detect_wasm) endif() endfunction() +# Handle assignment of CMAKE_POLICY_VERSION_MINIMUM for Android NDK cmake toolchain files shipped +# with NDK < r28, to avoid deprecation warnings. +# +# NOTE: If updating the version, also update +# qt_internal_get_android_qt_default_cmake_policy_version_minimum. +# +# Use a macro, to make propagation of the variable in the parent scope of the calling function +# easier. +macro(qt_auto_detect_set_android_cmake_policy_version_minimum is_android_detected) + if("${is_android_detected}" + AND CMAKE_VERSION VERSION_GREATER_EQUAL "4.0" + AND NOT QT_NO_SET_ANDROID_CMAKE_POLICY_VERSION_MINIMUM + ) + + if(QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM) + set(min_policy_version "${QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM}") + elseif(CMAKE_POLICY_VERSION_MINIMUM) + set(min_policy_version "${CMAKE_POLICY_VERSION_MINIMUM}") + else() + set(min_policy_version "3.10") + endif() + + message(DEBUG + "Setting CMAKE_POLICY_VERSION_MINIMUM to ${min_policy_version} for Android builds.") + + # Set the variable in the qtbase directory scope for easier reading. + set(CMAKE_POLICY_VERSION_MINIMUM "${min_policy_version}" PARENT_SCOPE) + + # Also set the environment variable, otherwise any try_compile project that's started + # by CMake itself, rather than Qt (e.g. compiler detection), will not inherit the + # assignment. + set(ENV{CMAKE_POLICY_VERSION_MINIMUM} "${min_policy_version}") + endif() +endmacro() + function(qt_auto_detect_android) # Don't assume an Android build if we're requesting to build Java documentation on the host. if(QT_BUILD_HOST_JAVA_DOCS) @@ -154,6 +189,8 @@ function(qt_auto_detect_android) elseif (QT_AUTODETECT_ANDROID) message(STATUS "Android build detected") endif() + + qt_auto_detect_set_android_cmake_policy_version_minimum("${android_detected}") endfunction() function(qt_auto_detect_vcpkg) diff --git a/cmake/QtBaseConfigureTests.cmake b/cmake/QtBaseConfigureTests.cmake index fcc3b1a3f1c..da97e2b4f8b 100644 --- a/cmake/QtBaseConfigureTests.cmake +++ b/cmake/QtBaseConfigureTests.cmake @@ -306,6 +306,36 @@ function(qt_internal_print_cmake_host_and_target_info) message(STATUS "CMAKE_SYSTEM_PROCESSOR: \"${CMAKE_SYSTEM_PROCESSOR}\"") message(STATUS "CMAKE_CROSSCOMPILING: \"${CMAKE_CROSSCOMPILING}\"") + + message(STATUS "CMAKE_CXX_COMPILER_ID: \"${CMAKE_CXX_COMPILER_ID}\"") + message(STATUS "CMAKE_CXX_COMPILER_VERSION: \"${CMAKE_CXX_COMPILER_VERSION}\"") + + # The variables might not be defined depending on platform and CMake version. + if(CMAKE_CXX_COMPILER_TARGET) + message(STATUS "CMAKE_CXX_COMPILER_TARGET: \"${CMAKE_CXX_COMPILER_TARGET}\"") + endif() + if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID) + message(STATUS + "CMAKE_CXX_COMPILER_ARCHITECTURE_ID: \"${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}\"") + endif() + if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT) + message(STATUS + "CMAKE_CXX_COMPILER_FRONTEND_VARIANT: \"${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}\"") + endif() + + if(CMAKE_CXX_COMPILER_LINKER_ID) + message(STATUS "CMAKE_CXX_COMPILER_LINKER_ID: \"${CMAKE_CXX_COMPILER_LINKER_ID}\"") + endif() + + if(CMAKE_CXX_COMPILER_LINKER_VERSION) + message(STATUS + "CMAKE_CXX_COMPILER_LINKER_VERSION: \"${CMAKE_CXX_COMPILER_LINKER_VERSION}\"") + endif() + + if(CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT) + message(STATUS "CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT: " + "\"${CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT}\"") + endif() endfunction() qt_internal_print_cmake_host_and_target_info() diff --git a/cmake/QtCMakeVersionHelpers.cmake b/cmake/QtCMakeVersionHelpers.cmake index 3a3ba0fc750..73b40b4a610 100644 --- a/cmake/QtCMakeVersionHelpers.cmake +++ b/cmake/QtCMakeVersionHelpers.cmake @@ -300,3 +300,90 @@ function(qt_internal_upgrade_cmake_policies) qt_internal_get_max_new_policy_cmake_version(upper_version) cmake_minimum_required(VERSION ${lower_version}...${upper_version}) endfunction() + +# Get which version to use for CMAKE_POLICY_VERSION_MINIMUM on Android. +# Allow various overrides via QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM and reading an existing +# CMAKE_POLICY_VERSION_MINIMUM. +function(qt_internal_get_android_cmake_policy_version_minimum_value out_var) + if(QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM) + set(value "${QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM}") + elseif(CMAKE_POLICY_VERSION_MINIMUM) + set(value "${CMAKE_POLICY_VERSION_MINIMUM}") + else() + qt_internal_get_android_qt_default_cmake_policy_version_minimum(default_value) + set(value "${default_value}") + endif() + + set(${out_var} "${value}" PARENT_SCOPE) +endfunction() + +# NOTE: If updating the version, also update +# qt_auto_detect_set_android_cmake_policy_version_minimum. +function(qt_internal_get_android_qt_default_cmake_policy_version_minimum out_var) + set(${out_var} "3.10" PARENT_SCOPE) +endfunction() + +# Handle assignment of CMAKE_POLICY_VERSION_MINIMUM for Android NDK cmake toolchain files shipped +# with NDK < r28, to avoid deprecation warnings. +# See https://github.com/android/ndk/issues/2100 +# and https://android.googlesource.com/platform/ndk/+/799e5a2d44cc2cc6c7d67f52f2d67957944b7680 +# The function is to get the appropriate var asisgnment for try_compile calls, +# as well as writing it to the Qt generated toolchain file. +# Various opt-outs and opt-ins are provided via QT_NO_SET_ANDROID_CMAKE_POLICY_VERSION_MINIMUM +# and QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM. +# +# See also usage in qt_auto_detect_set_android_cmake_policy_version_minimum. +function(qt_internal_get_android_cmake_policy_version_minimum_assignment out_var) + set(option_args "") + set(single_args + TYPE + ) + set(multi_args "") + + cmake_parse_arguments(PARSE_ARGV 1 arg + "${option_args}" + "${single_args}" + "${multi_args}" + ) + _qt_internal_validate_all_args_are_parsed(arg) + + set(value "") + if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.0" + AND NOT QT_NO_SET_ANDROID_CMAKE_POLICY_VERSION_MINIMUM + ) + qt_internal_get_android_cmake_policy_version_minimum_value(version) + + if(arg_TYPE STREQUAL "COMMAND_LINE") + set(value "-DCMAKE_POLICY_VERSION_MINIMUM=${version}") + + elseif(arg_TYPE STREQUAL "TOOLCHAIN_FILE_ASSIGNMENT") + set(value " +# Avoid deprecation warnings in Android ndk cmake toolchain file < r28 +set(__qt_initially_configured_android_cmake_policy_version_minimum \"${version}\") +if(CMAKE_VERSION VERSION_GREATER_EQUAL \"4.0\" + AND NOT QT_NO_SET_ANDROID_CMAKE_POLICY_VERSION_MINIMUM + ) + if(QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM) + set(__qt_toolchain_cmake_policy_version_minimum + \"\${QT_ANDROID_CMAKE_POLICY_VERSION_MINIMUM}\") + elseif(CMAKE_POLICY_VERSION_MINIMUM) + set(__qt_toolchain_cmake_policy_version_minimum + \"\${CMAKE_POLICY_VERSION_MINIMUM}\") + else() + set(__qt_toolchain_cmake_policy_version_minimum + \"\${__qt_initially_configured_android_cmake_policy_version_minimum}\") + endif() + set(CMAKE_POLICY_VERSION_MINIMUM \"\${__qt_toolchain_cmake_policy_version_minimum}\") + message(DEBUG + \"Setting CMAKE_POLICY_VERSION_MINIMUM to \" + \"\${__qt_toolchain_cmake_policy_version_minimum}\ in toolchain file.\") +endif() +") + else() + message(FATAL_ERROR "Unknown TYPE value '${arg_TYPE}'. " + " Supported values are COMMAND_LINE and VAR_ASSIGNMENT.") + endif() + endif() + + set(${out_var} "${value}" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index e8ae3dd5163..de0323214c5 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -1737,6 +1737,14 @@ function(qt_get_platform_try_compile_vars out_var) list(APPEND flags_cmd_line "-DCMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH:BOOL=OFF") endif() + if(ANDROID) + qt_internal_get_android_cmake_policy_version_minimum_assignment( + android_cmake_policy_version_minimum TYPE COMMAND_LINE) + if(android_cmake_policy_version_minimum) + list(APPEND flags_cmd_line "${android_cmake_policy_version_minimum}") + endif() + endif() + set("${out_var}" "${flags_cmd_line}" PARENT_SCOPE) endfunction() diff --git a/cmake/QtPublicCMakeVersionHelpers.cmake b/cmake/QtPublicCMakeVersionHelpers.cmake index 292d97c84e7..0207c087e1a 100644 --- a/cmake/QtPublicCMakeVersionHelpers.cmake +++ b/cmake/QtPublicCMakeVersionHelpers.cmake @@ -107,10 +107,9 @@ endfunction() # Handle force-assignment of CMP0156 policy when using CMake 3.29+. # # For Apple-platforms we set it to NEW, to avoid duplicate linker issues when using -ObjC flag. +# For Emscripten / WebAssembly we also set it to NEW, to avoid duplicate linker issues. # -# For non-Apple platforms we set it to OLD, because we haven't done the necessary testing to -# see which platforms / linkers can handle the new deduplication behavior, without breaking the -# various linking techniques that Qt uses for object library propagation. +# For other platforms, we leave the policy value as-is, without showing any warnings. function(__qt_internal_set_cmp0156) # Exit early if not using CMake 3.29+ if(NOT POLICY CMP0156) @@ -164,29 +163,32 @@ function(__qt_internal_set_cmp0156) set(default_policy_value NEW) set(unsupported_policy_value OLD) else() - # For non-Apple linkers, we keep the previous behavior of not deduplicating libraries, - # because we haven't done the necessary testing to identify on which platforms - # it is safe to deduplicate. - set(default_policy_value OLD) - set(unsupported_policy_value NEW) + # For other platforms we don't enforce any policy values and keep them as-is. + set(default_policy_value "") + set(unsupported_policy_value "") endif() # Force set the default policy value for the given platform, even if the policy value is # the same or empty. That's because in the calling function scope, the value can be empty # due to the cmake_minimum_required call in Qt6Config.cmake resetting the policy value. - get_cmake_property(debug_message_shown _qt_internal_cmp0156_debug_message_shown) - if(NOT debug_message_shown) - message(DEBUG "Force setting the CMP0156 policy to '${default_policy_value}' " - "for platform '${CMAKE_SYSTEM_NAME}'.") - set_property(GLOBAL PROPERTY _qt_internal_cmp0156_debug_message_shown TRUE) - endif() + if(default_policy_value) + get_cmake_property(debug_message_shown _qt_internal_cmp0156_debug_message_shown) + if(NOT debug_message_shown) + message(DEBUG "Force setting the CMP0156 policy to '${default_policy_value}' " + "for platform '${CMAKE_SYSTEM_NAME}'.") + set_property(GLOBAL PROPERTY _qt_internal_cmp0156_debug_message_shown TRUE) + endif() - cmake_policy(SET CMP0156 "${default_policy_value}") + cmake_policy(SET CMP0156 "${default_policy_value}") + endif() - # If the policy is explicitly set to a value other than the default, issue a warning. + # If the policy is explicitly set to a value other than the (non-empty) default, issue a + # warning. # Don't show the warning if the policy is unset, which would be the default for most # projects, because it's too much noise. Also don't show it for Qt builds. - if("${policy_value}" STREQUAL "${unsupported_policy_value}" AND NOT QT_BUILDING_QT) + if(unsupported_policy_value + AND "${policy_value}" STREQUAL "${unsupported_policy_value}" + AND NOT QT_BUILDING_QT) message(WARNING "CMP0156 is set to '${policy_value}'. Qt forces the '${default_policy_value}'" " behavior of this policy for the '${CMAKE_SYSTEM_NAME}' platform by default." diff --git a/cmake/QtToolchainHelpers.cmake b/cmake/QtToolchainHelpers.cmake index 9407fd0ebe5..348a3c25603 100644 --- a/cmake/QtToolchainHelpers.cmake +++ b/cmake/QtToolchainHelpers.cmake @@ -309,6 +309,13 @@ endif()") " \"Please specify the toolchain file with -DQT_CHAINLOAD_TOOLCHAIN_FILE=<file>.\")") list(APPEND init_platform " endif()") list(APPEND init_platform "endif()") + + qt_internal_get_android_cmake_policy_version_minimum_assignment( + android_cmake_policy_version_minimum TYPE TOOLCHAIN_FILE_ASSIGNMENT) + if(android_cmake_policy_version_minimum) + list(APPEND init_platform "${android_cmake_policy_version_minimum}") + endif() + elseif(EMSCRIPTEN) list(APPEND init_platform "include(\${CMAKE_CURRENT_LIST_DIR}/QtPublicWasmToolchainHelpers.cmake) diff --git a/coin/instructions/vxworks_testrunner.yaml b/coin/instructions/vxworks_testrunner.yaml index 6979b6807da..177a2ce7cf1 100644 --- a/coin/instructions/vxworks_testrunner.yaml +++ b/coin/instructions/vxworks_testrunner.yaml @@ -5,8 +5,41 @@ instructions: fileMode: 493 fileContents: | #!/bin/bash + + # Seconds of serial silence before triggering an SSH probe; also the cadence for repeated probes + EMPTY_READ_GRACE=60 + + # Maximum time (s) with no output while waiting; exceeding this triggers a timeout failure + MAX_WAIT_TIME=700 + quoted_args=`python3 -c 'import sys, shlex; print(shlex.join(sys.argv[2:]))' "$@"` + ssh_cmd() { + LD_LIBRARY_PATH=/usr/lib ssh -n -q -T \ + -o BatchMode=yes -o ConnectTimeout=1 -o HostKeyAlgorithms=+ssh-rsa \ + -o PreferredAuthentications=publickey \ + -o NumberOfPasswordPrompts=0 \ + ${VXWORKS_SSH} "$@" </dev/null + } + + ssh_probe_status() { + # Returns one of: FINISHED | RUNNING | UNRESPONSIVE + local out + out="$(ssh_cmd 'cmd rtp list; exit' 2>&1 || true)" + # If we don't see the normal SSH epilogue ("Au revoir!"), treat as UNRESPONSIVE + # (system is probably under a heavy load) + if ! printf '%s\n' "$out" | /usr/bin/grep -q 'Au revoir!'; then + echo "UNRESPONSIVE" + return 0 + fi + # Determine if our RTP is present; currently we match by 'tests' substring in 'rtp list' output + if printf '%s\n' "$out" | /usr/bin/grep -Fq "tests"; then + echo "RUNNING" + else + echo "FINISHED" + fi + } + testdir="$(dirname $1)" testexecutable="$1" echo RUNNING via serial: "$quoted_args" @@ -33,8 +66,16 @@ instructions: read -t 1 testline</tmp/guest.out fi - if [[ -z "$testline" ]]; then - echo "Restarting stuck emulator" + probe0="$(ssh_probe_status)" + + if [[ -z "$testline" || "$probe0" != "FINISHED" ]]; then + if [[ "$probe0" == "UNRESPONSIVE" ]]; then + echo "Restarting stuck emulator (SSH down)" + elif [[ "$probe0" == "RUNNING" ]]; then + echo "Restarting stuck emulator (RTP still running)" + else + echo "Restarting stuck emulator (serial down)" + fi pkill qemu-system rm /tmp/guest.in /tmp/guest.out mkfifo /tmp/guest.in /tmp/guest.out @@ -43,20 +84,57 @@ instructions: /bin/bash /home/qt/work/coin_vxworks_vars.sh sleep 1 else - echo "Emulator responding" + echo "VxWorks is functional" fi - # Empty output + # Empty out any pending serial output to start clean while read -t 1 line; do echo $line done < /tmp/guest.out - echo "cmd cd $testdir" > /tmp/guest.in - sleep 1 + echo "cmd cd $testdir; echo done" > /tmp/guest.in + + # Empty line before result of cd + read -t 1 line_cmd </tmp/guest.out + + if read -t 1 cdline </tmp/guest.out; then + cdline_clean=$(printf '%s' "$cdline" | tr -d '\r') + if printf '%s' "$cdline_clean" | /usr/bin/grep -Eq 'error\s*='; then + printf 'entering %s: %s\n' "$testdir" "$cdline_clean" + exit 1 + fi + fi + echo "</home/qt/work/vx.sh" > /tmp/guest.in - while read -t 600 line; do + silent_seconds=0 + last_probe_result="NONE" + + while true; do + if ! read -t $EMPTY_READ_GRACE line; then + silent_seconds=$((silent_seconds + EMPTY_READ_GRACE)) + + probe="$(ssh_probe_status)" + last_probe_result="$probe" + if [[ "$probe" == "UNRESPONSIVE" ]]; then + echo "system is under heavy load after ${silent_seconds}s of silence (SSH unresponsive)" + elif [[ "$probe" == "FINISHED" ]]; then + echo "RTP finished after ${silent_seconds}s of silence, ending" + exit 0 + fi + # RUNNING or UNRESPONSIVE - keep waiting + if (( silent_seconds >= MAX_WAIT_TIME )); then + echo "Timeout: no output for ${MAX_WAIT_TIME}s (last SSH probe: ${last_probe_result})" + exit 1 + fi + continue + fi + + # We received serial output + silent_seconds=0 + echo "$line" + if echo "$line" | /usr/bin/grep -q "qtest_in_vxworks_complete" then read -t 1 line</tmp/guest.out @@ -68,12 +146,12 @@ instructions: # Handle crashes if echo "$line" | /usr/bin/grep -qE "(SIGSEGV)|(SIGABRT)|(S_rtpLib_[A-Z_]+)" then - # Empty output pipe + # Drain the pipe for context, mark as crashed, and restart emulator next run. while read -t 1 line; do echo $line done < /tmp/guest.out echo "Test crashed" - pkill qemu-system # Kill emulator to force restart on next test start + pkill qemu-system exit 1 fi done < /tmp/guest.out diff --git a/examples/widgets/doc/src/draggableicons.qdoc b/examples/widgets/doc/src/draggableicons.qdoc index 0f1feacb7fb..3cf9b9b7266 100644 --- a/examples/widgets/doc/src/draggableicons.qdoc +++ b/examples/widgets/doc/src/draggableicons.qdoc @@ -4,6 +4,7 @@ /*! \example draganddrop/draggableicons \title Draggable Icons Example + \ingroup examples-user-input \examplecategory {User Interface Components} \brief The Draggable Icons example shows how to drag and drop image data between widgets diff --git a/examples/widgets/doc/src/draggabletext.qdoc b/examples/widgets/doc/src/draggabletext.qdoc index 3609ed5a1a1..f34e166ed89 100644 --- a/examples/widgets/doc/src/draggabletext.qdoc +++ b/examples/widgets/doc/src/draggabletext.qdoc @@ -4,6 +4,7 @@ /*! \example draganddrop/draggabletext \title Draggable Text Example + \ingroup examples-user-input \examplecategory {User Interface Components} \brief Illustrates how to drag and drop text between widgets. diff --git a/examples/widgets/doc/src/dropsite.qdoc b/examples/widgets/doc/src/dropsite.qdoc index 4c38ffc50d3..baa7b22fa8a 100644 --- a/examples/widgets/doc/src/dropsite.qdoc +++ b/examples/widgets/doc/src/dropsite.qdoc @@ -4,6 +4,7 @@ /*! \example draganddrop/dropsite \title Drop Site Example + \ingroup examples-user-input \examplecategory {User Interface Components} \brief The example shows how to distinguish the various MIME formats available diff --git a/examples/widgets/doc/src/tablet.qdoc b/examples/widgets/doc/src/tablet.qdoc index a18eb3249e7..f1238238df9 100644 --- a/examples/widgets/doc/src/tablet.qdoc +++ b/examples/widgets/doc/src/tablet.qdoc @@ -5,7 +5,7 @@ \example widgets/tablet \title Tablet Example \examplecategory {User Interface Components} - \ingroup examples-widgets + \ingroup examples-user-input \brief This example shows how to use a Wacom tablet in Qt applications. \image tabletexample.png {Application displaying a drawing area} diff --git a/examples/widgets/gallery/main.cpp b/examples/widgets/gallery/main.cpp index 95fffbdd3c1..b85706e1015 100644 --- a/examples/widgets/gallery/main.cpp +++ b/examples/widgets/gallery/main.cpp @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - app.styleHints()->setColorScheme(Qt::ColorScheme::Dark); + QGuiApplication::styleHints()->setColorScheme(Qt::ColorScheme::Dark); WidgetGallery gallery; gallery.show(); return QCoreApplication::exec(); diff --git a/examples/widgets/gallery/widgetgallery.cpp b/examples/widgets/gallery/widgetgallery.cpp index deef6b42181..e2ffec50e81 100644 --- a/examples/widgets/gallery/widgetgallery.cpp +++ b/examples/widgets/gallery/widgetgallery.cpp @@ -13,10 +13,10 @@ #include <QFileSystemModel> #include <QGridLayout> #include <QGroupBox> -#include <QMenu> #include <QLabel> #include <QLineEdit> #include <QListWidget> +#include <QMenu> #include <QPlainTextEdit> #include <QProgressBar> #include <QPushButton> @@ -26,26 +26,30 @@ #include <QSpinBox> #include <QStandardItemModel> #include <QStyle> -#include <QStyleHints> #include <QStyleFactory> -#include <QTextBrowser> -#include <QTreeView> +#include <QStyleHints> #include <QTableWidget> +#include <QTextBrowser> #include <QTextEdit> #include <QToolBox> #include <QToolButton> +#include <QTreeView> -#include <QIcon> #include <QDesktopServices> +#include <QIcon> #include <QScreen> #include <QWindow> +#include <QAnyStringView> #include <QDebug> #include <QLibraryInfo> +#include <QStringView> #include <QSysInfo> #include <QTextStream> #include <QTimer> +using namespace Qt::StringLiterals; + static inline QString className(const QObject *o) { return QString::fromUtf8(o->metaObject()->className()); @@ -56,7 +60,7 @@ static inline void setClassNameToolTip(QWidget *w) w->setToolTip(className(w)); } -static QString helpUrl(const QString &page) +static QString helpUrl(QStringView page) { QString result; QTextStream(&result) << "https://doc.qt.io/qt-" << QT_VERSION_MAJOR @@ -76,23 +80,23 @@ static void launchHelp(const QWidget *w) static void launchModuleHelp() { - QDesktopServices::openUrl(helpUrl(QLatin1String("qtwidgets-index"))); + QDesktopServices::openUrl(helpUrl(u"qtwidgets-index")); } template <class Widget> -Widget *createWidget(const char *name, QWidget *parent = nullptr) +Widget *createWidget(QAnyStringView name, QWidget *parent = nullptr) { - auto result = new Widget(parent); - result->setObjectName(QLatin1String(name)); + auto *result = new Widget(parent); + result->setObjectName(name); setClassNameToolTip(result); return result; } template <class Widget, class Parameter> -Widget *createWidget1(const Parameter &p1, const char *name, QWidget *parent = nullptr) +Widget *createWidget1(const Parameter &p1, QAnyStringView name, QWidget *parent = nullptr) { - auto result = new Widget(p1, parent); - result->setObjectName(QLatin1String(name)); + auto *result = new Widget(p1, parent); + result->setObjectName(name); setClassNameToolTip(result); return result; } @@ -108,9 +112,9 @@ static QString highDpiScaleFactorRoundingPolicy() { QString result; QDebug(&result) << QGuiApplication::highDpiScaleFactorRoundingPolicy(); - if (result.endsWith(QLatin1Char(')'))) + if (result.endsWith(u')')) result.chop(1); - const int lastSep = result.lastIndexOf(QLatin1String("::")); + const auto lastSep = result.lastIndexOf("::"_L1); if (lastSep != -1) result.remove(0, lastSep + 2); return result; @@ -122,10 +126,10 @@ WidgetGallery::WidgetGallery(QWidget *parent) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - auto styleComboBox = createWidget<QComboBox>("styleComboBox"); + auto *styleComboBox = createWidget<QComboBox>("styleComboBox"); const QString defaultStyleName = QApplication::style()->objectName(); QStringList styleNames = QStyleFactory::keys(); - for (int i = 1, size = styleNames.size(); i < size; ++i) { + for (qsizetype i = 1, size = styleNames.size(); i < size; ++i) { if (defaultStyleName.compare(styleNames.at(i), Qt::CaseInsensitive) == 0) { styleNames.swapItemsAt(0, i); break; @@ -133,16 +137,16 @@ WidgetGallery::WidgetGallery(QWidget *parent) } styleComboBox->addItems(styleNames); - auto styleLabel = createWidget1<QLabel>(tr("&Style:"), "styleLabel"); + auto *styleLabel = createWidget1<QLabel>(tr("&Style:"), "styleLabel"); styleLabel->setBuddy(styleComboBox); - auto colorSchemeComboBox = createWidget<QComboBox>("colorSchemeComboBox"); + auto *colorSchemeComboBox = createWidget<QComboBox>("colorSchemeComboBox"); colorSchemeComboBox->addItem(tr("Auto")); colorSchemeComboBox->addItem(tr("Light")); colorSchemeComboBox->addItem(tr("Dark")); colorSchemeComboBox->setCurrentIndex(static_cast<int>(qApp->styleHints()->colorScheme())); - auto colorSchemeLabel = createWidget1<QLabel>(tr("&Color Scheme:"), "colorSchemeLabel"); + auto *colorSchemeLabel = createWidget1<QLabel>(tr("&Color Scheme:"), "colorSchemeLabel"); colorSchemeLabel->setBuddy(colorSchemeComboBox); connect(colorSchemeComboBox, &QComboBox::currentIndexChanged, this, [](int index){ @@ -150,15 +154,16 @@ WidgetGallery::WidgetGallery(QWidget *parent) }); const QKeySequence helpKeySequence(QKeySequence::HelpContents); - auto helpLabel = createWidget1<QLabel>(tr("Press <kbd>%1</kbd> over a widget to see Documentation") - .arg(helpKeySequence.toString(QKeySequence::NativeText)), "helpLabel"); + const QString helpText = tr("Press <kbd>%1</kbd> over a widget to see Documentation") + .arg(helpKeySequence.toString(QKeySequence::NativeText)); + auto *helpLabel = createWidget1<QLabel>(helpText, "helpLabel"); - auto disableWidgetsCheckBox = createWidget1<QCheckBox>(tr("&Disable widgets"), "disableWidgetsCheckBox"); + auto *disableWidgetsCheckBox = createWidget1<QCheckBox>(tr("&Disable widgets"), "disableWidgetsCheckBox"); - auto buttonsGroupBox = createButtonsGroupBox(); - auto itemViewTabWidget = createItemViewTabWidget(); - auto simpleInputWidgetsGroupBox = createSimpleInputWidgetsGroupBox(); - auto textToolBox = createTextToolBox(); + auto *buttonsGroupBox = createButtonsGroupBox(); + auto *itemViewTabWidget = createItemViewTabWidget(); + auto *simpleInputWidgetsGroupBox = createSimpleInputWidgetsGroupBox(); + auto *textToolBox = createTextToolBox(); connect(styleComboBox, &QComboBox::textActivated, this, &WidgetGallery::changeStyle); @@ -171,8 +176,8 @@ WidgetGallery::WidgetGallery(QWidget *parent) connect(disableWidgetsCheckBox, &QCheckBox::toggled, simpleInputWidgetsGroupBox, &QWidget::setDisabled); - auto topLayout = new QHBoxLayout; - auto appearanceLayout = new QGridLayout; + auto *topLayout = new QHBoxLayout; + auto *appearanceLayout = new QGridLayout; appearanceLayout->addWidget(styleLabel, 0, 0); appearanceLayout->addWidget(styleComboBox, 0, 1); appearanceLayout->addWidget(colorSchemeLabel, 1, 0); @@ -183,12 +188,12 @@ WidgetGallery::WidgetGallery(QWidget *parent) topLayout->addStretch(1); topLayout->addWidget(disableWidgetsCheckBox); - auto dialogButtonBox = createWidget1<QDialogButtonBox>(QDialogButtonBox::Help | QDialogButtonBox::Close, - "dialogButtonBox"); + auto *dialogButtonBox = createWidget1<QDialogButtonBox>( + QDialogButtonBox::Help | QDialogButtonBox::Close, "dialogButtonBox"); connect(dialogButtonBox, &QDialogButtonBox::helpRequested, this, launchModuleHelp); connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - auto mainLayout = new QGridLayout(this); + auto *mainLayout = new QGridLayout(this); mainLayout->addLayout(topLayout, 0, 0, 1, 2); mainLayout->addWidget(buttonsGroupBox, 1, 0); mainLayout->addWidget(simpleInputWidgetsGroupBox, 1, 1); @@ -225,38 +230,38 @@ void WidgetGallery::advanceProgressBar() QGroupBox *WidgetGallery::createButtonsGroupBox() { - auto result = createWidget1<QGroupBox>(tr("Buttons"), "buttonsGroupBox"); + auto *result = createWidget1<QGroupBox>(tr("Buttons"), "buttonsGroupBox"); - auto defaultPushButton = createWidget1<QPushButton>(tr("Default Push Button"), "defaultPushButton"); + auto *defaultPushButton = createWidget1<QPushButton>(tr("Default Push Button"), "defaultPushButton"); defaultPushButton->setDefault(true); - auto togglePushButton = createWidget1<QPushButton>(tr("Toggle Push Button"), "togglePushButton"); + auto *togglePushButton = createWidget1<QPushButton>(tr("Toggle Push Button"), "togglePushButton"); togglePushButton->setCheckable(true); togglePushButton->setChecked(true); - auto flatPushButton = createWidget1<QPushButton>(tr("Flat Push Button"), "flatPushButton"); + auto *flatPushButton = createWidget1<QPushButton>(tr("Flat Push Button"), "flatPushButton"); flatPushButton->setFlat(true); - auto toolButton = createWidget<QToolButton>("toolButton"); + auto *toolButton = createWidget<QToolButton>("toolButton"); toolButton->setText(tr("Tool Button")); - auto menuToolButton = createWidget<QToolButton>("menuButton"); + auto *menuToolButton = createWidget<QToolButton>("menuButton"); menuToolButton->setText(tr("Menu Button")); - auto toolMenu = new QMenu(menuToolButton); + auto *toolMenu = new QMenu(menuToolButton); menuToolButton->setPopupMode(QToolButton::InstantPopup); toolMenu->addAction("Option"); toolMenu->addSeparator(); - auto action = toolMenu->addAction("Checkable Option"); + auto *action = toolMenu->addAction("Checkable Option"); action->setCheckable(true); menuToolButton->setMenu(toolMenu); - auto toolLayout = new QHBoxLayout; + auto *toolLayout = new QHBoxLayout; toolLayout->addWidget(toolButton); toolLayout->addWidget(menuToolButton); - auto commandLinkButton = createWidget1<QCommandLinkButton>(tr("Command Link Button"), "commandLinkButton"); + auto *commandLinkButton = createWidget1<QCommandLinkButton>(tr("Command Link Button"), "commandLinkButton"); commandLinkButton->setDescription(tr("Description")); - auto buttonLayout = new QVBoxLayout; + auto *buttonLayout = new QVBoxLayout; buttonLayout->addWidget(defaultPushButton); buttonLayout->addWidget(togglePushButton); buttonLayout->addWidget(flatPushButton); @@ -264,23 +269,23 @@ QGroupBox *WidgetGallery::createButtonsGroupBox() buttonLayout->addWidget(commandLinkButton); buttonLayout->addStretch(1); - auto radioButton1 = createWidget1<QRadioButton>(tr("Radio button 1"), "radioButton1"); - auto radioButton2 = createWidget1<QRadioButton>(tr("Radio button 2"), "radioButton2"); - auto radioButton3 = createWidget1<QRadioButton>(tr("Radio button 3"), "radioButton3"); + auto *radioButton1 = createWidget1<QRadioButton>(tr("Radio button 1"), "radioButton1"); + auto *radioButton2 = createWidget1<QRadioButton>(tr("Radio button 2"), "radioButton2"); + auto *radioButton3 = createWidget1<QRadioButton>(tr("Radio button 3"), "radioButton3"); radioButton1->setChecked(true); - auto checkBox = createWidget1<QCheckBox>(tr("Tri-state check box"), "checkBox"); + auto *checkBox = createWidget1<QCheckBox>(tr("Tri-state check box"), "checkBox"); checkBox->setTristate(true); checkBox->setCheckState(Qt::PartiallyChecked); - auto checkableLayout = new QVBoxLayout; + auto *checkableLayout = new QVBoxLayout; checkableLayout->addWidget(radioButton1); checkableLayout->addWidget(radioButton2); checkableLayout->addWidget(radioButton3); checkableLayout->addWidget(checkBox); checkableLayout->addStretch(1); - auto mainLayout = new QHBoxLayout(result); + auto *mainLayout = new QHBoxLayout(result); mainLayout->addLayout(buttonLayout); mainLayout->addLayout(checkableLayout); mainLayout->addStretch(); @@ -289,8 +294,8 @@ QGroupBox *WidgetGallery::createButtonsGroupBox() static QWidget *embedIntoHBoxLayout(QWidget *w, int margin = 5) { - auto result = new QWidget; - auto layout = new QHBoxLayout(result); + auto *result = new QWidget; + auto *layout = new QHBoxLayout(result); layout->setContentsMargins(margin, margin, margin, margin); layout->addWidget(w); return result; @@ -298,7 +303,7 @@ static QWidget *embedIntoHBoxLayout(QWidget *w, int margin = 5) QToolBox *WidgetGallery::createTextToolBox() { - auto result = createWidget<QToolBox>("toolBox"); + auto *result = createWidget<QToolBox>("toolBox"); const QString plainText = tr("Twinkle, twinkle, little star,\n" "How I wonder what you are.\n" @@ -307,13 +312,13 @@ QToolBox *WidgetGallery::createTextToolBox() "Twinkle, twinkle, little star,\n" "How I wonder what you are!\n"); // Create centered/italic HTML rich text - QString richText = QLatin1String("<html><head/><body><i>"); - for (const auto &line : QStringView{ plainText }.split(QLatin1Char('\n'))) - richText += QString::fromLatin1("<center>%1</center>").arg(line); - richText += QLatin1String("</i></body></html>"); + QString richText = "<html><head/><body><i>"_L1; + for (const auto &line : QStringView{ plainText }.split(u'\n')) + richText += "<center>"_L1 + line + "</center>"_L1; + richText += "</i></body></html>"_L1; - auto textEdit = createWidget1<QTextEdit>(richText, "textEdit"); - auto plainTextEdit = createWidget1<QPlainTextEdit>(plainText, "plainTextEdit"); + auto *textEdit = createWidget1<QTextEdit>(richText, "textEdit"); + auto *plainTextEdit = createWidget1<QPlainTextEdit>(plainText, "plainTextEdit"); systemInfoTextBrowser = createWidget<QTextBrowser>("systemInfoTextBrowser"); @@ -325,28 +330,28 @@ QToolBox *WidgetGallery::createTextToolBox() QTabWidget *WidgetGallery::createItemViewTabWidget() { - auto result = createWidget<QTabWidget>("bottomLeftTabWidget"); + auto *result = createWidget<QTabWidget>("bottomLeftTabWidget"); result->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored); - auto treeView = createWidget<QTreeView>("treeView"); - auto fileSystemModel = new QFileSystemModel(treeView); + auto *treeView = createWidget<QTreeView>("treeView"); + auto *fileSystemModel = new QFileSystemModel(treeView); fileSystemModel->setRootPath(QDir::rootPath()); treeView->setModel(fileSystemModel); - auto tableWidget = createWidget<QTableWidget>("tableWidget"); + auto *tableWidget = createWidget<QTableWidget>("tableWidget"); tableWidget->setRowCount(10); tableWidget->setColumnCount(10); - auto listModel = new QStandardItemModel(0, 1, result); - listModel->appendRow(new QStandardItem(QIcon(QLatin1String(":/qt-project.org/styles/commonstyle/images/diropen-128.png")), + auto *listModel = new QStandardItemModel(0, 1, result); + listModel->appendRow(new QStandardItem(QIcon(":/qt-project.org/styles/commonstyle/images/diropen-128.png"_L1), tr("Directory"))); - listModel->appendRow(new QStandardItem(QIcon(QLatin1String(":/qt-project.org/styles/commonstyle/images/computer-32.png")), + listModel->appendRow(new QStandardItem(QIcon(":/qt-project.org/styles/commonstyle/images/computer-32.png"_L1), tr("Computer"))); - auto listView = createWidget<QListView>("listView"); + auto *listView = createWidget<QListView>("listView"); listView->setModel(listModel); - auto iconModeListView = createWidget<QListView>("iconModeListView"); + auto *iconModeListView = createWidget<QListView>("iconModeListView"); iconModeListView->setViewMode(QListView::IconMode); iconModeListView->setModel(listModel); @@ -359,34 +364,34 @@ QTabWidget *WidgetGallery::createItemViewTabWidget() QGroupBox *WidgetGallery::createSimpleInputWidgetsGroupBox() { - auto result = createWidget1<QGroupBox>(tr("Simple Input Widgets"), "bottomRightGroupBox"); + auto *result = createWidget1<QGroupBox>(tr("Simple Input Widgets"), "bottomRightGroupBox"); result->setCheckable(true); result->setChecked(true); - auto lineEdit = createWidget1<QLineEdit>("s3cRe7", "lineEdit"); + auto *lineEdit = createWidget1<QLineEdit>("s3cRe7", "lineEdit"); lineEdit->setClearButtonEnabled(true); lineEdit->setEchoMode(QLineEdit::Password); - auto spinBox = createWidget<QSpinBox>("spinBox", result); + auto *spinBox = createWidget<QSpinBox>("spinBox", result); spinBox->setValue(50); - auto dateTimeEdit = createWidget<QDateTimeEdit>("dateTimeEdit", result); + auto *dateTimeEdit = createWidget<QDateTimeEdit>("dateTimeEdit", result); dateTimeEdit->setDateTime(QDateTime::currentDateTime()); - auto slider = createWidget<QSlider>("slider", result); + auto *slider = createWidget<QSlider>("slider", result); slider->setOrientation(Qt::Horizontal); slider->setValue(40); - auto scrollBar = createWidget<QScrollBar>("scrollBar", result); + auto *scrollBar = createWidget<QScrollBar>("scrollBar", result); scrollBar->setOrientation(Qt::Horizontal); setClassNameToolTip(scrollBar); scrollBar->setValue(60); - auto dial = createWidget<QDial>("dial", result); + auto *dial = createWidget<QDial>("dial", result); dial->setValue(30); dial->setNotchesVisible(true); - auto layout = new QGridLayout(result); + auto *layout = new QGridLayout(result); layout->addWidget(lineEdit, 0, 0, 1, 2); layout->addWidget(spinBox, 1, 0, 1, 2); layout->addWidget(dateTimeEdit, 2, 0, 1, 2); @@ -399,11 +404,11 @@ QGroupBox *WidgetGallery::createSimpleInputWidgetsGroupBox() QProgressBar *WidgetGallery::createProgressBar() { - auto result = createWidget<QProgressBar>("progressBar"); + auto *result = createWidget<QProgressBar>("progressBar"); result->setRange(0, 10000); result->setValue(0); - auto timer = new QTimer(this); + auto *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &WidgetGallery::advanceProgressBar); timer->start(1000); return result; @@ -414,11 +419,12 @@ 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(); - for (auto screen : screens) { + for (const auto *screen : screens) { const bool current = screen == this->screen(); str << "<li>"; if (current) @@ -437,9 +443,9 @@ void WidgetGallery::updateSystemInfo() void WidgetGallery::helpOnCurrentWidget() { // Skip over internal widgets - for (auto w = QApplication::widgetAt(QCursor::pos(screen())); w; w = w->parentWidget()) { + for (const auto *w = QApplication::widgetAt(QCursor::pos(screen())); w; w = w->parentWidget()) { const QString name = w->objectName(); - if (!name.isEmpty() && !name.startsWith(QLatin1String("qt_"))) { + if (!name.isEmpty() && !name.startsWith("qt_"_L1)) { launchHelp(w); break; } diff --git a/examples/widgets/gallery/widgetgallery.h b/examples/widgets/gallery/widgetgallery.h index c96437a4667..f220847a0cd 100644 --- a/examples/widgets/gallery/widgetgallery.h +++ b/examples/widgets/gallery/widgetgallery.h @@ -37,7 +37,7 @@ private: QProgressBar *createProgressBar(); QProgressBar *progressBar; - QTextBrowser *systemInfoTextBrowser; + QTextBrowser *systemInfoTextBrowser{}; }; #endif // WIDGETGALLERY_H diff --git a/examples/widgets/gestures/imagegestures/doc/src/imagegestures.qdoc b/examples/widgets/gestures/imagegestures/doc/src/imagegestures.qdoc index 03b31edabc5..63ca14787b6 100644 --- a/examples/widgets/gestures/imagegestures/doc/src/imagegestures.qdoc +++ b/examples/widgets/gestures/imagegestures/doc/src/imagegestures.qdoc @@ -5,6 +5,7 @@ \example gestures/imagegestures \title Image Gestures Example \examplecategory {User Interface Components} + \ingroup examples-user-input \brief Demonstrates the use of simple gestures in a widget. This example shows how to enable gestures for a widget and use gesture input diff --git a/examples/widgets/touch/knobs/doc/src/touch-knobs.qdoc b/examples/widgets/touch/knobs/doc/src/touch-knobs.qdoc index d229f54f5c7..38bd2016cab 100644 --- a/examples/widgets/touch/knobs/doc/src/touch-knobs.qdoc +++ b/examples/widgets/touch/knobs/doc/src/touch-knobs.qdoc @@ -6,6 +6,7 @@ \title Touch Knobs Example \examplecategory {User Interface Components} \ingroup touchinputexamples + \ingroup examples-user-input \brief Shows how to create custom controls that accept touch input. The Touch Knobs example shows how to create custom controls that 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/qobject.cpp b/src/corelib/kernel/qobject.cpp index fe510898429..02c9f00f301 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -1894,8 +1894,8 @@ int QObject::startTimer(int interval, Qt::TimerType timerType) A timer event will occur every \a interval until killTimer() is called. If \a interval is equal to \c{std::chrono::duration::zero()}, - then the timer event occurs once every time there are no more window - system events to process. + then the timer event occurs once every time control returns to the event + loop, that is, there are no more native window system events to process. \include timers-common.qdocinc negative-intervals-not-allowed @@ -2715,6 +2715,20 @@ static void err_info_about_objects(const char *func, const QObject *sender, cons qCWarning(lcConnect, "QObject::%s: (receiver name: '%s')", func, b.toLocal8Bit().data()); } +Q_DECL_COLD_FUNCTION +static void connectWarning(const QObject *sender, + const QMetaObject *senderMetaObject, + const QObject *receiver, + const char *message) +{ + const char *senderString = sender ? sender->metaObject()->className() + : senderMetaObject ? senderMetaObject->className() + : "Unknown"; + const char *receiverString = receiver ? receiver->metaObject()->className() + : "Unknown"; + qCWarning(lcConnect, "QObject::connect(%s, %s): %s", senderString, receiverString, message); +} + /*! Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; otherwise it returns \nullptr. The pointer @@ -4105,8 +4119,9 @@ QMetaObject::Connection QMetaObject::connectImpl(const QObject *sender, const QM { QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw); + const QMetaObject *senderMetaObject = sender->metaObject(); if (!signal.isValid() || signal.methodType() != QMetaMethod::Signal) { - qCWarning(lcConnect, "QObject::connect: invalid signal parameter"); + connectWarning(sender, senderMetaObject, receiver, "invalid signal parameter"); return QMetaObject::Connection(); } @@ -4116,7 +4131,6 @@ QMetaObject::Connection QMetaObject::connectImpl(const QObject *sender, const QM QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); } - const QMetaObject *senderMetaObject = sender->metaObject(); if (signal_index == -1) { qCWarning(lcConnect, "QObject::connect: Can't find signal %s on instance of class %s", signal.methodSignature().constData(), senderMetaObject->className()); @@ -5433,7 +5447,7 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa { QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw); if (!signal) { - qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter"); + connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter"); return QMetaObject::Connection(); } @@ -5445,26 +5459,13 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa break; } if (!senderMetaObject) { - qCWarning(lcConnect, "QObject::connect: signal not found in %s", sender->metaObject()->className()); + connectWarning(sender, senderMetaObject, receiver, "signal not found"); return QMetaObject::Connection(nullptr); } signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject); return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj.release(), type, types, senderMetaObject); } -static void connectWarning(const QObject *sender, - const QMetaObject *senderMetaObject, - const QObject *receiver, - const char *message) -{ - const char *senderString = sender ? sender->metaObject()->className() - : senderMetaObject ? senderMetaObject->className() - : "Unknown"; - const char *receiverString = receiver ? receiver->metaObject()->className() - : "Unknown"; - qCWarning(lcConnect, "QObject::connect(%s, %s): %s", senderString, receiverString, message); -} - /*! \internal @@ -5495,7 +5496,7 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s QOrderedMutexLocker locker(signalSlotLock(sender), signalSlotLock(receiver)); - if (type & Qt::UniqueConnection && slot) { + if (type & Qt::UniqueConnection) { QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed(); if (connections && connections->signalVectorCount() > signal_index) { const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed(); @@ -5683,7 +5684,7 @@ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signa { QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw); if (!sender) { - qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter"); + connectWarning(sender, nullptr, receiver, "invalid nullptr parameter"); return QMetaObject::Connection(); } const QMetaObject *senderMetaObject = sender->metaObject(); 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/platform/android/qandroidnativeinterface.cpp b/src/corelib/platform/android/qandroidnativeinterface.cpp index 680725dc6a1..5771d28f3a9 100644 --- a/src/corelib/platform/android/qandroidnativeinterface.cpp +++ b/src/corelib/platform/android/qandroidnativeinterface.cpp @@ -48,8 +48,9 @@ QT_DEFINE_NATIVE_INTERFACE(QAndroidApplication); /*! \fn QJniObject QNativeInterface::QAndroidApplication::context() - Returns the Android context as a \c QJniObject. The context is an \c Activity - if the most recently started activity object is valid. Otherwise, the context is a \c Service. + Returns the Android context as a \c QtJniTypes::Context. The context + is an \c Activity if the most recently started activity object is valid. + Otherwise, the context is a \c Service. \since 6.2 */ 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.cpp b/src/corelib/time/qtimezoneprivate.cpp index 5b61940cbbe..d8434f4fe1b 100644 --- a/src/corelib/time/qtimezoneprivate.cpp +++ b/src/corelib/time/qtimezoneprivate.cpp @@ -1081,12 +1081,6 @@ QList<QByteArray> QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windows return list; } -// Define template for derived classes to reimplement so QSharedDataPointer clone() works correctly -template<> QTimeZonePrivate *QSharedDataPointer<QTimeZonePrivate>::clone() -{ - return d->clone(); -} - static bool isEntryInIanaList(QByteArrayView id, QByteArrayView ianaIds) { qsizetype cut; diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h index 611c8e4b5e7..b1217402ce7 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); @@ -209,8 +209,6 @@ protected: }; Q_DECLARE_TYPEINFO(QTimeZonePrivate::Data, Q_RELOCATABLE_TYPE); -template<> QTimeZonePrivate *QSharedDataPointer<QTimeZonePrivate>::clone(); - class Q_AUTOTEST_EXPORT QUtcTimeZonePrivate final : public QTimeZonePrivate { bool operator=(const QUtcTimeZonePrivate &) const = delete; 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/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index d1e76b250bb..de749ee112e 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -413,14 +413,14 @@ qHashMultiCommutative(size_t seed, const T &... args) template <typename InputIterator> inline size_t qHashRange(InputIterator first, InputIterator last, size_t seed = 0) - noexcept(noexcept(qHash(*first))) // assume iterator operations don't throw + noexcept(noexcept(qHash(*first, 0))) // assume iterator operations don't throw { return std::accumulate(first, last, seed, QtPrivate::QHashCombine(seed)); } template <typename InputIterator> inline size_t qHashRangeCommutative(InputIterator first, InputIterator last, size_t seed = 0) - noexcept(noexcept(qHash(*first))) // assume iterator operations don't throw + noexcept(noexcept(qHash(*first, 0))) // assume iterator operations don't throw { return std::accumulate(first, last, seed, QtPrivate::QHashCombineCommutative(seed)); } diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 7a93ec688ef..a11f7913dc7 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,10 +576,15 @@ 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()); } + { + if (l.size()) + return assign(l.begin(), l.end()); + clear(); + return *this; + } template <typename ...Args> iterator emplace(const_iterator before, Args&&... args) 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/images/plaintext-layout.png b/src/gui/doc/images/plaintext-layout.png Binary files differdeleted file mode 100644 index 9172d7a044d..00000000000 --- a/src/gui/doc/images/plaintext-layout.png +++ /dev/null diff --git a/src/gui/doc/images/plaintext-layout.webp b/src/gui/doc/images/plaintext-layout.webp Binary files differnew file mode 100644 index 00000000000..b8266ec5a8c --- /dev/null +++ b/src/gui/doc/images/plaintext-layout.webp 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/doc/src/richtext.qdoc b/src/gui/doc/src/richtext.qdoc index 429233ec8f1..2fa49a31e03 100644 --- a/src/gui/doc/src/richtext.qdoc +++ b/src/gui/doc/src/richtext.qdoc @@ -650,7 +650,7 @@ the QTextLayout class, to help developers perform word-wrapping and layout tasks without the need to create a document first. - \image plaintext-layout.png {Screenshot of a text that flows around a + \image plaintext-layout.webp {Screenshot of a text that flows around a circle.} Formatting and drawing a paragraph of plain text is straightforward. 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..518843ffcbd 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -3407,6 +3407,15 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E return; QWindowPrivate *p = qt_window_private(window); + if (e->isExposed) { + // If the window has been automatically positioned or resized by the + // window manager, we now assume those have taken effect, even for + // asynchronous window managers. From this point on we want the window + // to keep its geometry, even when recreated. + p->positionAutomatic = false; + p->resizeAutomatic = false; + } + if (!p->receivedExpose) { if (p->resizeEventPending) { // as a convenience for plugins, send a resize event before the first expose event if they haven't done so 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/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index c23be8eb3ad..bad5932c457 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -582,12 +582,6 @@ void QWindowPrivate::create(bool recursive) platformWindow->initialize(); - // Now that the window is created and initialized the platform has had - // a chance to position and size it automatically. From this point on - // we want the window to keep its geometry, even when recreated. - positionAutomatic = false; - resizeAutomatic = false; - QObjectList childObjects = q->children(); for (int i = 0; i < childObjects.size(); i ++) { QObject *object = childObjects.at(i); 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/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 5763696266a..38c4a9ea87f 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -2937,6 +2937,8 @@ qreal QPainterPath::percentAtLength(qreal len) const if (len > totalLength) return 1; + Q_ASSERT(totalLength != 0); + if (d->cacheEnabled) { const int ei = qMax(d->elementAtT(len / totalLength), 1); // Skip initial MoveTo qreal res = 0; @@ -3117,6 +3119,7 @@ QPointF QPainterPath::pointAtPercent(qreal t) const qreal curLen = 0; qreal bezierLen = 0; QBezier b = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen); + Q_ASSERT(bezierLen != 0); qreal realT = (totalLength * t - curLen) / bezierLen; return b.pointAt(qBound(qreal(0), realT, qreal(1))); @@ -3141,10 +3144,14 @@ qreal QPainterPath::angleAtPercent(qreal t) const return 0; } + if (isEmpty()) + return 0; + qreal totalLength = length(); qreal curLen = 0; qreal bezierLen = 0; QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen); + Q_ASSERT(bezierLen != 0); qreal realT = (totalLength * t - curLen) / bezierLen; qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4); @@ -3170,10 +3177,14 @@ qreal QPainterPath::slopeAtPercent(qreal t) const return 0; } + if (isEmpty()) + return 0; + qreal totalLength = length(); qreal curLen = 0; qreal bezierLen = 0; QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen); + Q_ASSERT(bezierLen != 0); qreal realT = (totalLength * t - curLen) / bezierLen; qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4); 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/platform/darwin/qappleiconengine.mm b/src/gui/platform/darwin/qappleiconengine.mm index 20d50b0d8a8..3228b97fdb0 100644 --- a/src/gui/platform/darwin/qappleiconengine.mm +++ b/src/gui/platform/darwin/qappleiconengine.mm @@ -30,27 +30,28 @@ auto *loadImage(const QString &iconName) {"call-start"_L1, @"phone.arrow.up.right"}, {"call-stop"_L1, @"phone.down"}, {"contact-new"_L1, @"person.crop.circle.badge.plus"}, - {"document-new"_L1, @"doc.badge.plus"}, - {"document-open"_L1, @"folder"}, - {"document-open-recent"_L1, @"doc.badge.clock"}, - {"document-page-setup"_L1, @"doc.badge.gearshape"}, + {"document-new"_L1, @"plus"}, + {"document-open"_L1, @"arrow.up.forward"}, + {"document-open-recent"_L1, @"clock"}, + {"document-page-setup"_L1, @"text.page"}, {"document-print"_L1, @"printer"}, - //{"document-print-preview"_L1, @""}, - {"document-properties"_L1, @"doc.badge.ellipsis"}, - //{"document-revert"_L1, @""}, + {"document-print-preview"_L1, @"richtext.page"}, + {"document-properties"_L1, @"info.circle.text.page"}, + {"document-revert"_L1, @"arrow.counterclockwise"}, {"document-save"_L1, @"square.and.arrow.down"}, - //{"document-save-as"_L1, @""}, + {"document-save-as"_L1, @"square.and.arrow.down.on.square"}, {"document-send"_L1, @"paperplane"}, + {"document-export"_L1, @"square.and.arrow.up"}, {"edit-clear"_L1, @"xmark.circle"}, {"edit-copy"_L1, @"doc.on.doc"}, {"edit-cut"_L1, @"scissors"}, - {"edit-delete"_L1, @"delete.left"}, - {"edit-find"_L1, @"magnifyingglass"}, + {"edit-delete"_L1, @"trash"}, + {"edit-find"_L1, @"text.page.badge.magnifyingglass"}, //{"edit-find-replace"_L1, @"arrow.up.left.and.down.right.magnifyingglass"}, - {"edit-paste"_L1, @"clipboard"}, - {"edit-redo"_L1, @"arrowshape.turn.up.right"}, - //{"edit-select-all"_L1, @""}, - {"edit-undo"_L1, @"arrowshape.turn.up.left"}, + {"edit-paste"_L1, @"document.on.clipboard"}, + {"edit-redo"_L1, @"arrow.uturn.forward"}, + {"edit-select-all"_L1, @"character.textbox"}, + {"edit-undo"_L1, @"arrow.uturn.backward"}, {"folder-new"_L1, @"folder.badge.plus"}, {"format-indent-less"_L1, @"decrease.indent"}, {"format-indent-more"_L1, @"increase.indent"}, @@ -65,35 +66,36 @@ auto *loadImage(const QString &iconName) {"format-text-underline"_L1, @"underline"}, {"format-text-strikethrough"_L1, @"strikethrough"}, //{"go-bottom"_L1, @""}, - {"go-down"_L1, @"arrowshape.down"}, - {"go-first"_L1, @"increase.indent"}, + {"go-down"_L1, @"chevron.down"}, + //{"go-first"_L1, @""}, {"go-home"_L1, @"house"}, //{"go-jump"_L1, @""}, //{"go-last"_L1, @""}, - {"go-next"_L1, @"arrowshape.right"}, - {"go-previous"_L1, @"arrowshape.left"}, + {"go-next"_L1, @"chevron.right"}, + {"go-previous"_L1, @"chevron.left"}, //{"go-top"_L1, @""}, - {"go-up"_L1, @"arrowshape.up"}, + {"go-up"_L1, @"chevron.up"}, {"help-about"_L1, @"info.circle"}, //{"help-contents"_L1, @""}, {"help-faq"_L1, @"questionmark.app"}, {"insert-image"_L1, @"photo.badge.plus"}, {"insert-link"_L1, @"link.badge.plus"}, - //{"insert-object"_L1, @""}, - {"insert-text"_L1, @"textformat"}, + {"insert-object"_L1, @"widget.small.badge.plus"}, + {"insert-text"_L1, @"text.badge.plus"}, {"list-add"_L1, @"plus.circle"}, {"list-remove"_L1, @"minus.circle"}, {"mail-forward"_L1, @"arrowshape.turn.up.right"}, {"mail-mark-important"_L1, @"star"}, {"mail-mark-junk"_L1, @"xmark.bin"}, - {"mail-mark-notjunk"_L1, @"trash.slash"}, + {"mail-mark-notjunk"_L1, @"arrow.up.bin"}, {"mail-mark-read"_L1, @"envelope.open"}, - {"mail-mark-unread"_L1, @"envelope.fill"}, + {"mail-mark-unread"_L1, @"envelope.badge"}, {"mail-message-new"_L1, @"square.and.pencil"}, {"mail-reply-all"_L1, @"arrowshape.turn.up.left.2"}, {"mail-reply-sender"_L1, @"arrowshape.turn.up.left"}, - {"mail-send"_L1, @"paperplane"}, - {"mail-send-receive"_L1, @"envelope.arrow.triangle.branch"}, + {"mail-send"_L1, @"arrow.up.circle.fill"}, + {"mail-send-receive"_L1, @"arrow.trianglehead.2.clockwise.rotate.90"}, + {"mail-replied"_L1, @"envelope.and.arrow.trianglehead.branch"}, {"media-eject"_L1, @"eject"}, {"media-playback-pause"_L1, @"pause"}, {"media-playback-start"_L1, @"play"}, @@ -108,61 +110,61 @@ auto *loadImage(const QString &iconName) {"object-rotate-left"_L1, @"rotate.left"}, {"object-rotate-right"_L1, @"rotate.right"}, {"process-stop"_L1, @"stop.circle"}, - {"system-lock-screen"_L1, @"lock.display"}, - {"system-log-out"_L1, @"door.left.hand.open"}, + {"system-lock-screen"_L1, @"lock"}, + {"system-log-out"_L1, @"person.crop.circle"}, //{"system-run"_L1, @""}, {"system-search"_L1, @"magnifyingglass"}, - //{"system-reboot"_L1, @""}, + {"system-reboot"_L1, @"restart"}, {"system-shutdown"_L1, @"power"}, - //{"tools-check-spelling"_L1, @""}, + {"tools-check-spelling"_L1, @"textformat.characters.dottedunderline"}, {"view-fullscreen"_L1, @"arrow.up.left.and.arrow.down.right"}, {"view-refresh"_L1, @"arrow.clockwise"}, {"view-restore"_L1, @"arrow.down.right.and.arrow.up.left"}, - //{"view-sort-ascending"_L1, @""}, - //{"view-sort-descending"_L1, @""}, + {"view-sort-ascending"_L1, @"arrow.up"}, + {"view-sort-descending"_L1, @"arrow.down"}, {"window-close"_L1, @"xmark.circle"}, {"window-new"_L1, @"macwindow.badge.plus"}, {"zoom-fit-best"_L1, @"square.arrowtriangle.4.outward"}, {"zoom-in"_L1, @"plus.magnifyingglass"}, - //{"zoom-original"_L1, @""}, + {"zoom-original"_L1, @"1.magnifyingglass"}, {"zoom-out"_L1, @"minus.magnifyingglass"}, {"process-working"_L1, @"circle.dotted"}, - //{"accessories-calculator"_L1, @""}, - //{"accessories-character-map"_L1, @""}, + {"accessories-calculator"_L1, @"plus.forwardslash.minus"}, + {"accessories-character-map"_L1, @"keyboard.macwindow"}, {"accessories-dictionary"_L1, @"character.book.closed"}, {"accessories-text-editor"_L1, @"textformat"}, {"help-browser"_L1, @"folder.badge.questionmark"}, {"multimedia-volume-control"_L1, @"speaker.wave.3"}, {"preferences-desktop-accessibility"_L1, @"accessibility"}, - //{"preferences-desktop-font"_L1, @""}, - {"preferences-desktop-keyboard"_L1, @"keyboard.badge.ellipsis"}, - //{"preferences-desktop-locale"_L1, @""}, - //{"preferences-desktop-multimedia"_L1, @""}, - //{"preferences-desktop-screensaver"_L1, @""}, - //{"preferences-desktop-theme"_L1, @""}, - //{"preferences-desktop-wallpaper"_L1, @""}, + {"preferences-desktop-font"_L1, @"textformat.characters"}, + {"preferences-desktop-keyboard"_L1, @"keyboard"}, + {"preferences-desktop-locale"_L1, @"mappin.and.ellipse"}, + {"preferences-desktop-multimedia"_L1, @"music.note.tv"}, + {"preferences-desktop-screensaver"_L1, @"display"}, + {"preferences-desktop-theme"_L1, @"paintpalette"}, + {"preferences-desktop-wallpaper"_L1, @"photo.tv"}, {"system-file-manager"_L1, @"folder.badge.gearshape"}, - //{"system-software-install"_L1, @""}, - //{"system-software-update"_L1, @""}, d - //{"utilities-system-monitor"_L1, @""}, + {"system-software-install"_L1, @"laptopcomputer.and.arrow.down"}, + {"system-software-update"_L1, @"laptopcomputer.badge.checkmark"}, + {"utilities-system-monitor"_L1, @"chart.bar.xaxis"}, {"utilities-terminal"_L1, @"apple.terminal"}, - //{"applications-accessories"_L1, @""}, - //{"applications-development"_L1, @""}, - //{"applications-engineering"_L1, @""}, - {"applications-games"_L1, @"gamecontroller"}, - //{"applications-graphics"_L1, @""}, + {"applications-accessories"_L1, @"scanner"}, + {"applications-development"_L1, @"hammer"}, + {"applications-engineering"_L1, @"wrench.and.screwdriver"}, + {"applications-games"_L1, @"gamecontroller.circle"}, + {"applications-graphics"_L1, @"theatermask.and.paintbrush"}, {"applications-internet"_L1, @"network"}, {"applications-multimedia"_L1, @"tv.and.mediabox"}, - //{"applications-office"_L1, @""}, - //{"applications-other"_L1, @""}, + {"applications-office"_L1, @"square.and.pencil.circle"}, + {"applications-other"_L1, @"app.grid"}, {"applications-science"_L1, @"atom"}, - //{"applications-system"_L1, @""}, - //{"applications-utilities"_L1, @""}, + {"applications-system"_L1, @"gear"}, + {"applications-utilities"_L1, @"wrench.and.screwdriver"}, {"preferences-desktop"_L1, @"menubar.dock.rectangle"}, - //{"preferences-desktop-peripherals"_L1, @""}, - //{"preferences-desktop-personal"_L1, @""}, - //{"preferences-other"_L1, @""}, - //{"preferences-system"_L1, @""}, + {"preferences-desktop-peripherals"_L1, @"iphone.and.ipod"}, + {"preferences-desktop-personal"_L1, @"person.badge.key"}, + {"preferences-other"_L1, @"gear"}, + {"preferences-system"_L1, @"gear"}, {"preferences-system-network"_L1, @"network"}, {"system-help"_L1, @"questionmark.diamond"}, {"audio-card"_L1, @"waveform.circle"}, @@ -175,40 +177,40 @@ auto *loadImage(const QString &iconName) {"drive-harddisk"_L1, @"internaldrive"}, {"drive-optical"_L1, @"opticaldiscdrive"}, {"drive-removable-media"_L1, @"externaldrive"}, - {"input-gaming"_L1, @"gamecontroller"}, // "games" also using this one + {"input-gaming"_L1, @"gamecontroller"}, {"input-keyboard"_L1, @"keyboard"}, {"input-mouse"_L1, @"computermouse"}, {"input-tablet"_L1, @"ipad"}, {"media-flash"_L1, @"mediastick"}, //{"media-floppy"_L1, @""}, - //{"media-optical"_L1, @""}, + {"media-optical"_L1, @"opticaldisc"}, {"media-tape"_L1, @"recordingtape"}, - //{"modem"_L1, @""}, - {"multimedia-player"_L1, @"play.rectangle"}, + {"modem"_L1, @"phone.connection"}, + {"multimedia-player"_L1, @"ipod"}, {"network-wired"_L1, @"app.connected.to.app.below.fill"}, {"network-wireless"_L1, @"wifi"}, {"network-workgroup"_L1, @"network"}, - //{"pda"_L1, @""}, + {"pda"_L1, @"flipphone"}, {"phone"_L1, @"iphone"}, {"printer"_L1, @"printer"}, {"scanner"_L1, @"scanner"}, {"video-display"_L1, @"play.display"}, - //{"emblem-default"_L1, @""}, + {"emblem-default"_L1, @"circle"}, {"emblem-documents"_L1, @"doc.circle"}, {"emblem-downloads"_L1, @"arrow.down.circle"}, - {"emblem-favorite"_L1, @"star"}, - {"emblem-important"_L1, @"exclamationmark.bubble.circle"}, - {"emblem-mail"_L1, @"envelope"}, - {"emblem-photos"_L1, @"photo.stack"}, - //{"emblem-readonly"_L1, @""}, - {"emblem-shared"_L1, @"folder.badge.person.crop"}, + {"emblem-favorite"_L1, @"star.circle"}, + {"emblem-important"_L1, @"exclamationmark.circle"}, + {"emblem-mail"_L1, @"envelope.circle"}, + {"emblem-photos"_L1, @"photo.circle"}, + {"emblem-readonly"_L1, @"eye.circle"}, + {"emblem-shared"_L1, @"person.2.circle"}, {"emblem-symbolic-link"_L1, @"link.circle"}, {"emblem-synchronized"_L1, @"arrow.triangle.2.circlepath.circle"}, - {"emblem-system"_L1, @"gear"}, - //{"emblem-unreadable"_L1, @""}, + {"emblem-system"_L1, @"gear.circle"}, + {"emblem-unreadable"_L1, @"eye.slash.circle"}, {"text-x-generic"_L1, @"doc"}, // until iOS 18/macOS 15; @"document" after that {"folder"_L1, @"folder"}, - //{"folder-remote"_L1, @""}, + {"folder-remote"_L1, @"icloud"}, {"network-server"_L1, @"server.rack"}, //{"start-here"_L1, @""}, {"user-bookmarks"_L1, @"bookmark.circle"}, @@ -229,28 +231,28 @@ auto *loadImage(const QString &iconName) {"dialog-question"_L1, @"questionmark.circle"}, {"dialog-warning"_L1, @"exclamationmark.octagon"}, {"folder-drag-accept"_L1, @"plus.rectangle.on.folder"}, - //{"folder-open"_L1, @""}, + {"folder-open"_L1, @"arrow.forward.folder"}, {"folder-visiting"_L1, @"folder.circle"}, - {"image-loading"_L1, @"photo.circle"}, - {"image-missing"_L1, @"photo"}, + {"image-loading"_L1, @"photo.badge.arrow.down"}, + {"image-missing"_L1, @"photo.badge.exclamationmark"}, {"mail-attachment"_L1, @"paperclip"}, {"mail-unread"_L1, @"envelope.badge"}, {"mail-read"_L1, @"envelope.open"}, - {"mail-replied"_L1, @"arrowshape.turn.up.left"}, + //{"mail-replied"_L1, @""}, //{"mail-signed"_L1, @""}, - //{"mail-signed-verified"_L1, @""}, - {"media-playlist-repeat"_L1, @"repet"}, + {"mail-signed-verified"_L1, @"envelope.badge.shield.half.filled"}, + {"media-playlist-repeat"_L1, @"repeat"}, {"media-playlist-shuffle"_L1, @"shuffle"}, - //{"network-error"_L1, @""}, - //{"network-idle"_L1, @""}, - {"network-offline"_L1, @"network.slash"}, - //{"network-receive"_L1, @""}, - //{"network-transmit"_L1, @""}, - //{"network-transmit-receive"_L1, @""}, + {"network-error"_L1, @"xmark.icloud"}, + {"network-idle"_L1, @"icloud"}, + {"network-offline"_L1, @"icloud.slash"}, + {"network-receive"_L1, @"icloud.and.arrow.down"}, + {"network-transmit"_L1, @"icloud.and.arrow.up"}, + {"network-transmit-receive"_L1, @"arrow.trianglehead.2.clockwise.rotate.90.icloud"}, //{"printer-error"_L1, @""}, - {"printer-printing"_L1, @"printer.dotmatrix.filled.and.paper"}, // not sure - {"security-high"_L1, @"lock.shield"}, - //{"security-medium"_L1, @""}, + {"printer-printing"_L1, @"printer.dotmatrix"}, + {"security-high"_L1, @"lock.badge.checkmark"}, + {"security-medium"_L1, @"lock"}, {"security-low"_L1, @"lock.trianglebadge.exclamationmark"}, {"software-update-available"_L1, @"arrowshape.up.circle"}, {"software-update-urgent"_L1, @"exclamationmark.transmission"}, @@ -258,9 +260,9 @@ auto *loadImage(const QString &iconName) {"sync-synchronizing"_L1, @"arrow.triangle.2.circlepath"}, {"task-due"_L1, @"clock.badge.exclamationmark"}, {"task-past-due"_L1, @"clock.badge.xmark"}, - {"user-available"_L1, @"person.crop.circle.badge.checkmark"}, - {"user-away"_L1, @"person.crop.circle.badge.clock"}, - //{"user-idle"_L1, @""}, + {"user-available"_L1, @"person.crop.circle.badge"}, + {"user-away"_L1, @"person.crop.circle.badge.moon"}, + {"user-idle"_L1, @"person.crop.circle.badge.clock"}, {"user-offline"_L1, @"person.crop.circle.badge.xmark"}, //{"user-trash-full"_L1, @""}, {"weather-clear"_L1, @"sun.max"}, @@ -269,9 +271,9 @@ auto *loadImage(const QString &iconName) {"weather-few-clouds-night"_L1, @"cloud.moon"}, {"weather-fog"_L1, @"cloud.fog"}, {"weather-overcast"_L1, @"cloud"}, - //{"weather-severe-alert"_L1, @""}, - {"weather-showers"_L1, @"cloud.rain"}, - //{"weather-showers-scattered"_L1, @""}, + {"weather-severe-alert"_L1, @"cloud.bolt.rain"}, + {"weather-showers"_L1, @"cloud.heavyrain"}, + {"weather-showers-scattered"_L1, @"cloud.drizzle"}, {"weather-snow"_L1, @"cloud.snow"}, {"weather-storm"_L1, @"tropicalstorm"}, }; diff --git a/src/gui/platform/darwin/qapplekeymapper.mm b/src/gui/platform/darwin/qapplekeymapper.mm index 214865864ff..49f7a8ff0cb 100644 --- a/src/gui/platform/darwin/qapplekeymapper.mm +++ b/src/gui/platform/darwin/qapplekeymapper.mm @@ -94,60 +94,10 @@ static CarbonModifiers toCarbonModifiers(Qt::KeyboardModifiers qtModifiers) return carbonModifiers; } -// Keyboard keys (non-modifiers) -static QHash<char16_t, Qt::Key> standardKeys = { - { kHomeCharCode, Qt::Key_Home }, - { kEnterCharCode, Qt::Key_Enter }, - { kEndCharCode, Qt::Key_End }, - { kBackspaceCharCode, Qt::Key_Backspace }, - { kTabCharCode, Qt::Key_Tab }, - { kPageUpCharCode, Qt::Key_PageUp }, - { kPageDownCharCode, Qt::Key_PageDown }, - { kReturnCharCode, Qt::Key_Return }, - { kEscapeCharCode, Qt::Key_Escape }, - { kLeftArrowCharCode, Qt::Key_Left }, - { kRightArrowCharCode, Qt::Key_Right }, - { kUpArrowCharCode, Qt::Key_Up }, - { kDownArrowCharCode, Qt::Key_Down }, - { kHelpCharCode, Qt::Key_Help }, - { kDeleteCharCode, Qt::Key_Delete }, - // ASCII maps, for debugging - { ':', Qt::Key_Colon }, - { ';', Qt::Key_Semicolon }, - { '<', Qt::Key_Less }, - { '=', Qt::Key_Equal }, - { '>', Qt::Key_Greater }, - { '?', Qt::Key_Question }, - { '@', Qt::Key_At }, - { ' ', Qt::Key_Space }, - { '!', Qt::Key_Exclam }, - { '"', Qt::Key_QuoteDbl }, - { '#', Qt::Key_NumberSign }, - { '$', Qt::Key_Dollar }, - { '%', Qt::Key_Percent }, - { '&', Qt::Key_Ampersand }, - { '\'', Qt::Key_Apostrophe }, - { '(', Qt::Key_ParenLeft }, - { ')', Qt::Key_ParenRight }, - { '*', Qt::Key_Asterisk }, - { '+', Qt::Key_Plus }, - { ',', Qt::Key_Comma }, - { '-', Qt::Key_Minus }, - { '.', Qt::Key_Period }, - { '/', Qt::Key_Slash }, - { '[', Qt::Key_BracketLeft }, - { ']', Qt::Key_BracketRight }, - { '\\', Qt::Key_Backslash }, - { '_', Qt::Key_Underscore }, - { '`', Qt::Key_QuoteLeft }, - { '{', Qt::Key_BraceLeft }, - { '}', Qt::Key_BraceRight }, - { '|', Qt::Key_Bar }, - { '~', Qt::Key_AsciiTilde }, - { '^', Qt::Key_AsciiCircum } -}; - -static QHash<char16_t, Qt::Key> virtualKeys = { +// NSEvent.keyCode codes for keys that are independent of keyboard layout. +// Some of these are technically possible to add custom key maps for, but +// doing so would be unexpected. +static QHash<char16_t, Qt::Key> layoutIndependentKeyCodes = { { kVK_F1, Qt::Key_F1 }, { kVK_F2, Qt::Key_F2 }, { kVK_F3, Qt::Key_F3 }, @@ -164,16 +114,57 @@ static QHash<char16_t, Qt::Key> virtualKeys = { { kVK_F14, Qt::Key_F14 }, { kVK_F15, Qt::Key_F15 }, { kVK_F16, Qt::Key_F16 }, + { kVK_F17, Qt::Key_F17 }, + { kVK_F18, Qt::Key_F18 }, + { kVK_F19, Qt::Key_F19 }, + { kVK_F20, Qt::Key_F20 }, + { kVK_Return, Qt::Key_Return }, { kVK_Tab, Qt::Key_Tab }, + { kVK_Space, Qt::Key_Space }, { kVK_Escape, Qt::Key_Escape }, - { kVK_Help, Qt::Key_Help }, + { kVK_Delete, Qt::Key_Backspace }, + { kVK_ForwardDelete, Qt::Key_Delete }, + + { kVK_Home, Qt::Key_Home }, + { kVK_End, Qt::Key_End }, + { kVK_PageUp, Qt::Key_PageUp }, + { kVK_PageDown, Qt::Key_PageDown }, + { kVK_UpArrow, Qt::Key_Up }, { kVK_DownArrow, Qt::Key_Down }, { kVK_LeftArrow, Qt::Key_Left }, { kVK_RightArrow, Qt::Key_Right }, - { kVK_PageUp, Qt::Key_PageUp }, - { kVK_PageDown, Qt::Key_PageDown } + + { kVK_CapsLock, Qt::Key_CapsLock }, + { kVK_Shift, Qt::Key_Shift }, + { kVK_RightShift, Qt::Key_Shift }, + +#if 0 + // FIXME: Map these here instead of relying on + // custom logic in [QNSView flagsChanged:] + + { kVK_Command, Qt::Key_unknown }, + { kVK_RightCommand, Qt::Key_unknown }, + { kVK_Option, Qt::Key_unknown }, + { kVK_RightOption, Qt::Key_unknown }, + { kVK_Control, Qt::Key_unknown }, + { kVK_RightControl, Qt::Key_unknown }, + { kVK_Function, Qt::Key_unknown }, +#endif + + { kVK_VolumeUp, Qt::Key_VolumeUp }, + { kVK_VolumeDown, Qt::Key_VolumeDown }, + { kVK_Mute, Qt::Key_VolumeMute }, + +#if 0 + // FIXME: Figure out which Qt::Key this maps to + { kVK_ContextualMenu, Qt::Key_unknown }, +#endif + { kVK_Help, Qt::Key_Help }, + + { kVK_ANSI_KeypadClear, Qt::Key_Clear }, + { kVK_ANSI_KeypadEnter, Qt::Key_Enter }, }; static QHash<char16_t, Qt::Key> functionKeys = { @@ -211,41 +202,19 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers) qCDebug(lcQpaKeyMapperKeys, "Mapping key: %d (0x%04x) / vk %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey); - if (key == char16_t(kClearCharCode) && virtualKey == 0x47) - return Qt::Key_Clear; - - if (key.isDigit()) { - qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue()); - return key.digitValue() + Qt::Key_0; - } - - if (key.isLetter()) { - qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A')); - return (key.toUpper().unicode() - 'A') + Qt::Key_A; - } - if (key.isSymbol()) { - qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode())); - return key.unicode(); - } - - if (auto qtKey = standardKeys.value(key.unicode())) { + // Check first if we have a virtual key that should be treated as layout + // independent. If so, we want to return early without inspecting the key. + if (auto qtKey = layoutIndependentKeyCodes.value(virtualKey)) { + qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey << "based on layout independent virtual key"; // To work like Qt for X11 we issue Backtab when Shift + Tab are pressed if (qtKey == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { - qCDebug(lcQpaKeyMapperKeys, "Got key: Qt::Key_Backtab"); + qCDebug(lcQpaKeyMapperKeys, "Transformed into Qt::Key_Backtab"); return Qt::Key_Backtab; } - - qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; - return qtKey; - } - - // Last ditch try to match the scan code - if (auto qtKey = virtualKeys.value(virtualKey)) { - qCDebug(lcQpaKeyMapperKeys) << "Got scancode" << qtKey; return qtKey; } - // Check if they belong to key codes in private unicode range + // Then check if the key is one of the functions keys in the private Unicode range if (key >= char16_t(NSUpArrowFunctionKey) && key <= char16_t(NSModeSwitchFunctionKey)) { if (auto qtKey = functionKeys.value(key.unicode())) { qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; @@ -257,6 +226,20 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers) } } + if (key.isDigit()) { + qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue()); + return key.digitValue() + Qt::Key_0; + } + + if (key.isLetter()) { + qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A')); + return (key.toUpper().unicode() - 'A') + Qt::Key_A; + } + if (key.isSymbol()) { + qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode())); + return key.unicode(); + } + qCDebug(lcQpaKeyMapperKeys, "Unknown case.. %d[%d] %d", key.unicode(), key.toLatin1(), virtualKey); return Qt::Key_unknown; } diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index 1b6e76c2017..5797d1a68b4 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -16,6 +16,7 @@ #include <qbytearray.h> #include <qdatastream.h> #include <qdatetime.h> +#include <QtCore/private/qstringiterator_p.h> QT_BEGIN_NAMESPACE @@ -582,8 +583,11 @@ bool QTextHtmlImporter::appendNodeText() QString textToInsert; textToInsert.reserve(text.size()); - for (QChar ch : text) { - if (ch.isSpace() + QStringIterator it(text); + while (it.hasNext()) { + char32_t ch = it.next(); + + if (QChar::isSpace(ch) && ch != QChar::Nbsp && ch != QChar::ParagraphSeparator) { @@ -646,12 +650,12 @@ bool QTextHtmlImporter::appendNodeText() format.setAnchor(true); format.setAnchorNames(namedAnchors); - cursor.insertText(ch, format); + cursor.insertText(QString::fromUcs4(&ch, 1), format); namedAnchors.clear(); format.clearProperty(QTextFormat::IsAnchor); format.clearProperty(QTextFormat::AnchorName); } else { - textToInsert += ch; + textToInsert += QChar::fromUcs4(ch); } } } 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/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp index ec67ee80a1e..82632110d32 100644 --- a/src/network/kernel/qhostaddress.cpp +++ b/src/network/kernel/qhostaddress.cpp @@ -140,13 +140,6 @@ bool QHostAddressPrivate::parse(const QString &ipString) return false; } -void QHostAddressPrivate::clear() -{ - a = 0; - protocol = QHostAddress::UnknownNetworkLayerProtocol; - memset(&a6, 0, sizeof(a6)); -} - AddressClassification QHostAddressPrivate::classify() const { if (a) { diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h index 6cc28cd5a9b..608080e9ede 100644 --- a/src/network/kernel/qhostaddress_p.h +++ b/src/network/kernel/qhostaddress_p.h @@ -74,7 +74,13 @@ public: void setAddress(const Q_IPV6ADDR &a_); bool parse(const QString &ipString); - void clear(); + void clear() + { + a6 = {}; + a = 0; + protocol = QHostAddress::UnknownNetworkLayerProtocol; + scopeId.clear(); + } QString scopeId; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index bcd9aecdea9..430197ccc6e 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -33,7 +33,7 @@ #include <sys/socket.h> #include <netinet/sctp.h> #endif -#ifdef Q_OS_BSD4 +#ifdef AF_LINK # include <net/if_dl.h> #endif @@ -142,10 +142,6 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt, level = IPPROTO_IP; #ifdef IP_PKTINFO n = IP_PKTINFO; -#elif defined(IP_RECVDSTADDR) - // variant found in QNX and FreeBSD; it will get us only the - // destination address, not the interface; we need IP_RECVIF for that. - n = IP_RECVDSTADDR; #endif } break; @@ -326,6 +322,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 +338,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 @@ -364,6 +356,18 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::BindExclusively: return true; + case QNativeSocketEngine::ReceivePacketInformation: + if (socketProtocol == QAbstractSocket::IPv4Protocol) { +#if !defined(IP_PKTINFO) && defined(IP_RECVDSTADDR) && defined(IP_RECVIF) + // Seen on FreeBSD and QNX. We need both to get the information we want. + int r = 0; + r += ::setsockopt(socketDescriptor, IPPROTO_IP, IP_RECVDSTADDR, &v, sizeof(v)); + r += ::setsockopt(socketDescriptor, IPPROTO_IP, IP_RECVIF, &v, sizeof(v)); + return r == 0; +#endif + } + break; + case QNativeSocketEngine::MaxStreamsSocketOption: { #ifndef QT_NO_SCTP sctp_initmsg sctpInitMsg; @@ -417,6 +421,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) @@ -918,7 +923,7 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS { // we use quintptr to force the alignment quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) -#if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(Q_OS_BSD4) +#if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(AF_LINK) + CMSG_SPACE(sizeof(sockaddr_dl)) #endif #ifndef QT_NO_SCTP @@ -1010,7 +1015,7 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS header->destinationAddress.setAddress(ntohl(addr->s_addr)); } # endif -# if defined(IP_RECVIF) && defined(Q_OS_BSD4) +# if defined(IP_RECVIF) && defined(AF_LINK) if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_RECVIF && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sockaddr_dl))) { sockaddr_dl *sdl = reinterpret_cast<sockaddr_dl *>(CMSG_DATA(cmsgptr)); 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/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index d4c5d0f0425..e0ef6cec794 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -161,7 +161,8 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; bool selectable = (m_options->acceptMode() == QFileDialogOptions::AcceptSave) || [self panel:m_panel shouldEnableURL:url]; - m_panel.nameFieldStringValue = selectable ? info.fileName().toNSString() : @""; + if (!openpanel_cast(m_panel)) + m_panel.nameFieldStringValue = selectable ? info.fileName().toNSString() : @""; [self updateProperties]; 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/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h index 60b9bc8fc02..8fdcf88293e 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QIOSIMAGEPICKERCONTROLLER_H +#define QIOSIMAGEPICKERCONTROLLER_H + #import <UIKit/UIKit.h> #include "../../qiosfiledialog.h" @@ -9,3 +12,5 @@ @interface QIOSImagePickerController : UIImagePickerController <UIImagePickerControllerDelegate, UINavigationControllerDelegate> - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; @end + +#endif // QIOSIMAGEPICKERCONTROLLER_H diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h index 289c3ee3258..a227312c5b0 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QIOSDOCUMENTPICKERCONTROLLER_H +#define QIOSDOCUMENTPICKERCONTROLLER_H + #import <UIKit/UIKit.h> #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h> @@ -12,3 +15,5 @@ UIAdaptivePresentationControllerDelegate> - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; @end + +#endif // QIOSDOCUMENTPICKERCONTROLLER_H diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm index 4e019b69cc4..6ca6554f673 100644 --- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm +++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm @@ -7,8 +7,10 @@ #include "qiosdocumentpickercontroller.h" +#include <QtCore/qpointer.h> + @implementation QIOSDocumentPickerController { - QIOSFileDialog *m_fileDialog; + QPointer<QIOSFileDialog> m_fileDialog; } - (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog @@ -61,6 +63,9 @@ { Q_UNUSED(controller); + if (!m_fileDialog) + return; + QList<QUrl> files; for (NSURL* url in urls) files.append(QUrl::fromNSURL(url)); @@ -71,12 +76,18 @@ - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { + if (!m_fileDialog) + return; + Q_UNUSED(controller); emit m_fileDialog->reject(); } - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController { + if (!m_fileDialog) + return; + Q_UNUSED(presentationController); // "Called on the delegate when the user has taken action to dismiss the diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h index addfae3d748..7d73ed9821a 100644 --- a/src/plugins/platforms/ios/qiostextresponder.h +++ b/src/plugins/platforms/ios/qiostextresponder.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QIOSTEXTRESPONDER_H +#define QIOSTEXTRESPONDER_H + #import <UIKit/UIKit.h> #include <QtCore/qstring.h> @@ -50,3 +53,5 @@ QT_END_NAMESPACE @property(nonatomic, assign) id<UITextInputDelegate> inputDelegate; @end + +#endif // QIOSTEXTRESPONDER_H diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h index 84726216021..12ae3646ad9 100644 --- a/src/plugins/platforms/ios/quiview.h +++ b/src/plugins/platforms/ios/quiview.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default +#ifndef QUIVIEW_H +#define QUIVIEW_H + #import <UIKit/UIKit.h> #include <qhash.h> @@ -39,3 +42,5 @@ QT_END_NAMESPACE @interface QUIMetalView : QUIView @end #endif + +#endif // QUIVIEW_H 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/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp index 614d5bd25a3..18a457198f1 100644 --- a/src/plugins/platforms/wasm/qwasminputcontext.cpp +++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp @@ -225,8 +225,8 @@ void QWasmInputContext::updateGeometry() qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "propagating inputItemRectangle:" << inputItemRectangle; m_inputElement["style"].set("left", std::to_string(inputItemRectangle.x()) + "px"); m_inputElement["style"].set("top", std::to_string(inputItemRectangle.y()) + "px"); - m_inputElement["style"].set("width", std::to_string(inputItemRectangle.width()) + "px"); - m_inputElement["style"].set("height", std::to_string(inputItemRectangle.height()) + "px"); + m_inputElement["style"].set("width", "1px"); + m_inputElement["style"].set("height", "1px"); } } 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/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp index 28b12bd81f9..3b1b7a7eacb 100644 --- a/src/plugins/styles/modernwindows/qwindows11style.cpp +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -923,15 +923,13 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption case PE_IndicatorRadioButton: { const bool isRtl = option->direction == Qt::RightToLeft; const bool isOn = option->state & State_On; - qreal innerRadius = 7.0f; + qreal innerRadius = radioButtonInnerRadius(state); if (d->transitionsEnabled() && option->styleObject) { if (option->styleObject->property("_q_end_radius").isNull()) option->styleObject->setProperty("_q_end_radius", innerRadius); QNumberStyleAnimation *animation = qobject_cast<QNumberStyleAnimation *>(d->animation(option->styleObject)); innerRadius = animation ? animation->currentValue() : option->styleObject->property("_q_end_radius").toFloat(); option->styleObject->setProperty("_q_inner_radius", innerRadius); - } else { - innerRadius = radioButtonInnerRadius(state); } QRectF rect = isRtl ? option->rect.adjusted(0, 0, -2, 0) : option->rect.adjusted(2, 0, 0, 0); @@ -1331,11 +1329,12 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op } } break; - case QStyle::CE_ProgressBarGroove:{ - if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) { - QRect rect = subElementRect(SE_ProgressBarContents, progbaropt, widget); +#if QT_CONFIG(progressbar) + case CE_ProgressBarGroove: + if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) { + QRect rect = option->rect; QPointF center = rect.center(); - if (progbaropt->state & QStyle::State_Horizontal) { + if (baropt->state & QStyle::State_Horizontal) { rect.setHeight(1); rect.moveTop(center.y()); } else { @@ -1347,11 +1346,10 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op painter->drawRect(rect); } break; - } - case QStyle::CE_ProgressBarContents: + case CE_ProgressBarContents: if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { QPainterStateGuard psg(painter); - QRectF rect = subElementRect(SE_ProgressBarContents, baropt, widget); + QRectF rect = option->rect; painter->translate(rect.topLeft()); rect.translate(-rect.topLeft()); @@ -1411,16 +1409,17 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op drawRoundedRect(painter, rect, Qt::NoPen, baropt->palette.accent()); } break; - case QStyle::CE_ProgressBarLabel: - if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) { - const bool vertical = !(progbaropt->state & QStyle::State_Horizontal); + case CE_ProgressBarLabel: + if (const auto baropt = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + const bool vertical = !(baropt->state & QStyle::State_Horizontal); if (!vertical) { - QRect rect = subElementRect(SE_ProgressBarLabel, progbaropt, widget); - painter->setPen(progbaropt->palette.text().color()); - painter->drawText(rect, progbaropt->text, progbaropt->textAlignment); + proxy()->drawItemText(painter, baropt->rect, Qt::AlignCenter | Qt::TextSingleLine, + baropt->palette, baropt->state & State_Enabled, baropt->text, + QPalette::Text); } } break; +#endif // QT_CONFIG(progressbar) case CE_PushButtonLabel: if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { QRect textRect = btn->rect; @@ -1914,15 +1913,18 @@ QRect QWindows11Style::subElementRect(QStyle::SubElement element, const QStyleOp ret = QWindowsVistaStyle::subElementRect(element, option, widget); } break; - case QStyle::SE_ProgressBarLabel: - if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { - if (pb->textAlignment.testFlags(Qt::AlignVCenter)) { - ret = option->rect.adjusted(0, 6, 0, 0); - } else { - ret = QWindowsVistaStyle::subElementRect(element, option, widget); - } +#if QT_CONFIG(progressbar) + case SE_ProgressBarGroove: + case SE_ProgressBarContents: + case SE_ProgressBarLabel: + if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + QStyleOptionProgressBar optCopy(*pb); + // we only support label right from content + optCopy.textAlignment = Qt::AlignRight; + return QWindowsVistaStyle::subElementRect(element, &optCopy, widget); } break; +#endif // QT_CONFIG(progressbar) case QStyle::SE_HeaderLabel: case QStyle::SE_HeaderArrow: ret = QCommonStyle::subElementRect(element, option, widget); @@ -2448,9 +2450,6 @@ void QWindows11Style::polish(QPalette& result) SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color()); SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color()); - if (highContrastTheme) - result.setColor(QPalette::Active, QPalette::HighlightedText, result.windowText().color()); - auto *d = const_cast<QWindows11StylePrivate *>(d_func()); d->m_titleBarMinIcon = QIcon(); d->m_titleBarMaxIcon = QIcon(); 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/testlib/qtestcase.h b/src/testlib/qtestcase.h index ebb32238d67..ad3874bd4c9 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -430,7 +430,7 @@ namespace QTest Q_TESTLIB_EXPORT int qExec(QObject *testObject, const QStringList &arguments); #if QT_CONFIG(batch_test_support) || defined(Q_QDOC) - using TestEntryFunction = int (*)(int, char **); + using TestEntryFunction = std::function<int(int, char **)>; Q_TESTLIB_EXPORT void qRegisterTestCase(const QString &name, TestEntryFunction entryFunction); #endif // QT_CONFIG(batch_test_support) diff --git a/src/testlib/qtestregistry_p.h b/src/testlib/qtestregistry_p.h index 85e236cd046..fcfa6c60701 100644 --- a/src/testlib/qtestregistry_p.h +++ b/src/testlib/qtestregistry_p.h @@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE namespace QTest { class TestRegistry { public: - using TestEntryFunction = int(*)(int argv, char** argc); + using TestEntryFunction = std::function<int(int, char **)>; static TestRegistry* instance(); 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/tools/macdeployqt/macdeployqt/main.cpp b/src/tools/macdeployqt/macdeployqt/main.cpp index f5e6ab8f31a..ecbccdef2b3 100644 --- a/src/tools/macdeployqt/macdeployqt/main.cpp +++ b/src/tools/macdeployqt/macdeployqt/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char **argv) QStringList qmlDirs; QStringList qmlImportPaths; extern bool runCodesign; - extern QString codesignIdentiy; + QString codesignIdentity = QStringLiteral("-"); extern bool hardenedRuntime; bool noCodesignExplicit = false; extern bool appstoreCompliant; @@ -166,7 +166,7 @@ int main(int argc, char **argv) return 1; } else { runCodesign = true; - codesignIdentiy = argument.mid(index+1); + codesignIdentity = argument.mid(index + 1); } } else if (argument.startsWith(QByteArray("-sign-for-notarization"))) { LogDebug() << "Argument found:" << argument; @@ -182,7 +182,7 @@ int main(int argc, char **argv) runCodesign = true; hardenedRuntime = true; secureTimestamp = true; - codesignIdentiy = argument.mid(index+1); + codesignIdentity = argument.mid(index + 1); } } else if (argument.startsWith(QByteArray("-hardened-runtime"))) { LogDebug() << "Argument found:" << argument; @@ -273,7 +273,7 @@ int main(int argc, char **argv) stripAppBinary(appBundlePath); if (runCodesign) - codesign(codesignIdentiy, appBundlePath); + codesign(codesignIdentity, appBundlePath); if (dmg) { LogNormal(); diff --git a/src/tools/macdeployqt/shared/shared.cpp b/src/tools/macdeployqt/shared/shared.cpp index 4e81229ebf5..bd7f4fba854 100644 --- a/src/tools/macdeployqt/shared/shared.cpp +++ b/src/tools/macdeployqt/shared/shared.cpp @@ -30,7 +30,6 @@ bool runStripEnabled = true; bool alwaysOwerwriteEnabled = false; bool runCodesign = true; QStringList librarySearchPath; -QString codesignIdentiy = "-"; QString extraEntitlements; bool hardenedRuntime = false; bool secureTimestamp = false; 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/modelview.qdoc b/src/widgets/doc/src/modelview.qdoc index b2448a2c705..121cc30ed09 100644 --- a/src/widgets/doc/src/modelview.qdoc +++ b/src/widgets/doc/src/modelview.qdoc @@ -573,281 +573,10 @@ out of range when using ModelTest. - \section1 4. Good Sources of Additional Information - - \section2 4.1 Books - - Model/View programming is covered quite extensively in the documentation of - Qt but also in several good books. - - \list 1 - \li \b{C++ GUI Programming with Qt 4} / Jasmin Blanchette, Mark Summerfield, - \e{Prentice Hall, 2nd edition}, ISBN 0-13-235416-0. Also available in - German: \b{C++ GUI Programmierung mit Qt 4: Die offizielle Einführung}, - \e{Addison-Wesley}, ISBN 3-827327-29-6 - \li \b{The Book of Qt4, The Art of Building Qt Applications} / Daniel Molkentin, - \e{Open Source Press}, ISBN 1-59327-147-6. - Translated from \b{Qt 4, Einführung in die Applikationsentwicklung}, - \e{Open Source Press}, ISBN 3-937514-12-0. - \li \b{Foundations of Qt Development} / Johan Thelin, \e{Apress}, ISBN 1-59059-831-8. - \li \b{Advanced Qt Programming} / Mark Summerfield, \e{Prentice Hall}, ISBN 0-321-63590-6. - This book covers Model/View programming on more than 150 pages. - \endlist - - The following list provides an overview of example programs contained in the first three - books listed above. Some of them make very good templates for developing similar - applications. - - \table - \header - \li Example name - \li View class used - \li Model used - \li Aspects covered - \li - \row - \li Team Leaders - \li QListview - \li QStringListModel - \li - \li Book 1, Chapter 10, Figure 10.6 - \row - \li Color Names - \li QListView - \li QSortFilterProxyModel - applied to QStringListModel - \li - \li Book 1, Chapter 10, Figure 10.8 - \row - \li Currencies - \li QTableView - \li custom model based on - QAbstractTableModel - \li Read only - \li Book 1, Chapter 10, Figure 10.10 - \row - \li Cities - \li QTableView - \li Custom model based on - QAbstractTableModel - \li Read / write - \li Book 1, Chapter 10, Figure 10.12 - \row - \li Boolean Parser - \li QTreeView - \li Custom model based on - QAbstractItemModel - \li Read only - \li Book 1, Chapter 10, Figure 10.14 - \row - \li Track Editor - \li {2, 1} QTableWidget - \li Custom delegate providing a custom editor - \li Book 1, Chapter 10, Figure 10.15 - - \row - \li Address Book - \li QListView - QTableView - QTreeView - \li Custom model based on - QAbstractTableModel - \li Read / write - \li Book2, Chapter 8.4 - \row - \li Address Book with sorting - \li - \li QSortfilterProxyModel - \li Introducing sort and filter capabilities - \li Book2, Chapter 8.5 - \row - \li Address Book - with checkboxes - \li - \li - \li Introducing checkboxes in model/view - \li Book2, Chapter 8.6 - \row - \li Address Book with transposed grid - \li - \li Custom proxy Model based on QAbstractProxyModel - \li Introducing a custom model - \li Book2, Chapter 8.7 - \row - \li Address Book with drag and drop - \li - \li - \li Introducing drag and drop support - \li Book2, Chapter 8.8 - \row - \li Address Book with custom editor - \li - \li - \li Introducing custom delegates - \li Book2, Chapter 8.9 - \row - \li Views - \li QListView - QTableView - QTreeView - \li QStandardItemModel - \li Read only - \li Book 3, Chapter 5, figure 5-3 - \row - \li Bardelegate - \li QTableView - \li - \li Custom delegate for presentation based on QAbstractItemDelegate - \li Book 3, Chapter 5, figure 5-5 - \row - \li Editdelegate - \li QTableView - \li - \li Custom delegate for editing based on QAbstractItemDelegate - \li Book 3, Chapter 5, figure 5-6 - \row - \li Singleitemview - \li Custom view based on QAbstractItemView - \li - \li Custom view - \li Book 3, - Chapter 5, - figure 5-7 - \row - \li listmodel - \li QTableView - \li Custom Model based on QAbstractTableModel - \li Read only - \li Book 3, Chapter 5, Figure 5-8 - \row - \li treemodel - \li QTreeView - \li Custom Model based on QAbstractItemModel - \li Read only - \li Book 3, Chapter 5, Figure 5-10 - \row - \li edit integers - \li QListView - \li Custom Model based on QAbstractListModel - \li Read / write - \li Book 3, Chapter 5, Listing 5-37, Figure 5-11 - \row - \li sorting - \li QTableView - \li QSortFilterProxyModel applied to QStringListModel - \li Demonstrates sorting - \li Book 3, Chapter 5, Figure 5-12 - \endtable - - - \section2 4.2 Qt Documentation - - Qt 5.0 comes with 19 examples for model/view. - The examples can be found on the \l{Item Views Examples} page. - - \table - \header - \li Example name - \li View class used - \li Model used - \li Aspects covered - \row - \li Address Book - \li QTableView - \li QAbstractTableModel - QSortFilterProxyModel - \li Usage of QSortFilterProxyModel to generate different - subsets from one data pool - \row - \li Basic Sort/Filter Model - \li QTreeView - \li QStandardItemModel - QSortFilterProxyModel - \li - \row - \li Chart - \li Custom view - \li QStandardItemModel - \li Designing custom views that cooperate with selection models - \row - \li Color Editor Factory - \li {2, 1} QTableWidget - \li Enhancing the standard delegate with a new custom editor to choose colours - \row - \li Combo Widget Mapper - \li QDataWidgetMapper to map QLineEdit, QTextEdit and QComboBox - \li QStandardItemModel - \li Shows how a QComboBox can serve as a view class - \row - \li Custom Sort/Filter Model - \li QTreeView - \li QStandardItemModel - QSortFilterProxyModel - \li Subclass QSortFilterProxyModel for advanced sorting and filtering - \row - \li Dir View - \li QTreeView - \li QFileSystemModel - \li Very small example to demonstrate how to assign a model to a view - \row - \li Editable Tree Model - \li QTreeView - \li Custom tree model - \li Comprehensive example for working with trees, demonstrates - editing cells and tree structure with an underlying custom - model - \row - \li Fetch More - \li QListView - \li Custom list model - \li Dynamically changing model - \row - \li Frozen Column - \li QTableView - \li QStandardItemModel - \li - \row - \li Interview - \li Multiple - \li Custom item model - \li Multiple views - \row - \li Pixelator - \li QTableView - \li Custom table model - \li Implementation of a custom delegate - \row - \li Puzzle - \li QListView - \li Custom list model - \li Model/view with drag and drop - \row - \li Simple DOM Model - \li QTreeView - \li Custom tree model - \li Read only example for a custom tree model - \row - \li Simple Tree Model - \li QTreeView - \li Custom tree model - \li Read only example for a custom tree model - \row - \li Simple Widget Mapper - \li QDataWidgetMapper to map QLineEdit, QTextEdit and QSpinBox - \li QStandardItemModel - \li Basic QDataWidgetMapper usage - \row - \li Spreadsheet - \li {2, 1} QTableView - \li Custom delegates - \row - \li Star Delegate - \li {2, 1} QTableWidget - \li Comprehensive custom delegate example. - \endtable + \section1 Examples - A \l{Model/View Programming}{reference document} for model/view technology - is also available. + Qt comes with multiple examples for model/view. You can find them on the + \l{Item Views Examples} page. */ /*! 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..ce509dc9e98 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; @@ -204,7 +209,7 @@ public: virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const override; - using QAbstractScrollArea::update; + using QWidget::update; public Q_SLOTS: virtual void reset(); 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/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 82d16cb1252..b2cfb27e814 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -3419,29 +3419,28 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC } QRect gr = subControlRect(cc, opt, SC_SliderGroove, w); - if (slider->subControls & SC_SliderGroove) { + if (slider->subControls & SC_SliderGroove) grooveSubRule.drawRule(p, gr); - } if (slider->subControls & SC_SliderHandle) { QRect hr = subControlRect(cc, opt, SC_SliderHandle, w); - QRenderRule subRule1 = renderRule(w, opt, PseudoElement_SliderSubPage); - if (subRule1.hasDrawable()) { - QRect r(gr.topLeft(), - slider->orientation == Qt::Horizontal - ? QPoint(hr.x()+hr.width()/2, gr.y()+gr.height() - 1) - : QPoint(gr.x()+gr.width() - 1, hr.y()+hr.height()/2)); - subRule1.drawRule(p, r); - } - - QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SliderAddPage); - if (subRule2.hasDrawable()) { - QRect r(slider->orientation == Qt::Horizontal - ? QPoint(hr.x()+hr.width()/2+1, gr.y()) - : QPoint(gr.x(), hr.y()+hr.height()/2+1), - gr.bottomRight()); - subRule2.drawRule(p, r); + if (slider->subControls & SC_SliderGroove) { + const bool isHor = slider->orientation == Qt::Horizontal; + QRenderRule subRule1 = renderRule(w, opt, PseudoElement_SliderSubPage); + if (subRule1.hasDrawable()) { + QRect r(gr.topLeft(), + isHor ? QPoint(hr.x() + hr.width() / 2, gr.y() + gr.height() - 1) + : QPoint(gr.x() + gr.width() - 1, hr.y() + hr.height() / 2)); + subRule1.drawRule(p, r); + } + QRenderRule subRule2 = renderRule(w, opt, PseudoElement_SliderAddPage); + if (subRule2.hasDrawable()) { + QRect r(isHor ? QPoint(hr.x() + hr.width() / 2 + 1, gr.y()) + : QPoint(gr.x(), hr.y() + hr.height() / 2 + 1), + gr.bottomRight()); + subRule2.drawRule(p, r); + } } handleSubRule.drawRule(p, handleSubRule.boxRect(hr, Margin)); diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index 1ca6b8a47a1..7aff0da3327 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -2220,7 +2220,7 @@ QMdiSubWindow::QMdiSubWindow(QWidget *parent, Qt::WindowFlags flags) d->titleBarPalette = d->desktopPalette(); d->font = QApplication::font("QMdiSubWindowTitleBar"); // We don't want the menu icon by default on mac. -#ifndef Q_OS_MAC +#ifndef Q_OS_DARWIN if (windowIcon().isNull()) d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, nullptr, this); else @@ -2847,8 +2847,11 @@ bool QMdiSubWindow::event(QEvent *event) break; case QEvent::WindowIconChange: d->menuIcon = windowIcon(); + // We don't want the default menu icon on mac. +#ifndef Q_OS_DARWIN if (d->menuIcon.isNull()) d->menuIcon = style()->standardIcon(QStyle::SP_TitleBarMenuButton, nullptr, this); +#endif if (d->controlContainer) d->controlContainer->updateWindowIcon(d->menuIcon); if (!maximizedSystemMenuIconWidget()) diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 7d4228709be..92ff14dd44f 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, diff --git a/tests/auto/cmake/test_qt_add_ui_common/functions.cmake b/tests/auto/cmake/test_qt_add_ui_common/functions.cmake index b06ba25e21f..d3878da3fbc 100644 --- a/tests/auto/cmake/test_qt_add_ui_common/functions.cmake +++ b/tests/auto/cmake/test_qt_add_ui_common/functions.cmake @@ -8,7 +8,30 @@ function(generate_hash_folder target_name infile out_folder) set(${out_folder} "${short_hash}" PARENT_SCOPE) endfunction() +function(check_generator_works out_var generator_name) + message(STATUS "Checking if generator '${generator_name}' works") + string(CONCAT source_dir + "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/vs_generator_test") + set(build_dir + "${CMAKE_CURRENT_BINARY_DIR}/vs_generator_test-build") + run_cmake_configure(SOURCE_DIR "${source_dir}" + BUILD_DIR "${build_dir}" + GENERATOR "${generator_name}" + CLEAN_FIRST + RESULT_VARIABLE cmake_result) + + if("${cmake_result}" EQUAL 0) + set(generator_works "TRUE") + else() + set(generator_works "FALSE") + endif() + message(STATUS "Checking if generator '${generator_name}' works - ${generator_works}") + set(${out_var} "${generator_works}" PARENT_SCOPE) +endfunction() + function(get_latest_vs_generator output) + message(STATUS "Checking which latest Visual Studio generator can be used.") execute_process(COMMAND ${CMAKE_COMMAND} -G ERROR_VARIABLE CMAKE_GENERATORS_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -23,7 +46,14 @@ function(get_latest_vs_generator output) set(last_generator "") foreach(generator IN LISTS vs_generators) string(REGEX MATCH "Visual Studio ([0-9]+) [0-9]+" unused "${generator}") + if("${CMAKE_MATCH_1}" VERSION_GREATER "${last_version}") + # Skip Visual Studio 18 2026 because it's not installed in CI yet + check_generator_works(generator_works "${CMAKE_MATCH_0}") + if(NOT generator_works) + continue() + endif() + set(last_version "${CMAKE_MATCH_1}") set(last_generator "${CMAKE_MATCH_0}") endif() diff --git a/tests/auto/cmake/test_qt_add_ui_common/vs_generator_test/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/vs_generator_test/CMakeLists.txt new file mode 100644 index 00000000000..dbde5e369e5 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/vs_generator_test/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(VsGeneratorTest LANGUAGES CXX) 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/qdirlisting/tst_qdirlisting.cpp b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp index 3a8ea31740c..29b78415924 100644 --- a/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp +++ b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp @@ -92,8 +92,8 @@ private slots: void uncPaths(); #endif #ifndef Q_OS_WIN - void hiddenFiles(); - void hiddenDirs(); + void hidden_data(); + void hidden(); #endif void withStdAlgorithms(); @@ -871,47 +871,46 @@ void tst_QDirListing::uncPaths() // anything starting by a '.' is a hidden file. // For that reason these two tests aren't run on Windows. -void tst_QDirListing::hiddenFiles() +void tst_QDirListing::hidden_data() { - QStringList expected = { - "hiddenDirs_hiddenFiles/normalFile"_L1, + QTest::addColumn<QDirListing::IteratorFlags>("flags"); + QTest::addColumn<QStringList>("expected"); + + using F = QDirListing::IteratorFlag; + + QTest::newRow("Recursive-ExcludeDirs") + << (F::ExcludeDirs | F::IncludeHidden | F::Recursive) + << QStringList{ + "hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenFile"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory/normalFile"_L1, "hiddenDirs_hiddenFiles/.hiddenFile"_L1, - "hiddenDirs_hiddenFiles/normalDirectory/normalFile"_L1, "hiddenDirs_hiddenFiles/normalDirectory/.hiddenFile"_L1, - "hiddenDirs_hiddenFiles/.hiddenDirectory/normalFile"_L1, - "hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenFile"_L1, - }; - expected.sort(); - - constexpr auto flags = ItFlag::ExcludeDirs | ItFlag::IncludeHidden | ItFlag::Recursive; - QStringList list; - list.reserve(expected.size()); - for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, flags)) { - QVERIFY(dirEntry.isFile()); - list.emplace_back(dirEntry.filePath()); - } - list.sort(); + "hiddenDirs_hiddenFiles/normalDirectory/normalFile"_L1, + "hiddenDirs_hiddenFiles/normalFile"_L1, + }; - QCOMPARE_EQ(list, expected); + QTest::newRow("Recursive-ExcludeFiles") + << (F::ExcludeFiles | F::IncludeHidden | F::Recursive) + << QStringList{ + "hiddenDirs_hiddenFiles/.hiddenDirectory"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory/.hidden-subdir"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory/subdir"_L1, + "hiddenDirs_hiddenFiles/normalDirectory"_L1, + "hiddenDirs_hiddenFiles/normalDirectory/.hidden-subdir"_L1, + "hiddenDirs_hiddenFiles/normalDirectory/subdir"_L1, + }; } -void tst_QDirListing::hiddenDirs() +void tst_QDirListing::hidden() { - QStringList expected = { - "hiddenDirs_hiddenFiles/normalDirectory"_L1, - "hiddenDirs_hiddenFiles/normalDirectory/subdir"_L1, - "hiddenDirs_hiddenFiles/normalDirectory/.hidden-subdir"_L1, - "hiddenDirs_hiddenFiles/.hiddenDirectory"_L1, - "hiddenDirs_hiddenFiles/.hiddenDirectory/subdir"_L1, - "hiddenDirs_hiddenFiles/.hiddenDirectory/.hidden-subdir"_L1, - }; - expected.sort(); + QFETCH(QDirListing::IteratorFlags, flags); + QFETCH(QStringList, expected); - constexpr auto flags = ItFlag::ExcludeFiles | ItFlag::IncludeHidden | ItFlag::Recursive; QStringList list; list.reserve(expected.size()); + bool isDir = flags.testFlags(ItFlag::ExcludeFiles); for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, flags)) { - QVERIFY(dirEntry.isDir()); + QVERIFY(isDir ? dirEntry.isDir() : dirEntry.isFile()); list.emplace_back(dirEntry.filePath()); } list.sort(); diff --git a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp index 560d4196b28..6a898e7af0d 100644 --- a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp +++ b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp @@ -291,7 +291,7 @@ void tst_QProcessEnvironment::putenv() static bool testRan = false; if (testRan) - QFAIL("You cannot run this test more than once, since we modify the environment"); + QSKIP("You cannot run this test more than once, since we modify the environment"); testRan = true; QByteArray valBefore = qgetenv(envname); diff --git a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp index 472e03adcac..00672ef0b6a 100644 --- a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp +++ b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp @@ -678,8 +678,20 @@ void tst_QResourceEngine::registerNestedRccFile() "/registeredNestedRccFile")); QVERIFY2(QResource::registerResource(":/nestedrcc/runtime_resource.rcc", "/registeredNestedRccFile"), - "Second QResource::registerResource call failed."); + "Second QResource::registerResource call does not failed."); QVERIFY(QFile::exists(":/registeredNestedRccFile/runtime_resource/search_file.txt")); + + // clean up + QVERIFY(QResource::unregisterResource(":/nestedrcc/runtime_resource.rcc", + "/registeredNestedRccFile")); + + QVERIFY(QFile::exists(":/registeredNestedRccFile/runtime_resource/search_file.txt")); + + QVERIFY(QResource::unregisterResource(":/nestedrcc/runtime_resource.rcc", + "/registeredNestedRccFile")); + + // verify clean up + QVERIFY(!QFile::exists(":/registeredNestedRccFile/runtime_resource/search_file.txt")); } QTEST_MAIN(tst_QResourceEngine) diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index 3dd36bdae35..5453237cadb 100644 --- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp +++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp @@ -182,7 +182,16 @@ private slots: void childKeys(); void testIniParsing_data(); void testIniParsing(); + void testEscapes(); + void testEscapedKeys_data(); + 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(); } @@ -2831,35 +2840,6 @@ void tst_QSettings::testEscapes() { QSettings settings(QSettings::UserScope, "software.org", "KillerAPP"); -#define testEscapedKey(plainKey, escKey) \ - QCOMPARE(iniEscapedKey(plainKey), QByteArray(escKey)); \ - QCOMPARE(iniUnescapedKey(escKey), QString(plainKey)); - -#define testUnescapedKey(escKey, plainKey, reescKey) \ - QCOMPARE(iniUnescapedKey(escKey), QString(plainKey)); \ - QCOMPARE(iniEscapedKey(plainKey), QByteArray(reescKey)); \ - QCOMPARE(iniUnescapedKey(reescKey), QString(plainKey)); - -#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); \ @@ -2875,57 +2855,6 @@ void tst_QSettings::testEscapes() QCOMPARE(v.toString(), QString(vStr)); \ } - testEscapedKey("", ""); - testEscapedKey(" ", "%20"); - testEscapedKey(" 0123 abcd ", "%200123%20abcd%20"); - testEscapedKey("~!@#$%^&*()_+.-/\\=", "%7E%21%40%23%24%25%5E%26%2A%28%29_%2B.-\\%5C%3D"); - testEscapedKey(QString() + QChar(0xabcd) + QChar(0x1234) + QChar(0x0081), "%UABCD%U1234%81"); - testEscapedKey(QString() + QChar(0xFE) + QChar(0xFF) + QChar(0x100) + QChar(0x101), "%FE%FF%U0100%U0101"); - - testUnescapedKey("", "", ""); - testUnescapedKey("%20", " ", "%20"); - testUnescapedKey("/alpha/beta", "/alpha/beta", "\\alpha\\beta"); - testUnescapedKey("\\alpha\\beta", "/alpha/beta", "\\alpha\\beta"); - testUnescapedKey("%5Calpha%5Cbeta", "\\alpha\\beta", "%5Calpha%5Cbeta"); - testUnescapedKey("%", "%", "%25"); - testUnescapedKey("%f%!%%%%1x%x1%U%Uz%U123%U1234%1234%", QString("%f%!%%%%1x%x1%U%Uz%U123") + QChar(0x1234) + "\x12" + "34%", - "%25f%25%21%25%25%25%251x%25x1%25U%25Uz%25U123%U1234%1234%25"); - - 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); @@ -2949,6 +2878,190 @@ void tst_QSettings::testEscapes() testBadEscape("@Rect(1 2 3)", "@Rect(1 2 3)"); testBadEscape("@@Rect(1 2 3)", "@Rect(1 2 3)"); } + +void tst_QSettings::testEscapedKeys_data() +{ + QTest::addColumn<QString>("plainKey"); + QTest::addColumn<QByteArray>("escKey"); + + QTest::newRow("empty-string") << u""_s << ""_ba; + QTest::newRow("space") << u" "_s << "%20"_ba; + QTest::newRow(" 0123 abcd ") << " 0123 abcd " << "%200123%20abcd%20"_ba; + + QTest::newRow("special-characters") + << "~!@#$%^&*()_+.-/\\=" + << "%7E%21%40%23%24%25%5E%26%2A%28%29_%2B.-\\%5C%3D"_ba; + + const std::array arr1 = {QChar(0xabcd), QChar(0x1234), QChar(0x0081)}; + QTest::newRow("qchar-array1") << QString(arr1) << "%UABCD%U1234%81"_ba; + + const std::array arr2 = {QChar(0xFE), QChar(0xFF), QChar(0x100), QChar(0x101)}; + QTest::newRow("qchar-array2") << QString(arr2) << "%FE%FF%U0100%U0101"_ba; +} + +void tst_QSettings::testEscapedKeys() +{ + QFETCH(QString, plainKey); + QFETCH(QByteArray, escKey); + + QSettings settings(QSettings::UserScope, "example.org", "KillerAPP"); + + QCOMPARE(iniEscapedKey(plainKey), escKey); + QCOMPARE(iniUnescapedKey(escKey), plainKey); +} + +void tst_QSettings::testUnescapedKeys_data() +{ + QTest::addColumn<QByteArray>("escKey"); + QTest::addColumn<QString>("plainKey"); + QTest::addColumn<QByteArray>("reescKey"); + + QTest::newRow("empty-string") << ""_ba << u""_s << ""_ba; + QTest::newRow("space") << "%20"_ba << u" "_s << "%20"_ba; + QTest::newRow("%") << "%"_ba << u"%"_s << "%25"_ba; + + QTest::newRow("/alpha/beta") << "/alpha/beta"_ba << "/alpha/beta" << "\\alpha\\beta"_ba; + QTest::newRow("\\alpha\\beta") << "\\alpha\\beta"_ba << "/alpha/beta" << "\\alpha\\beta"_ba; + QTest::newRow("%5Calpha%5Cbeta") << "%5Calpha%5Cbeta"_ba << "\\alpha\\beta" << "%5Calpha%5Cbeta"_ba; + + QTest::newRow("many-percent") + << "%f%!%%%%1x%x1%U%Uz%U123%U1234%1234%"_ba + << QString("%f%!%%%%1x%x1%U%Uz%U123"_L1 + QChar(0x1234) + "\x12" + "34%") + << "%25f%25%21%25%25%25%251x%25x1%25U%25Uz%25U123%U1234%1234%25"_ba; +} + +void tst_QSettings::testUnescapedKeys() +{ + QFETCH(QByteArray, escKey); + QFETCH(QString, plainKey); + QFETCH(QByteArray, reescKey); + + QSettings settings(QSettings::UserScope, "example.org", "KillerAPP"); + + QCOMPARE(iniUnescapedKey(escKey), plainKey); + QCOMPARE(iniEscapedKey(plainKey), reescKey); + 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/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 7c0e5e10b71..bb7555f0329 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -22,6 +22,7 @@ private slots: void variantProperty(); void notifySignal(); void enumerator(); + void enumProperty(); void classInfo(); void relatedMetaObject(); void staticMetacall(); @@ -1019,6 +1020,26 @@ void tst_QMetaObjectBuilder::enumerator() QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Enumerators)); } +void tst_QMetaObjectBuilder::enumProperty() +{ + // When adding property with an enumeration type, QMetaProperty::isEnumType() + // should return true. + QMetaObjectBuilder builder; + builder.setSuperClass(QObject::metaObject()); + + auto enumMetaType = QMetaType::fromType<Qt::Orientation>(); + QVERIFY(enumMetaType.isValid()); + + builder.addProperty("orientation", "Qt::Orientation", enumMetaType); + + auto *mo = builder.toMetaObject(); + QVERIFY(mo != nullptr); + const int index = mo->indexOfProperty("orientation"); + QVERIFY(index != -1); + QVERIFY(mo->property(index).isEnumType()); + free(mo); +} + void tst_QMetaObjectBuilder::classInfo() { QMetaObjectBuilder builder; @@ -1711,7 +1732,7 @@ void tst_QMetaObjectBuilder::usage_templateConnect() testObject.data(), &TestObject::voidSlotInt)); // Something that isn't a signal - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in TestObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(TestObject, TestObject): signal not found"); con = QObject::connect(testObject.data(), &TestObject::setIntProp, testObject.data(), &TestObject::intPropChanged); QVERIFY(!con); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 220d3af568b..afb0bf0169a 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -4864,18 +4864,18 @@ void tst_QObject::pointerConnect() QVERIFY(!QObject::disconnect(con)); //connect a slot to a signal (== error) - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(ReceiverObject, SenderObject): signal not found"); con = connect(&r1, &ReceiverObject::slot4 , &s, &SenderObject::signal4); QVERIFY(!con); QVERIFY(!QObject::disconnect(con)); //connect an arbitrary PMF to a slot - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(ReceiverObject, ReceiverObject): signal not found"); con = connect(&r1, &ReceiverObject::reset, &r1, &ReceiverObject::slot1); QVERIFY(!con); QVERIFY(!QObject::disconnect(con)); - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(ReceiverObject, ReceiverObject): signal not found"); con = connect(&r1, &ReceiverObject::reset, &r1, [](){}); QVERIFY(!con); QVERIFY(!QObject::disconnect(con)); diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index adff512e6a3..81316b061d0 100644 --- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp +++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp @@ -45,11 +45,11 @@ using namespace std::chrono_literals; class tst_QTimer : public QObject { Q_OBJECT -public: - static void initMain(); private slots: + void initTestCase(); void cleanupTestCase(); + void zeroTimer(); void singleShotTimeout(); void timeout(); @@ -1678,11 +1678,11 @@ struct StaticSingleShotUser }; // NOTE: to prevent any static initialization order fiasco, we implement -// initMain() to instantiate staticSingleShotUser before qApp +// initTestCase() to instantiate staticSingleShotUser before qApp static StaticSingleShotUser *s_staticSingleShotUser = nullptr; -void tst_QTimer::initMain() +void tst_QTimer::initTestCase() { s_staticSingleShotUser = new StaticSingleShotUser; } diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp index 2e0d8dd7be2..dbaf6873af2 100644 --- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp +++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp @@ -3595,7 +3595,7 @@ void tst_QtJson::bom() QCOMPARE(error.error, QJsonParseError::NoError); } -void tst_QtJson::nesting() +static void nesting_test() { // check that we abort parsing too deeply nested json documents. // this is to make sure we don't crash because the parser exhausts the @@ -3654,6 +3654,26 @@ void tst_QtJson::nesting() } +void tst_QtJson::nesting() +{ +#if defined(Q_OS_QNX) || defined(Q_OS_VXWORKS) || defined(Q_OS_WASM) + // This test misbehaving probably indicates a stack overflow due to the + // recursive parser in qjsonparser.cpp. The recursion prevention limit may + // be too high for this platform. Someone should investigate. + QSKIP("Test freezes or crashes - probably a stack overflow"); +#endif + + QThread *thr = QThread::create(nesting_test); +#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) || \ + defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer) + // force a larger stack size - 8 MB seems sufficient + thr->setStackSize(8192 * 1024); +#endif + thr->start(); + thr->wait(); + delete thr; +} + void tst_QtJson::longStrings() { // test around 15 and 16 bit boundaries, as these are limits diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 76fcee11b7b..806b6b43161 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -3998,7 +3998,7 @@ void tst_QFuture::signalConnect() #if defined(Q_CC_MSVC_ONLY) && (Q_CC_MSVC < 1940 || !defined(_DEBUG)) #define EXPECT_FUTURE_CONNECT_FAIL() QEXPECT_FAIL("", "QTBUG-101761, test fails on Windows/MSVC", Continue) #else - QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in SenderObject"); + QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, SenderObject): signal not found"); #define EXPECT_FUTURE_CONNECT_FAIL() #endif diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index b9e239af31d..fde06d2edd9 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -2411,7 +2411,7 @@ void tst_QDateTime::springForward_data() QTest::newRow("PT from day after") << pacific << QDate(2015, 3, 8) << QTime(2, 30) << -1 << -420; } - if (const QTimeZone eastern("America/Ottawa"); eastern.isValid()) { + if (const QTimeZone eastern("America/Toronto"); eastern.isValid()) { QTest::newRow("ET from day before") << eastern << QDate(2015, 3, 8) << QTime(2, 30) << 1 << -300; QTest::newRow("ET from day after") @@ -3500,11 +3500,11 @@ void tst_QDateTime::fromStringStringFormat_localTimeZone_data() QTest::newRow("local-timezone-ttt-with-zone:Etc/GMT+3") << "GMT"_ba << u"2008-10-13 Etc/GMT+3 11.50"_s << u"yyyy-MM-dd ttt hh.mm"_s << 1900 << QDateTime(); // Zone name not valid when offset expected - QTimeZone gmtWithOffset("GMT-2"); + QTimeZone gmtWithOffset("GMT-0"); if (gmtWithOffset.isValid()) { lacksRows = false; - QTest::newRow("local-timezone-with-offset:GMT-2") - << "GMT"_ba << u"2008-10-13 GMT-2 11.50"_s << u"yyyy-MM-dd t hh.mm"_s << 1900 + QTest::newRow("local-timezone-with-offset:GMT-0") + << "GMT"_ba << u"2008-10-13 GMT-0 11.50"_s << u"yyyy-MM-dd t hh.mm"_s << 1900 << QDateTime(QDate(2008, 10, 13), QTime(11, 50), gmtWithOffset); } QTimeZone gmt("GMT"); diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index 3b66ad76a2f..6a72b5ddf38 100644 --- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -1887,12 +1887,14 @@ void tst_QTimeZone::roundtripDisplayNames_data() "UTC"_ba, // Those named overtly in tst_QDateTime - special cases first: "UTC-02:00"_ba, "UTC+02:00"_ba, "UTC+12:00"_ba, - "Etc/GMT+3"_ba, "GMT-2"_ba, "GMT"_ba, + "Etc/GMT+3"_ba, "GMT-0"_ba, "GMT"_ba, // ... then ordinary names in alphabetic order: - "America/New_York"_ba, "America/Sao_Paulo"_ba, "America/Vancouver"_ba, - "Asia/Kathmandu"_ba, "Asia/Singapore"_ba, + "America/Anchorage"_ba, "America/Metlakatla"_ba, "America/New_York"_ba, + "America/Sao_Paulo"_ba, "America/Toronto"_ba, "America/Vancouver"_ba, + "Asia/Kathmandu"_ba, "Asia/Manila"_ba, "Asia/Singapore"_ba, "Australia/Brisbane"_ba, "Australia/Eucla"_ba, "Australia/Sydney"_ba, - "Europe/Berlin"_ba, "Europe/Helsinki"_ba, "Europe/Rome"_ba, "Europe/Oslo"_ba, + "Europe/Berlin"_ba, "Europe/Helsinki"_ba, "Europe/Lisbon"_ba, "Europe/Oslo"_ba, + "Europe/Rome"_ba, "Pacific/Apia"_ba, "Pacific/Auckland"_ba, "Pacific/Kiritimati"_ba, "Vulcan/ShiKahr"_ba // Invalid: also worth testing. }; diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index b03750389bd..8ec9cb516d3 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -3153,6 +3153,8 @@ qHash(const StdHashKeyType<HasQHash> &s, size_t seed) template <typename T> void stdHashImpl() { + T::StdHashUsed = false; + QHash<T, int> hash; for (int i = 0; i < 1000; ++i) hash.insert(T{i}, i); diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index e84c35691da..a19cebe3faf 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -121,6 +121,84 @@ Q_DECLARE_TYPEINFO(Movable, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(Movable); +struct NoexceptMovable +{ + NoexceptMovable(char input = 'j') noexcept + : i(input) + { + counter.fetchAndAddRelaxed(1); + } + NoexceptMovable(const NoexceptMovable &other) noexcept + : i(other.i) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + } + NoexceptMovable(NoexceptMovable &&other) noexcept + : i(other.i) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + other.that = nullptr; + } + + ~NoexceptMovable() // implicitly noexcept + { + check(state, Constructed); + i = 0; + counter.fetchAndAddRelaxed(-1); + state = Destructed; // this is likely a dead store + } + + bool operator ==(const NoexceptMovable &other) const noexcept + { + check(state, Constructed); + check(other.state, Constructed); + return i == other.i; + } + + NoexceptMovable &operator=(const NoexceptMovable &other) noexcept + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = this; + return *this; + } + NoexceptMovable &operator=(NoexceptMovable &&other) noexcept + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = other.that; + other.that = nullptr; + return *this; + } + bool wasConstructedAt(const NoexceptMovable *other) const noexcept + { + return that == other; + } + char i; + static inline QAtomicInt counter; +private: + NoexceptMovable *that = this; // used to check if an instance was moved + + enum State { Constructed = 106, Destructed = 110 }; + State state = Constructed; + + static void check(const State state1, const State state2) noexcept + { + QCOMPARE(state1, state2); + } + + friend inline size_t qHash(const NoexceptMovable &key, size_t seed) noexcept + { return qHash(key.i, seed); } +}; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(NoexceptMovable, Q_RELOCATABLE_TYPE); +QT_END_NAMESPACE + struct Custom { Custom(char input = 'j') : i(input) @@ -225,37 +303,47 @@ private slots: void constructors_reserveAndInitialize() const; void copyConstructorInt() const { copyConstructor<int>(); } void copyConstructorMovable() const { copyConstructor<Movable>(); } + void copyConstructorNoexceptMovable() const { copyConstructor<NoexceptMovable>(); } void copyConstructorCustom() const { copyConstructor<Custom>(); } void assignmentInt() const { testAssignment<int>(); } void assignmentMovable() const { testAssignment<Movable>(); } + void assignmentNoexceptMovable() const { testAssignment<NoexceptMovable>(); } void assignmentCustom() const { testAssignment<Custom>(); } void assignFromInitializerListInt() const { assignFromInitializerList<int>(); } void assignFromInitializerListMovable() const { assignFromInitializerList<Movable>(); } + void assignFromInitializerListNoexceptMovable() const { assignFromInitializerList<NoexceptMovable>(); } void assignFromInitializerListCustom() const { assignFromInitializerList<Custom>(); } void addInt() const { add<int>(); } void addMovable() const { add<Movable>(); } + void addNoexceptMovable() const { add<NoexceptMovable>(); } void addCustom() const { add<Custom>(); } void appendInt() const { append<int>(); } void appendMovable() const { append<Movable>(); } + void appendNoexceptMovable() const { append<NoexceptMovable>(); } void appendCustom() const { append<Custom>(); } void appendRvalue() const; void appendList() const; void assignEmpty() const; void assignInt() const { assign<int>(); } void assignMovable() const { assign<Movable>(); } + void assignNoexceptMovable() const { assign<NoexceptMovable>(); } void assignCustom() const { assign<Custom>(); } void assignUsesPrependBuffer_int_data() { assignUsesPrependBuffer_data(); } void assignUsesPrependBuffer_int() const { assignUsesPrependBuffer<int>(); } void assignUsesPrependBuffer_Movable_data() { assignUsesPrependBuffer_data(); } void assignUsesPrependBuffer_Movable() const { assignUsesPrependBuffer<Movable>(); } + void assignUsesPrependBuffer_NoexceptMovable_data() { assignUsesPrependBuffer_data(); } + void assignUsesPrependBuffer_NoexceptMovable() const { assignUsesPrependBuffer<NoexceptMovable>(); } void assignUsesPrependBuffer_Custom_data() { assignUsesPrependBuffer_data(); } void assignUsesPrependBuffer_Custom() const { assignUsesPrependBuffer<Custom>(); } void at() const; void capacityInt() const { capacity<int>(); } void capacityMovable() const { capacity<Movable>(); } + void capacityNoexceptMovable() const { capacity<NoexceptMovable>(); } void capacityCustom() const { capacity<Custom>(); } void clearInt() const { clear<int>(); } void clearMovable() const { clear<Movable>(); } + void clearNoexceptMovable() const { clear<NoexceptMovable>(); } void clearCustom() const { clear<Custom>(); } void constData() const; void constFirst() const; @@ -263,43 +351,54 @@ private slots: void contains() const; void countInt() const { count<int>(); } void countMovable() const { count<Movable>(); } + void countNoexceptMovable() const { count<NoexceptMovable>(); } void countCustom() const { count<Custom>(); } void cpp17ctad() const; void data() const; void reinterpreted() const; void emptyInt() const { empty<int>(); } void emptyMovable() const { empty<Movable>(); } + void emptyNoexceptMovable() const { empty<NoexceptMovable>(); } void emptyCustom() const { empty<Custom>(); } void endsWith() const; void eraseEmptyInt() const { eraseEmpty<int>(); } void eraseEmptyMovable() const { eraseEmpty<Movable>(); } + void eraseEmptyNoexceptMovable() const { eraseEmpty<NoexceptMovable>(); } void eraseEmptyCustom() const { eraseEmpty<Custom>(); } void eraseEmptyReservedInt() const { eraseEmptyReserved<int>(); } void eraseEmptyReservedMovable() const { eraseEmptyReserved<Movable>(); } + void eraseEmptyReservedNoexceptMovable() const { eraseEmptyReserved<NoexceptMovable>(); } void eraseEmptyReservedCustom() const { eraseEmptyReserved<Custom>(); } void eraseInt() const { erase<int>(false); } void eraseIntShared() const { erase<int>(true); } void eraseMovable() const { erase<Movable>(false); } + void eraseNoexceptMovable() const { erase<NoexceptMovable>(false); } void eraseMovableShared() const { erase<Movable>(true); } + void eraseNoexceptMovableShared() const { erase<NoexceptMovable>(true); } void eraseCustom() const { erase<Custom>(false); } void eraseCustomShared() const { erase<Custom>(true); } void eraseReservedInt() const { eraseReserved<int>(); } void eraseReservedMovable() const { eraseReserved<Movable>(); } + void eraseReservedNoexceptMovable() const { eraseReserved<NoexceptMovable>(); } void eraseReservedCustom() const { eraseReserved<Custom>(); } void fillInt() const { fill<int>(); } void fillMovable() const { fill<Movable>(); } + void fillNoexceptMovable() const { fill<NoexceptMovable>(); } void fillCustom() const { fill<Custom>(); } void fillDetachInt() const { fillDetach<int>(); } void fillDetachMovable() const { fillDetach<Movable>(); } + void fillDetachNoexceptMovable() const { fillDetach<NoexceptMovable>(); } void fillDetachCustom() const { fillDetach<Custom>(); } void first() const; void freeSpaceAtBeginEventuallyShrinks() const; void fromListInt() const { fromList<int>(); } void fromListMovable() const { fromList<Movable>(); } + void fromListNoexceptMovable() const { fromList<NoexceptMovable>(); } void fromListCustom() const { fromList<Custom>(); } void indexOf() const; void insertInt() const { insert<int>(); } void insertMovable() const { insert<Movable>(); } + void insertNoexceptMovable() const { insert<NoexceptMovable>(); } void insertCustom() const { insert<Custom>(); } void insertZeroCount_data(); void insertZeroCount() const; @@ -310,17 +409,21 @@ private slots: void sliced() const; void moveInt() const { move<int>(); } void moveMovable() const { move<Movable>(); } + void moveNoexceptMovable() const { move<NoexceptMovable>(); } void moveCustom() const { move<Custom>(); } void prependInt() const { prepend<int>(); } void prependMovable() const { prepend<Movable>(); } + void prependNoexceptMovable() const { prepend<NoexceptMovable>(); } void prependCustom() const { prepend<Custom>(); } void prependRvalue() const; void qhashInt() const { qhash<int>(); } void qhashMovable() const { qhash<Movable>(); } + void qhashNoexceptMovable() const { qhash<NoexceptMovable>(); } void qhashCustom() const { qhash<Custom>(); } void removeAllWithAlias() const; void removeInt() const { remove<int>(); } void removeMovable() const { remove<Movable>(); } + void removeNoexceptMovable() const { remove<NoexceptMovable>(); } void removeCustom() const { remove<Custom>(); } void removeFirstLast() const; void resizePOD_data() const; @@ -339,10 +442,12 @@ private slots: void reverseIterators() const; void sizeInt() const { size<int>(); } void sizeMovable() const { size<Movable>(); } + void sizeNoexceptMovable() const { size<NoexceptMovable>(); } void sizeCustom() const { size<Custom>(); } void startsWith() const; void swapInt() const { swap<int>(); } void swapMovable() const { swap<Movable>(); } + void swapNoexceptMovable() const { swap<NoexceptMovable>(); } void swapCustom() const { swap<Custom>(); } void toAddress() const; void toList() const; @@ -356,10 +461,12 @@ private slots: void reserveZero(); void initializeListInt() { initializeList<int>(); } void initializeListMovable() { initializeList<Movable>(); } + void initializeListNoexceptMovable() { initializeList<NoexceptMovable>(); } void initializeListCustom() { initializeList<Custom>(); } void const_shared_null(); void detachInt() const { detach<int>(); } void detachMovable() const { detach<Movable>(); } + void detachNoexceptMovable() const { detach<NoexceptMovable>(); } void detachCustom() const { detach<Custom>(); } void detachThreadSafetyInt() const; void detachThreadSafetyMovable() const; @@ -369,9 +476,11 @@ private slots: void emplaceInt() { emplaceImpl<int>(); } void emplaceCustom() { emplaceImpl<Custom>(); } void emplaceMovable() { emplaceImpl<Movable>(); } + void emplaceNoexceptMovable() { emplaceImpl<NoexceptMovable>(); } void emplaceConsistentWithStdVectorInt() { emplaceConsistentWithStdVectorImpl<int>(); } void emplaceConsistentWithStdVectorCustom() { emplaceConsistentWithStdVectorImpl<Custom>(); } void emplaceConsistentWithStdVectorMovable() { emplaceConsistentWithStdVectorImpl<Movable>(); } + void emplaceConsistentWithStdVectorNoexceptMovable() { emplaceConsistentWithStdVectorImpl<NoexceptMovable>(); } void emplaceConsistentWithStdVectorQString() { emplaceConsistentWithStdVectorImpl<QString>(); } void emplaceReturnsIterator(); void emplaceFront() const; @@ -383,35 +492,45 @@ private slots: void replaceInt() const { replace<int>(); } void replaceCustom() const { replace<Custom>(); } void replaceMovable() const { replace<Movable>(); } + void replaceNoexceptMovable() const { replace<NoexceptMovable>(); } void fromReadOnlyData() const; void reallocateCustomAlignedType_qtbug90359() const; void reinsertToBeginInt_qtbug91360() const { reinsertToBegin<int>(); } void reinsertToBeginMovable_qtbug91360() const { reinsertToBegin<Movable>(); } + void reinsertToBeginNoexceptMovable_qtbug91360() const { reinsertToBegin<NoexceptMovable>(); } void reinsertToBeginCustom_qtbug91360() const { reinsertToBegin<Custom>(); } void reinsertToEndInt_qtbug91360() const { reinsertToEnd<int>(); } void reinsertToEndMovable_qtbug91360() const { reinsertToEnd<Movable>(); } + void reinsertToEndNoexceptMovable_qtbug91360() const { reinsertToEnd<NoexceptMovable>(); } void reinsertToEndCustom_qtbug91360() const { reinsertToEnd<Custom>(); } void reinsertRangeToEndInt_qtbug91360() const { reinsertRangeToEnd<int>(); } void reinsertRangeToEndMovable_qtbug91360() const { reinsertRangeToEnd<Movable>(); } + void reinsertRangeToEndNoexceptMovable_qtbug91360() const { reinsertRangeToEnd<NoexceptMovable>(); } void reinsertRangeToEndCustom_qtbug91360() const { reinsertRangeToEnd<Custom>(); } // QList reference stability tests: void stability_reserveInt() const { stability_reserve<int>(); } void stability_reserveMovable() const { stability_reserve<Movable>(); } + void stability_reserveNoexceptMovable() const { stability_reserve<NoexceptMovable>(); } void stability_reserveCustom() const { stability_reserve<Custom>(); } void stability_eraseInt() const { stability_erase<int>(); } void stability_eraseMovable() const { stability_erase<Movable>(); } + void stability_eraseNoexceptMovable() const { stability_erase<NoexceptMovable>(); } void stability_eraseCustom() const { stability_erase<Custom>(); } void stability_appendInt() const { stability_append<int>(); } void stability_appendMovable() const { stability_append<Movable>(); } + void stability_appendNoexceptMovable() const { stability_append<NoexceptMovable>(); } void stability_appendCustom() const { stability_append<Custom>(); } void stability_insertElementInt() const { stability_insertElement<int>(); } void stability_insertElementMovable() const { stability_insertElement<Movable>(); } + void stability_insertElementNoexceptMovable() const { stability_insertElement<NoexceptMovable>(); } void stability_insertElementCustom() const { stability_insertElement<Custom>(); } void stability_emplaceInt() const { stability_emplace<int>(); } void stability_emplaceMovable() const { stability_emplace<Movable>(); } + void stability_emplaceNoexceptMovable() const { stability_emplace<NoexceptMovable>(); } void stability_emplaceCustom() const { stability_emplace<Custom>(); } void stability_resizeInt() const { stability_resize<int>(); } void stability_resizeMovable() const { stability_resize<Movable>(); } + void stability_resizeNoexceptMovable() const { stability_resize<NoexceptMovable>(); } void stability_resizeCustom() const { stability_resize<Custom>(); } private: @@ -515,16 +634,9 @@ template<typename T> struct SimpleValue } static const uint MaxIndex = 6; - static const T Values[MaxIndex]; + static inline const T Values[MaxIndex] = { 110, 105, 101, 114, 111, 98 }; }; -template<> -const int SimpleValue<int>::Values[] = { 110, 105, 101, 114, 111, 98 }; -template<> -const Movable SimpleValue<Movable>::Values[] = { 110, 105, 101, 114, 111, 98 }; -template<> -const Custom SimpleValue<Custom>::Values[] = { 110, 105, 101, 114, 111, 98 }; - // Make some macros for the tests to use in order to be slightly more readable... #define T_FOO SimpleValue<T>::at(0) #define T_BAR SimpleValue<T>::at(1) @@ -656,12 +768,36 @@ void tst_QList::assignFromInitializerList() const T val2(SimpleValue<T>::at(2)); T val3(SimpleValue<T>::at(3)); - QList<T> v1 = {val1, val2, val3}; + QList<T> v1 = {}; + QCOMPARE(v1.size(), 0); + + v1 = {val1, val2, val3}; QCOMPARE(v1, QList<T>() << val1 << val2 << val3); QCOMPARE(v1, (QList<T> {val1, val2, val3})); v1 = {}; QCOMPARE(v1.size(), 0); + + // repeat, but make v1 shared before we assign + QList v2 = {val3, val1, val2}; + v1 = v2; + v1 = {val1, val2, val3}; + QCOMPARE(v1, QList<T>() << val1 << val2 << val3); + QCOMPARE(v1, (QList<T> {val1, val2, val3})); + v1 = v2; + v1 = {}; + QCOMPARE(v1.size(), 0); + + // repeat again, but now detached copies + v1 = v2; + v1.detach(); + v1 = {val1, val2, val3}; + QCOMPARE(v1, QList<T>() << val1 << val2 << val3); + QCOMPARE(v1, (QList<T> {val1, val2, val3})); + v1 = v2; + v1.detach(); + v1 = {}; + QCOMPARE(v1.size(), 0); } template<typename T> @@ -834,6 +970,65 @@ void tst_QList::assign() const QVERIFY(!myvecCopy.isSharedWith(myvec)); QCOMPARE(myvecCopy, QList<T>() << T_FOO << T_FOO); } + + // test assigning an empty range to an empty list + { + QList<T> myvec, empty; + + const T *ptr = nullptr; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + ptr = SimpleValue<T>::Values + 3; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + } + + // test assigning empty to non-empty + { + QList<T> nonempty = SimpleValue<T>::vector(2); + QList<T> myvec; + + const T *ptr = nullptr; + myvec = nonempty; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + ptr = SimpleValue<T>::Values + 3; + myvec = nonempty; + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + // repeat, after having detached + ptr = nullptr; + myvec = nonempty; + myvec.detach(); + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + + ptr = SimpleValue<T>::Values + 3; + myvec = nonempty; + myvec.detach(); + myvec.assign(ptr, ptr); + QVERIFY(myvec.isEmpty()); + } + + // assign a smaller range + { + QList<T> myvec = SimpleValue<T>::vector(3); + QCOMPARE(myvec.size(), 3); + + myvec.assign(SimpleValue<T>::Values + 3, SimpleValue<T>::Values + 5); + QCOMPARE(myvec.size(), 2); + QCOMPARE(myvec.at(0), T_CAT); + QCOMPARE(myvec.at(1), T_DOG); + + // with detaching: + QList copy = myvec; + myvec.assign(SimpleValue<T>::Values, SimpleValue<T>::Values + 1); + QCOMPARE(myvec.size(), 1); + QCOMPARE(myvec.at(0), T_FOO); + } } inline namespace Scenarios { @@ -2108,10 +2303,15 @@ void tst_QList::qhash() const TST_QLIST_CHECK_LEAKS(T) QList<T> l1, l2; - QCOMPARE(qHash(l1), qHash(l2)); + QCOMPARE(qHash(l1, 0), qHash(l2, 0)); l1 << SimpleValue<T>::at(0); l2 << SimpleValue<T>::at(0); - QCOMPARE(qHash(l1), qHash(l2)); + QCOMPARE(qHash(l1, 0), qHash(l2, 0)); + + QCOMPARE(qHash(l1, 1), qHash(l2, 1)); + l1.clear(); + l2.clear(); + QCOMPARE(qHash(l1, 1), qHash(l2, 1)); } template <typename T> diff --git a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp index 1dadbb9efea..b3f96588304 100644 --- a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp +++ b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp @@ -26,6 +26,8 @@ private Q_SLOTS: void optionalGuard(); void leavingScope(); void exceptions(); + + void init(); }; void func() @@ -169,7 +171,6 @@ void tst_QScopeGuard::leavingScope() void tst_QScopeGuard::exceptions() { - s_globalState = 0; bool caught = false; QT_TRY { @@ -187,5 +188,10 @@ void tst_QScopeGuard::exceptions() } +void tst_QScopeGuard::init() +{ + s_globalState = 0; +} + QTEST_MAIN(tst_QScopeGuard) #include "tst_qscopeguard.moc" diff --git a/tests/auto/gui/kernel/qsurfaceformat/tst_qsurfaceformat.cpp b/tests/auto/gui/kernel/qsurfaceformat/tst_qsurfaceformat.cpp index 3f655bd905d..28085e1405f 100644 --- a/tests/auto/gui/kernel/qsurfaceformat/tst_qsurfaceformat.cpp +++ b/tests/auto/gui/kernel/qsurfaceformat/tst_qsurfaceformat.cpp @@ -54,10 +54,10 @@ void tst_QSurfaceFormat::versionCheck() format.setMinorVersion(formatMinor); format.setMajorVersion(formatMajor); - QCOMPARE(format.version() >= qMakePair(compareMajor, compareMinor), expected); + QCOMPARE(format.version() >= std::pair(compareMajor, compareMinor), expected); format.setVersion(formatMajor, formatMinor); - QCOMPARE(format.version() >= qMakePair(compareMajor, compareMinor), expected); + QCOMPARE(format.version() >= std::pair(compareMajor, compareMinor), expected); } #include <tst_qsurfaceformat.moc> diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 64add80907a..7dac778d5ff 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -292,6 +292,8 @@ private slots: void alphaBlitToNonAlphaFormats_data(); void alphaBlitToNonAlphaFormats(); + void floatRounding(); + private: void fillData(); void setPenColor(QPainter& p); @@ -3843,26 +3845,23 @@ void tst_QPainter::linearGradientSymmetry_data() QTest::addColumn<QGradientStops>("stops"); if (sizeof(qreal) != sizeof(float)) { - QGradientStops stops; - stops << qMakePair(qreal(0.0), QColor(Qt::blue)); - stops << qMakePair(qreal(0.2), QColor(220, 220, 220, 0)); - stops << qMakePair(qreal(0.6), QColor(Qt::red)); - stops << qMakePair(qreal(0.9), QColor(220, 220, 220, 255)); - stops << qMakePair(qreal(1.0), QColor(Qt::black)); + QGradientStops stops = {{qreal(0.0), QColor(Qt::blue)}, + {qreal(0.2), QColor(220, 220, 220, 0)}, + {qreal(0.6), QColor(Qt::red)}, + {qreal(0.9), QColor(220, 220, 220, 255)}, + {qreal(1.0), QColor(Qt::black)}}; QTest::newRow("multiple stops") << stops; } { - QGradientStops stops; - stops << qMakePair(qreal(0.0), QColor(Qt::blue)); - stops << qMakePair(qreal(1.0), QColor(Qt::black)); + QGradientStops stops = {{qreal(0.0), QColor(Qt::blue)}, + {qreal(1.0), QColor(Qt::black)}}; QTest::newRow("two stops") << stops; } if (sizeof(qreal) != sizeof(float)) { - QGradientStops stops; - stops << qMakePair(qreal(0.3), QColor(Qt::blue)); - stops << qMakePair(qreal(0.6), QColor(Qt::black)); + QGradientStops stops = {{qreal(0.3), QColor(Qt::blue)}, + {qreal(0.6), QColor(Qt::black)}}; QTest::newRow("two stops 2") << stops; } } @@ -3913,12 +3912,10 @@ void tst_QPainter::gradientPixelFormat() QImage a(8, 64, QImage::Format_ARGB32_Premultiplied); QImage b(8, 64, format); - - QGradientStops stops; - stops << qMakePair(qreal(0.0), QColor(Qt::blue)); - stops << qMakePair(qreal(0.3), QColor(Qt::red)); - stops << qMakePair(qreal(0.6), QColor(Qt::green)); - stops << qMakePair(qreal(1.0), QColor(Qt::black)); + QGradientStops stops = {{qreal(0.0), QColor(Qt::blue)}, + {qreal(0.3), QColor(Qt::red)}, + {qreal(0.6), QColor(Qt::green)}, + {qreal(1.0), QColor(Qt::black)}}; a.fill(0); b.fill(0); @@ -5751,6 +5748,30 @@ void tst_QPainter::alphaBlitToNonAlphaFormats() } } +void tst_QPainter::floatRounding() +{ + // oss-fuzz issue 429123947 + // The following triggered an assert in QDashStroker::processCurrentSubpath(): "dpos >= 0" + // when it expected the calculation's result to be zero but it was actually smaller: + // qreal(4) + qreal(0.1) - qreal(0.1) - qreal(4) + // actual result: -4.440892098500626e-16 + QImage img(5, 5, QImage::Format_RGB888); + QPainter p(&img); + + QList<qreal> pattern {0.1, 0.3, 0.1, 0.1, 0.3, 0.1}; + QPainterPathStroker stroker; + stroker.setDashPattern(pattern); + + QPainterPath pp; + pp.moveTo(4.0, 0.0); + pp.lineTo(0.1, 0.0); + pp.lineTo(0.0, 0.0); + pp.lineTo(0.0, 5.0); + + QPolygonF poly = stroker.createStroke(pp).toFillPolygon(); + p.drawPolygon(poly); +} + QTEST_MAIN(tst_QPainter) #include "tst_qpainter.moc" diff --git a/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp b/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp index bd9e00fc376..7b4193e3749 100644 --- a/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp +++ b/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp @@ -86,6 +86,9 @@ private slots: void intersectionPointOnEdge(); void boundsAtStartPoint(); + + void percentAtEmptyPath_data(); + void percentAtEmptyPath(); }; void tst_QPainterPath::cleanupTestCase() @@ -1637,6 +1640,29 @@ void tst_QPainterPath::boundsAtStartPoint() QCOMPARE(constructedPath.controlPointRect(), defaultPath.controlPointRect()); } +void tst_QPainterPath::percentAtEmptyPath_data() +{ + const QPointF qpf(2., 2.); + QTest::addColumn<QPainterPath>("path"); + + QTest::newRow("defaultConstructed") << QPainterPath(); + QTest::newRow("withStartPoint") << QPainterPath(qpf); + QPainterPath pp(qpf); + pp.lineTo(qpf); + QTest::newRow("equalPoints") << pp; +} + +void tst_QPainterPath::percentAtEmptyPath() +{ + QFETCH(QPainterPath, path); + + path.angleAtPercent(0.5); + path.pointAtPercent(0.5); + path.slopeAtPercent(0.5); + + path.percentAtLength(0); +} + QTEST_APPLESS_MAIN(tst_QPainterPath) #include "tst_qpainterpath.moc" diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index ab0e8693644..511007e0ae4 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -3525,7 +3525,7 @@ void tst_QRhi::renderToTextureMultipleUniformBuffersAndDynamicOffset() // "see" an all zero matrix and zero opacity, thus leading to different // rendering output. This way we can verify if using dynamic offsets, and // more than one at the same time, is functional. - QVarLengthArray<QPair<int, quint32>, 2> dynamicOffset = { + QVarLengthArray<std::pair<int, quint32>, 2> dynamicOffset = { { 0, quint32(ubufElemSize * 2) }, { 1, quint32(ubuf2ElemSize * 3) }, }; diff --git a/tests/auto/gui/rhi/qshader/tst_qshader.cpp b/tests/auto/gui/rhi/qshader/tst_qshader.cpp index 9e179c95c35..371da0d800c 100644 --- a/tests/auto/gui/rhi/qshader/tst_qshader.cpp +++ b/tests/auto/gui/rhi/qshader/tst_qshader.cpp @@ -279,7 +279,7 @@ void tst_QShader::mslResourceMapping() QCOMPARE(resMap.size(), 2); QCOMPARE(resMap.value(0).first, 0); // mapped to native buffer index 0 - QCOMPARE(resMap.value(1), qMakePair(0, 0)); // mapped to native texture index 0 and sampler index 0 + QCOMPARE(resMap.value(1), std::pair(0, 0)); // mapped to native texture index 0 and sampler index 0 } void tst_QShader::serializeShaderDesc() @@ -667,7 +667,8 @@ void tst_QShader::loadV7() QCOMPARE(tese.description().inputBuiltinVariables()[3].type, QShaderDescription::TessCoordBuiltin); QCOMPARE(tese.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))).size(), 1); - QCOMPARE(tese.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))).value(0), qMakePair(0, -1)); + QCOMPARE(tese.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))).value(0), + std::pair(0, -1)); QShader frag = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.frag.qsb")); QVERIFY(frag.isValid()); diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index fa1d70a942b..fdc2dde7921 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -781,7 +781,7 @@ private: void parseContentLength() { - int index = receivedData.indexOf("content-length:"); + int index = receivedData.toLower().indexOf("content-length:"); if (index == -1) return; @@ -3620,7 +3620,7 @@ void tst_QNetworkReply::connectToIPv6Address() QByteArray content = reply->readAll(); //qDebug() << server.receivedData; QByteArray hostinfo = "\r\nhost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n"; - QVERIFY(server.receivedData.contains(hostinfo)); + QVERIFY(server.receivedData.toLower().contains(hostinfo)); QCOMPARE(content, dataToSend); QCOMPARE(reply->url(), request.url()); QCOMPARE(reply->error(), error); @@ -8793,7 +8793,11 @@ void tst_QNetworkReply::httpUserAgent() QVERIFY(reply->isFinished()); QCOMPARE(reply->error(), QNetworkReply::NoError); - QVERIFY(server.receivedData.contains("\r\nuser-agent: abcDEFghi\r\n")); + const char userAgentSearch[] = "\r\nuser-agent: "; + qsizetype userAgentIndex = server.receivedData.toLower().indexOf(userAgentSearch); + QCOMPARE_NE(userAgentIndex, -1); + userAgentIndex += sizeof(userAgentSearch) - 1; + QVERIFY(server.receivedData.slice(userAgentIndex).startsWith("abcDEFghi\r\n")); } void tst_QNetworkReply::synchronousAuthenticationCache() @@ -8813,7 +8817,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache() "content-type: text/plain\r\n" "\r\n" "auth"; - QRegularExpression rx("authorization: Basic ([^\r\n]*)\r\n"); + QRegularExpression rx("[Aa]uthorization: Basic ([^\r\n]*)\r\n"); QRegularExpressionMatch match = rx.match(receivedData); if (match.hasMatch()) { if (QByteArray::fromBase64(match.captured(1).toLatin1()) == "login:password") { @@ -9526,7 +9530,7 @@ void tst_QNetworkReply::ioHttpCookiesDuringRedirect() manager.setRedirectPolicy(oldRedirectPolicy); QVERIFY(waitForFinish(reply) == Success); - QVERIFY(target.receivedData.contains("\r\ncookie: hello=world\r\n")); + QVERIFY(target.receivedData.toLower().contains("\r\ncookie: hello=world\r\n")); QVERIFY(validateRedirectedResponseHeaders(reply)); } @@ -10439,7 +10443,7 @@ void tst_QNetworkReply::contentEncoding() { // Check that we included the content encoding method in our Accept-Encoding header const QByteArray &receivedData = server.receivedData; - int start = receivedData.indexOf("accept-encoding"); + int start = receivedData.toLower().indexOf("accept-encoding"); QVERIFY(start != -1); int end = receivedData.indexOf("\r\n", start); QVERIFY(end != -1); diff --git a/tests/auto/network/access/qnetworkreply_local/minihttpserver.h b/tests/auto/network/access/qnetworkreply_local/minihttpserver.h index daad88cdbcc..ae1069d7a7d 100644 --- a/tests/auto/network/access/qnetworkreply_local/minihttpserver.h +++ b/tests/auto/network/access/qnetworkreply_local/minihttpserver.h @@ -152,7 +152,7 @@ private: void parseContentLength(State &st, QByteArrayView header) { - qsizetype index = header.indexOf("\r\ncontent-length:"); + qsizetype index = header.toByteArray().toLower().indexOf("\r\ncontent-length:"); if (index == -1) return; st.foundContentLength = true; diff --git a/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp index 8bed904c230..977a047c58e 100644 --- a/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp +++ b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp @@ -270,7 +270,7 @@ void tst_QNetworkReply_local::fullServerName() QVERIFY(receivedData.startsWith(expectedGet)); const QByteArray expectedHost = "host: " % url.host().toUtf8() % "\r\n"; - QVERIFY(receivedData.contains(expectedHost)); + QVERIFY(receivedData.toLower().contains(expectedHost)); } #endif diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp index 0d2b7bfde64..7417f0648e4 100644 --- a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp +++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp @@ -30,6 +30,7 @@ public: tst_QHostAddress(); private slots: + void constructor(); void constructor_QString_data(); void constructor_QString(); void setAddress_QString_data(); @@ -62,6 +63,43 @@ tst_QHostAddress::tst_QHostAddress() qRegisterMetaType<QHostAddress>("QHostAddress"); } +static void verifyClear(const QHostAddress &addr) +{ + QVERIFY(addr.isNull()); + QVERIFY(!addr.isLoopback()); + QVERIFY(!addr.isGlobal()); + QVERIFY(!addr.isLinkLocal()); + QVERIFY(!addr.isSiteLocal()); + QVERIFY(!addr.isUniqueLocalUnicast()); + QVERIFY(!addr.isMulticast()); + QVERIFY(!addr.isBroadcast()); + QVERIFY(!addr.isPrivateUse()); + QCOMPARE(addr, QHostAddress()); + QCOMPARE(addr, QHostAddress::Null); + QCOMPARE(addr.protocol(), QHostAddress::UnknownNetworkLayerProtocol); + + bool ok = true; + QCOMPARE(addr.toIPv4Address(&ok), 0); + QVERIFY(!ok); + + QCOMPARE(QByteArrayView::fromArray(addr.toIPv6Address().c), + QByteArrayView::fromArray(QIPv6Address{}.c)); + QCOMPARE(addr.scopeId(), QString()); + + QCOMPARE(addr.toString(), QString()); + + size_t seed = QHashSeed::globalSeed(); + QCOMPARE(qHash(addr, seed), qHash(QHostAddress(), seed)); + seed = 0; + QCOMPARE(qHash(addr, seed), qHash(QHostAddress(), seed)); +} + +void tst_QHostAddress::constructor() +{ + QHostAddress addr; + verifyClear(addr); +} + void tst_QHostAddress::constructor_QString_data() { setAddress_QString_data(); @@ -94,6 +132,9 @@ void tst_QHostAddress::constructor_QString() QVERIFY( hostAddr.isNull() ); QVERIFY( hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol ); } + + hostAddr.clear(); + verifyClear(hostAddr); } void tst_QHostAddress::setAddress_QString_data() @@ -201,6 +242,9 @@ void tst_QHostAddress::setAddress_QString() QVERIFY( hostAddr.isNull() ); QVERIFY( hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol ); } + + hostAddr.clear(); + verifyClear(hostAddr); } void tst_QHostAddress::specialAddresses_data() @@ -239,6 +283,7 @@ void tst_QHostAddress::specialAddresses() QFETCH(QHostAddress::SpecialAddress, address); QFETCH(bool, result); QCOMPARE(QHostAddress(text) == address, result); + size_t seed = QHashSeed::globalSeed(); //check special address equal to itself (QTBUG-22898), note two overloads of operator== QVERIFY(QHostAddress(address) == QHostAddress(address)); @@ -252,11 +297,19 @@ void tst_QHostAddress::specialAddresses() QHostAddress ha; ha.setAddress(address); QVERIFY(ha == address); + QCOMPARE(qHash(ha, seed), qHash(QHostAddress(address), seed)); + QCOMPARE(qHash(ha, 0), qHash(QHostAddress(address), 0)); } QHostAddress setter; setter.setAddress(text); - QCOMPARE(setter == address, result); + if (result) { + QCOMPARE(setter, address); + QCOMPARE(qHash(setter, seed), qHash(QHostAddress(address), seed)); + QCOMPARE(qHash(setter, 0), qHash(QHostAddress(address), 0)); + } else { + QCOMPARE_NE(setter, address); + } } @@ -288,8 +341,11 @@ void tst_QHostAddress::compare() QCOMPARE(first == second, result); QCOMPARE(second == first, result); - if (result == true) - QCOMPARE(qHash(first), qHash(second)); + if (result == true) { + size_t seed = QHashSeed::globalSeed(); + QCOMPARE(qHash(first, seed), qHash(second, seed)); + QCOMPARE(qHash(first, 0), qHash(second, 0)); + } } void tst_QHostAddress::isEqual_data() @@ -370,6 +426,11 @@ void tst_QHostAddress::scopeId() address2 = address; QCOMPARE(address2.scopeId(), QString("eth0")); QCOMPARE(address2.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%eth0")); + + address.clear(); + verifyClear(address); + address2.clear(); + verifyClear(address2); } void tst_QHostAddress::hashKey() diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp index 4814206cd75..40292f0c44d 100644 --- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -132,6 +132,7 @@ private: bool m_skipUnsupportedIPv6Tests; bool m_workaroundLinuxKernelBug; + int loopbackInterface = 0; // unknown by default QList<QHostAddress> allAddresses; QHostAddress multicastGroup4, multicastGroup6; QList<QHostAddress> linklocalMulticastGroups; @@ -140,6 +141,16 @@ private: QUdpSocket *m_asyncReceiver; }; +// Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of obtaining +// the packet's destination addresses. This means the destinationAddress() be +// empty, so whitelist the OSes for which we know we have an implementation. +// That's currently all of them, which means there probably is code in this +// test that assumes this works without checking this variable. +// +// Note: this applies to single-stack operations; dual stack implementations +// appear to be buggy or not present at all in some OSes. +static constexpr bool HasWorkingIPv4DestinationAddress = true; + #ifdef SHOULD_CHECK_SYSCALL_SUPPORT bool tst_QUdpSocket::ipv6SetsockoptionMissing(int level, int optname) { @@ -246,7 +257,23 @@ void tst_QUdpSocket::initTestCase() if (!QtNetworkSettings::verifyTestNetworkSettings()) QSKIP("No network test server available"); #endif - allAddresses = QNetworkInterface::allAddresses(); + + allAddresses.clear(); + for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) { + if (!iface.flags().testAnyFlags(QNetworkInterface::IsUp)) + continue; + if (iface.flags().testAnyFlags(QNetworkInterface::IsLoopBack)) + loopbackInterface = iface.index(); + + // add this interface's addresses + const QList<QNetworkAddressEntry> addresses = iface.addressEntries(); + for (const QNetworkAddressEntry &entry : addresses) { + allAddresses += entry.ip(); + if (!loopbackInterface && entry.ip().isLoopback()) + loopbackInterface = iface.index(); + } + } + m_skipUnsupportedIPv6Tests = shouldSkipIpv6TestsForBrokenSetsockopt(); // Create a pair of random multicast groups so we avoid clashing with any @@ -568,12 +595,12 @@ void tst_QUdpSocket::broadcasting() QVERIFY2(allAddresses.contains(dgram.senderAddress()), dgram.senderAddress().toString().toLatin1()); QCOMPARE(dgram.senderPort(), int(broadcastSocket.localPort())); - if (!dgram.destinationAddress().isNull()) { + if (HasWorkingIPv4DestinationAddress) { QVERIFY2(dgram.destinationAddress() == QHostAddress::Broadcast || broadcastAddresses.contains(dgram.destinationAddress()), dgram.destinationAddress().toString().toLatin1()); - QCOMPARE(dgram.destinationPort(), int(serverSocket.localPort())); } + QCOMPARE(dgram.destinationPort(), int(serverSocket.localPort())); int ttl = dgram.hopLimit(); if (ttl != -1) @@ -738,15 +765,7 @@ void tst_QUdpSocket::loop() QCOMPARE(paulDatagram.senderPort(), int(peter.localPort())); QCOMPARE(peterDatagram.senderPort(), int(paul.localPort())); - // Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of - // obtaining the packet's destination addresses. The destinationAddress and - // destinationPort calls could fail, so whitelist the OSes for which we - // know we have an implementation. -#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN) - QVERIFY(peterDatagram.destinationPort() != -1); - QVERIFY(paulDatagram.destinationPort() != -1); -#endif - if (peterDatagram.destinationPort() == -1) { + if (!HasWorkingIPv4DestinationAddress) { QCOMPARE(peterDatagram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol); QCOMPARE(paulDatagram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol); } else { @@ -754,6 +773,11 @@ void tst_QUdpSocket::loop() QCOMPARE(paulDatagram.destinationAddress(), makeNonAny(paul.localAddress())); QVERIFY(peterDatagram.destinationAddress().isEqual(makeNonAny(peter.localAddress()))); QVERIFY(paulDatagram.destinationAddress().isEqual(makeNonAny(paul.localAddress()))); + + if (loopbackInterface) { + QCOMPARE(peterDatagram.interfaceIndex(), loopbackInterface); + QCOMPARE(paulDatagram.interfaceIndex(), loopbackInterface); + } } } @@ -820,6 +844,11 @@ void tst_QUdpSocket::ipv6Loop() QCOMPARE(paulDatagram.destinationAddress(), makeNonAny(paul.localAddress())); QCOMPARE(peterDatagram.destinationPort(), peterPort); QCOMPARE(paulDatagram.destinationPort(), paulPort); + + if (loopbackInterface) { + QCOMPARE(peterDatagram.interfaceIndex(), loopbackInterface); + QCOMPARE(paulDatagram.interfaceIndex(), loopbackInterface); + } } void tst_QUdpSocket::dualStack() @@ -850,6 +879,8 @@ void tst_QUdpSocket::dualStack() QCOMPARE(dgram.destinationPort(), int(dualSock.localPort())); QVERIFY(dgram.destinationAddress().isEqual(makeNonAny(dualSock.localAddress(), QHostAddress::LocalHost))); } else { + // Observed on QNX: the IPV6_PKTINFO ancillary data appears to be + // missing if the sender is IPv4. qInfo("Getting IPv4 destination address failed."); } @@ -889,12 +920,11 @@ void tst_QUdpSocket::dualStack() QCOMPARE(dgram.data(), dualData); QCOMPARE(dgram.senderPort(), int(dualSock.localPort())); QCOMPARE(dgram.senderAddress(), makeNonAny(dualSock.localAddress(), QHostAddress::LocalHost)); -#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN) - QVERIFY(dgram.destinationPort() != -1); -#endif - if (dgram.destinationPort() != -1) { - QCOMPARE(dgram.destinationPort(), int(v4Sock.localPort())); + QCOMPARE(dgram.destinationPort(), int(v4Sock.localPort())); + if (HasWorkingIPv4DestinationAddress) { QCOMPARE(dgram.destinationAddress(), makeNonAny(v4Sock.localAddress(), QHostAddress::LocalHost)); + if (loopbackInterface) + QCOMPARE(dgram.interfaceIndex(), loopbackInterface); } } @@ -1748,10 +1778,10 @@ void tst_QUdpSocket::multicast() QVERIFY2(allAddresses.contains(dgram.senderAddress()), dgram.senderAddress().toString().toLatin1()); QCOMPARE(dgram.senderPort(), int(sender.localPort())); - if (!dgram.destinationAddress().isNull()) { + if (HasWorkingIPv4DestinationAddress) { QCOMPARE(dgram.destinationAddress(), groupAddress); - QCOMPARE(dgram.destinationPort(), int(receiver.localPort())); } + QCOMPARE(dgram.destinationPort(), int(receiver.localPort())); int ttl = dgram.hopLimit(); if (ttl != -1) @@ -1962,19 +1992,12 @@ void tst_QUdpSocket::linkLocalIPv4() QCOMPARE(dgram.data().size(), testData.size()); QCOMPARE(dgram.data(), testData); - // Unlike for IPv6 with IPV6_PKTINFO, IPv4 has no standardized way of - // obtaining the packet's destination addresses. The destinationAddress - // and destinationPort calls could fail, so whitelist the OSes we know - // we have an implementation. -#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) || defined(Q_OS_WIN) - QVERIFY(dgram.destinationPort() != -1); -#endif - if (dgram.destinationPort() == -1) { + if (!HasWorkingIPv4DestinationAddress) { QCOMPARE(dgram.destinationAddress().protocol(), QAbstractSocket::UnknownNetworkLayerProtocol); } else { QCOMPARE(dgram.destinationAddress(), s->localAddress()); - QCOMPARE(dgram.destinationPort(), int(neutral.localPort())); } + QCOMPARE(dgram.destinationPort(), int(neutral.localPort())); QVERIFY(neutral.writeDatagram(dgram.makeReply(testData))); QVERIFY2(s->waitForReadyRead(10000), QtNetworkSettings::msgSocketError(*s).constData()); 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/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index 17acdea014e..2f0cbf78b25 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -25,6 +25,7 @@ #include <QtWidgets/qtablewidget.h> #include <QtGui/qevent.h> +#include <QtGui/qstylehints.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/qmimedata.h> @@ -3333,7 +3334,7 @@ void tst_QGraphicsProxyWidget::clickFocus() EventSpy proxySpy(proxy); EventSpy widgetSpy(proxy->widget()); - view.setFrameStyle(0); + view.setFrameStyle(QFrame::NoFrame); view.resize(300, 300); view.show(); QVERIFY(QTest::qWaitForWindowFocused(&view)); @@ -3469,10 +3470,12 @@ void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget() auto *hoverButton = new HoverButton(background); hoverButton->setText("Second button"_L1); hoverButton->setGeometry(10, 10, 200, 50); + hoverButton->setAttribute(Qt::WA_Hover, true); scene.addWidget(background); auto *hideButton = new QPushButton("I'm a button with a very very long text"_L1); hideButton->setGeometry(10, 10, 400, 50); + hideButton->setAttribute(Qt::WA_Hover, true); QGraphicsProxyWidget *topButton = scene.addWidget(hideButton); connect(hideButton, &QPushButton::clicked, &scene, [&]() { topButton->hide(); }); topButton->setFocus(); @@ -3659,6 +3662,11 @@ public: return QWidget::event(event); } + void paintEvent(QPaintEvent *) override + { + QPainter p(this); + p.fillRect(rect(), Qt::green); + } }; #if QT_CONFIG(wheelevent) @@ -3676,6 +3684,11 @@ public: */ void tst_QGraphicsProxyWidget::wheelEventPropagation() { + qApp->styleHints()->setWheelScrollLines(3); + const auto guard = qScopeGuard([&]() { + qApp->styleHints()->setWheelScrollLines(-1); + }); + QGraphicsScene scene(0, 0, 600, 600); auto *label = new QLabel("Direct"_L1); @@ -3697,6 +3710,11 @@ void tst_QGraphicsProxyWidget::wheelEventPropagation() int wheelEventCount = 0; protected: + void paintEvent(QPaintEvent *) override + { + QPainter p(this); + p.fillRect(rect(), Qt::green); + } void wheelEvent(QWheelEvent *) override { ++wheelEventCount; @@ -3710,10 +3728,13 @@ void tst_QGraphicsProxyWidget::wheelEventPropagation() QGraphicsView view(&scene); view.setFixedHeight(200); + view.setFixedWidth(700); + view.setFrameStyle(QFrame::NoFrame); view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.verticalScrollBar()->isVisible()); + QVERIFY(!view.horizontalScrollBar()->isVisible()); view.verticalScrollBar()->setValue(0); QSignalSpy scrollSpy(view.verticalScrollBar(), &QScrollBar::valueChanged); @@ -3729,6 +3750,8 @@ void tst_QGraphicsProxyWidget::wheelEventPropagation() phase); QCoreApplication::processEvents(); }; + auto vs = view.verticalScrollBar(); + vs->setSingleStep(9); // each wheel event: wheelScrollLines() * 9 = 27px qsizetype scrollCount = 0; // test non-kinetic events; they are not grabbed, and should scroll the view unless @@ -3816,6 +3839,7 @@ void tst_QGraphicsProxyWidget::forwardTouchEvent() QGraphicsView view(&scene); view.show(); + view.setFrameStyle(QFrame::NoFrame); QVERIFY(QTest::qWaitForWindowActive(&view)); EventSpy eventSpy(widget); @@ -3832,16 +3856,14 @@ void tst_QGraphicsProxyWidget::forwardTouchEvent() QTest::touchEvent(&view, device.get()).move(0, QPoint(16, 16), &view); QTest::touchEvent(&view, device.get()).release(0, QPoint(15, 15), &view); - QApplication::processEvents(); - - QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 1); - QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 2); - QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 1); + QTRY_COMPARE(eventSpy.counts[QEvent::TouchBegin], 1); + QTRY_COMPARE(eventSpy.counts[QEvent::TouchUpdate], 2); + QTRY_COMPARE(eventSpy.counts[QEvent::TouchEnd], 1); } void tst_QGraphicsProxyWidget::touchEventPropagation() { - QGraphicsScene scene(0, 0, 300, 200); + QGraphicsScene scene; auto *simpleWidget = new QWidget; simpleWidget->setObjectName("simpleWidget"); simpleWidget->setAttribute(Qt::WA_AcceptTouchEvents, true); @@ -3873,15 +3895,26 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() vbox->addWidget(touchWidget2); QGraphicsProxyWidget *formProxy = scene.addWidget(formWidget); formProxy->setAcceptTouchEvents(true); - formProxy->setGeometry(QRectF(50, 50, 200, 160)); + const auto minSize = formWidget->minimumSize(); + formProxy->setGeometry(QRectF(50, 50, minSize.width(), minSize.height())); + + // topLeft must be 0/0 + const auto sceneRect = scene.sceneRect(); + scene.setSceneRect(QRectF(0, 0, sceneRect.width(), sceneRect.height())); QGraphicsView view(&scene); + // by setting NoFrame, view and view.viewport() have the same + // coordinate system + view.setFrameStyle(QFrame::NoFrame); + // make sure to not have scrollbars view.setFixedSize(scene.width(), scene.height()); view.verticalScrollBar()->setValue(0); view.horizontalScrollBar()->setValue(0); view.viewport()->setObjectName("GraphicsView's Viewport"); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); class TouchEventSpy : public QObject { @@ -3913,8 +3946,12 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() case QEvent::TouchEnd: { auto *touchEvent = static_cast<QTouchEvent *>(event); // instead of detaching each QEventPoint, just store the relative positions - for (const auto &touchPoint : touchEvent->points()) - records[touchPoint.id()] << TouchRecord{receiver, event->type(), touchPoint.position()}; + for (const auto& touchPoint : touchEvent->points()) { + records[touchPoint.id()] + << TouchRecord{ receiver, event->type(), touchPoint.position() }; + qCDebug(lcTests) << touchPoint.id() << "recv:" << receiver << "type" + << event->type() << "pos" << touchPoint.position(); + } qCDebug(lcTests) << "Recording" << event << receiver; break; } @@ -3944,9 +3981,9 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() }; // verify that the embedded widget gets the correctly translated event - QTest::touchEvent(&view, touchDevice.get()).press(0, simpleCenter.toPoint()); + QTest::touchEvent(&view, touchDevice.get()).press(0, view.mapFromScene(simpleCenter)); // window, viewport, scene, simpleProxy, simpleWidget - QCOMPARE(eventSpy.count(), 5); + QTRY_COMPARE(eventSpy.count(), 5); QCOMPARE(eventSpy.at(0).receiver, view.windowHandle()); QCOMPARE(eventSpy.at(1).receiver, view.viewport()); QCOMPARE(eventSpy.at(2).receiver, &scene); @@ -3969,7 +4006,7 @@ void tst_QGraphicsProxyWidget::touchEventPropagation() QCOMPARE(formWidget->childAt(touchWidget2->pos() + tw2Center), touchWidget2); // touch events are sent to the view, in view coordinates - const QPoint formProxyPox = view.mapFromScene(formProxy->pos().toPoint()); + const QPoint formProxyPox = view.mapFromScene(formProxy->pos()); const QPoint pb1TouchPos = pushButton1->pos() + pb1Center + formProxyPox; const QPoint pb2TouchPos = pushButton2->pos() + pb2Center + formProxyPox; const QPoint tw1TouchPos = touchWidget1->pos() + tw1Center + formProxyPox; diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index df19ea1568e..48310996c46 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -2058,28 +2058,21 @@ void tst_QGraphicsView::mapFromSceneRect() QWidget topLevel; QGraphicsView view(&scene,&topLevel); view.rotate(90); - view.setFixedSize(200, 200); - view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + const auto fw = view.frameWidth() * 2; + view.setFixedSize(200 + fw, 200 + fw); topLevel.show(); - QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(QTest::qWaitForWindowActive(&topLevel)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); - QPolygon polygon; - polygon << QPoint(98, 98); - polygon << QPoint(98, 108); - polygon << QPoint(88, 108); - polygon << QPoint(88, 98); - - - QPolygon viewPolygon = view.mapFromScene(0, 0, 10, 10); - for (int i = 0; i < 4; ++i) { - QVERIFY(qAbs(viewPolygon[i].x() - polygon[i].x()) < 3); - QVERIFY(qAbs(viewPolygon[i].y() - polygon[i].y()) < 3); - } + const QRectF input(0, 0, 10, 10); + // fixed size is 200x200 so center is 100x100 + const QPolygon polygon{ QPoint(100, 100), QPoint(100, 110), QPoint(90, 110), QPoint(90, 100) }; + const QPolygon viewPolygon = view.mapFromScene(input); + QCOMPARE(viewPolygon, polygon); QPoint pt = view.mapFromScene(QPointF()); - QPolygon p; - p << pt << pt << pt << pt; + QPolygon p{ pt, pt, pt, pt }; QCOMPARE(view.mapFromScene(QRectF()), p); } @@ -2088,28 +2081,18 @@ void tst_QGraphicsView::mapFromScenePoly() QGraphicsScene scene; QGraphicsView view(&scene); view.rotate(90); - view.setFixedSize(200, 200); - view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + const auto fw = view.frameWidth() * 2; + view.setFixedSize(200 + fw, 200 + fw); view.show(); - - QPolygonF polygon; - polygon << QPoint(0, 0); - polygon << QPoint(10, 0); - polygon << QPoint(10, 10); - polygon << QPoint(0, 10); - - QPolygon polygon2; - polygon2 << QPoint(98, 98); - polygon2 << QPoint(98, 108); - polygon2 << QPoint(88, 108); - polygon2 << QPoint(88, 98); - - QPolygon viewPolygon = view.mapFromScene(polygon); - for (int i = 0; i < 4; ++i) { - QVERIFY(qAbs(viewPolygon[i].x() - polygon2[i].x()) < 3); - QVERIFY(qAbs(viewPolygon[i].y() - polygon2[i].y()) < 3); - } + QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); + + const QPolygonF input{ QPoint(0, 0), QPoint(10, 0), QPoint(10, 10), QPoint(0, 10) }; + // fixed size is 200x200 so center is 100x100 + const QPolygon polygon{ QPoint(100, 100), QPoint(100, 110), QPoint(90, 110), QPoint(90, 100) }; + const QPolygon viewPolygon = view.mapFromScene(input); + QCOMPARE(viewPolygon, polygon); } void tst_QGraphicsView::mapFromScenePath() @@ -2117,34 +2100,22 @@ void tst_QGraphicsView::mapFromScenePath() QGraphicsScene scene; QGraphicsView view(&scene); view.rotate(90); - view.setFixedSize(200, 200); - view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + const auto fw = view.frameWidth() * 2; + view.setFixedSize(200 + fw, 200 + fw); view.show(); - - QPolygonF polygon; - polygon << QPoint(0, 0); - polygon << QPoint(10, 0); - polygon << QPoint(10, 10); - polygon << QPoint(0, 10); + QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(!view.horizontalScrollBar()->isVisible()); + QVERIFY(!view.verticalScrollBar()->isVisible()); + + const QPolygonF input{ QPoint(0, 0), QPoint(10, 0), QPoint(10, 10), QPoint(0, 10) }; + QPainterPath inputPath; + inputPath.addPolygon(input); + // fixed size is 200x200 so center is 100x100 + const QPolygon polygon{ QPoint(100, 100), QPoint(100, 110), QPoint(90, 110), QPoint(90, 100) }; QPainterPath path; path.addPolygon(polygon); - - QPolygon polygon2; - polygon2 << QPoint(98, 98); - polygon2 << QPoint(98, 108); - polygon2 << QPoint(88, 108); - polygon2 << QPoint(88, 98); - QPainterPath path2; - path2.addPolygon(polygon2); - - QPolygonF pathPoly = view.mapFromScene(path).toFillPolygon(); - QPolygonF path2Poly = path2.toFillPolygon(); - - for (int i = 0; i < pathPoly.size(); ++i) { - QVERIFY(qAbs(pathPoly[i].x() - path2Poly[i].x()) < 3); - QVERIFY(qAbs(pathPoly[i].y() - path2Poly[i].y()) < 3); - } + QPainterPath viewPath = view.mapFromScene(inputPath); + QCOMPARE(viewPath, path); } void tst_QGraphicsView::sendEvent() 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 diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp index 4de87cdb7a4..704770edfcc 100644 --- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp +++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp @@ -19,8 +19,9 @@ #include <QTranslator> #include <qscreen.h> -#include <qobject.h> +#include <QtCore/qobject.h> #include <QtCore/qscopeguard.h> +#include <QtCore/qtimer.h> #include <QtWidgets/private/qapplication_p.h> @@ -129,6 +130,8 @@ private slots: void pressDragRelease_data(); void pressDragRelease(); + void setActiveAction_disablesSloppyTimer(); + protected slots: void onSimpleActivated( QAction*); void onComplexActionTriggered(); @@ -1525,6 +1528,50 @@ void tst_QMenuBar::pressDragRelease() QTRY_COMPARE(triggeredSpy.size(), 1); } +void tst_QMenuBar::setActiveAction_disablesSloppyTimer() +{ + QMenuBar menuBar; + QMenu *menu = new QMenu(&menuBar); + menuBar.addMenu(menu); + + QAction *item1 = menu->addAction("Item 1"); + QAction *item2 = menu->addAction("Item 2"); + + // Create submenu for first item + QMenu *submenu = new QMenu(&menuBar); + submenu->addAction("Subitem 1"); + submenu->addAction("Subitem 2"); + submenu->addAction("Subitem 3"); + item1->setMenu(submenu); + + using namespace std::chrono_literals; + + QTimer::singleShot(0, [&]() { + menu->show(); + }); + + QTimer::singleShot(100ms, [&]() { + menu->setActiveAction(item1); + QCOMPARE_EQ(menu->activeAction(), item1); + }); + + QTimer::singleShot(200ms, [&] { + menu->setActiveAction(item2); + QCOMPARE_EQ(menu->activeAction(), item2); + }); + + bool done = false; + // QTBUG-138956: sloppy timer should not fire when calling setActiveAction + QTimer::singleShot(2s, [&] { + QCOMPARE_EQ(menu->activeAction(), item2); + done = true; + }); + + QVERIFY(QTest::qWaitFor([&]{ + return done; + })); +} + // QTBUG-56526 void tst_QMenuBar::platformMenu() { diff --git a/tests/benchmarks/corelib/time/qtimezone/tst_bench_qtimezone.cpp b/tests/benchmarks/corelib/time/qtimezone/tst_bench_qtimezone.cpp index 3b58b6927ae..fbcce88fe81 100644 --- a/tests/benchmarks/corelib/time/qtimezone/tst_bench_qtimezone.cpp +++ b/tests/benchmarks/corelib/time/qtimezone/tst_bench_qtimezone.cpp @@ -41,15 +41,17 @@ static QList<QByteArray> enoughZones() QByteArray("UTC"), // Those named overtly in tst_QDateTime - special cases first: QByteArray("UTC-02:00"), QByteArray("UTC+02:00"), QByteArray("UTC+12:00"), - QByteArray("Etc/GMT+3"), QByteArray("GMT-2"), QByteArray("GMT"), + QByteArray("Etc/GMT+3"), QByteArray("GMT-0"), QByteArray("GMT"), // ... then ordinary names in alphabetic order: + QByteArray("America/Anchorage"), QByteArray("America/Metlakatla"), QByteArray("America/New_York"), QByteArray("America/Sao_Paulo"), - QByteArray("America/Vancouver"), - QByteArray("Asia/Kathmandu"), QByteArray("Asia/Singapore"), + QByteArray("America/Toronto"), QByteArray("America/Vancouver"), + QByteArray("Asia/Kathmandu"), QByteArray("Asia/Manila"), QByteArray("Asia/Singapore"), QByteArray("Australia/Brisbane"), QByteArray("Australia/Eucla"), QByteArray("Australia/Sydney"), QByteArray("Europe/Berlin"), QByteArray("Europe/Helsinki"), - QByteArray("Europe/Rome"), QByteArray("Europe/Oslo"), + QByteArray("Europe/Lisbon"), QByteArray("Europe/Oslo"), + QByteArray("Europe/Rome"), QByteArray("Pacific/Apia"), QByteArray("Pacific/Auckland"), QByteArray("Pacific/Kiritimati") }; |
