diff options
176 files changed, 3164 insertions, 1792 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/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/coin/instructions/cmake_regular_test_instructions.yaml b/coin/instructions/cmake_regular_test_instructions.yaml index 91593df6b4f..60fbd2e8e20 100644 --- a/coin/instructions/cmake_regular_test_instructions.yaml +++ b/coin/instructions/cmake_regular_test_instructions.yaml @@ -33,7 +33,7 @@ instructions: conditions: - condition: property property: id - contains_value: "macos-15" + equals_value: "macos-26-arm64-tests" - condition: runtime env_var: COIN_CTEST_FORCE_IGNORE_EXIT_CODE equals_value: null 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/3rdparty/iaccessible2/REUSE.toml b/src/3rdparty/iaccessible2/REUSE.toml index f4d00d7f4f1..24da6e46400 100644 --- a/src/3rdparty/iaccessible2/REUSE.toml +++ b/src/3rdparty/iaccessible2/REUSE.toml @@ -5,5 +5,5 @@ path = ["**"] precedence = "closest" SPDX-FileCopyrightText = ["Copyright (c) 2000, 2006 Sun Microsystems, Inc.", "Copyright (c) 2006 IBM Corporation", - "Copyright (c) 2007, 2010, 2012, 2013 Linux Foundation\nIAccessible2 is a trademark of the Linux Foundation. The IAccessible2 mark may be used in accordance with the Linux Foundation Trademark Policy to indicate compliance with the IAccessible2 specification."] + "Copyright (c) 2007, 2010, 2012, 2013 Linux Foundation IAccessible2 is a trademark of the Linux Foundation. The IAccessible2 mark may be used in accordance with the Linux Foundation Trademark Policy to indicate compliance with the IAccessible2 specification."] SPDX-License-Identifier = "BSD-3-Clause" diff --git a/src/3rdparty/libpsl/REUSE.toml b/src/3rdparty/libpsl/REUSE.toml index 6c911a98dfe..7b63d4ed725 100644 --- a/src/3rdparty/libpsl/REUSE.toml +++ b/src/3rdparty/libpsl/REUSE.toml @@ -10,6 +10,6 @@ SPDX-License-Identifier = "BSD-3-Clause" [[annotations]] path = ["psl_data.cpp"] precedence = "closest" -SPDX-FileCopyrightText = ["The list was originally provided by Jo Hermans <[email protected]>.\nIt is now maintained on github (https://github.com/publicsuffix/list)."] +SPDX-FileCopyrightText = ["The list was originally provided by Jo Hermans <[email protected]>.It is now maintained on github (https://github.com/publicsuffix/list)."] SPDX-License-Identifier = "MPL-2.0" diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt index b073dbea0ea..e0a675ab463 100644 --- a/src/android/jar/CMakeLists.txt +++ b/src/android/jar/CMakeLists.txt @@ -53,6 +53,7 @@ set(java_sources src/org/qtproject/qt/android/BackgroundActionsTracker.java src/org/qtproject/qt/android/QtApkFileEngine.java src/org/qtproject/qt/android/QtContentFileEngine.java + src/org/qtproject/qt/android/QtWindowInsetsController.java ) qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java index aca9e3d3c11..78763e65905 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java @@ -102,9 +102,11 @@ public class QtActivityBase extends Activity requestWindowFeature(Window.FEATURE_ACTION_BAR); if (!m_isCustomThemeSet) { - setTheme(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? - android.R.style.Theme_DeviceDefault_DayNight : - android.R.style.Theme_Holo_Light); + @SuppressWarnings("deprecation") + int themeId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + ? android.R.style.Theme_DeviceDefault_DayNight + : android.R.style.Theme_Holo_Light; + setTheme(themeId); } if (QtNative.getStateDetails().isStarted) { @@ -168,7 +170,7 @@ public class QtActivityBase extends Activity m_delegate.displayManager().registerDisplayListener(); QtWindow.updateWindows(); // Suspending the app clears the immersive mode, so we need to set it again. - m_delegate.displayManager().reinstateFullScreen(); + QtWindowInsetsController.restoreFullScreenVisibility(this); } } @@ -311,9 +313,7 @@ public class QtActivityBase extends Activity return; QtNative.setStarted(savedInstanceState.getBoolean("Started")); - boolean isFullScreen = savedInstanceState.getBoolean("isFullScreen"); - boolean expandedToCutout = savedInstanceState.getBoolean("expandedToCutout"); - m_delegate.displayManager().setSystemUiVisibility(isFullScreen, expandedToCutout); + QtWindowInsetsController.restoreFullScreenVisibility(this); // FIXME restore all surfaces } @@ -329,8 +329,6 @@ public class QtActivityBase extends Activity protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putBoolean("isFullScreen", m_delegate.displayManager().isFullScreen()); - outState.putBoolean("expandedToCutout", m_delegate.displayManager().expandedToCutout()); outState.putBoolean("Started", QtNative.getStateDetails().isStarted); } @@ -339,7 +337,7 @@ public class QtActivityBase extends Activity { super.onWindowFocusChanged(hasFocus); if (hasFocus) - m_delegate.displayManager().reinstateFullScreen(); + QtWindowInsetsController.restoreFullScreenVisibility(this); } @Override diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java index 118845efcec..aa2964c3ae9 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java @@ -86,21 +86,6 @@ class QtActivityDelegate extends QtActivityDelegateBase } @Override - public void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) - { - if (m_layout == null) - return; - - QtNative.runAction(() -> { - if (m_layout != null) { - m_displayManager.setSystemUiVisibility(isFullScreen, expandedToCutout); - QtWindow.updateWindows(); - } - }); - } - - - @Override final public void onAppStateDetailsChanged(QtNative.ApplicationStateDetails details) { if (details.isStarted) registerBackends(); @@ -138,9 +123,9 @@ class QtActivityDelegate extends QtActivityDelegateBase int orientation = m_activity.getResources().getConfiguration().orientation; setUpSplashScreen(orientation); m_activity.registerForContextMenu(m_layout); - m_activity.setContentView(m_layout, - new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); + ViewGroup.LayoutParams rootParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + m_activity.setContentView(m_layout, rootParams); handleUiModeChange(); diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java index 35f519ca9a4..7f6163f164d 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java @@ -46,7 +46,9 @@ abstract class QtActivityDelegateBase m_activity = activity; QtNative.setActivity(m_activity); m_displayManager = new QtDisplayManager(m_activity); - m_inputDelegate = new QtInputDelegate(m_displayManager::reinstateFullScreen); + m_inputDelegate = new QtInputDelegate(() -> { + QtWindowInsetsController.restoreFullScreenVisibility(m_activity); + }); m_accessibilityDelegate = new QtAccessibilityDelegate(); } @@ -105,20 +107,20 @@ abstract class QtActivityDelegateBase Configuration config = resources.getConfiguration(); int uiMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK; - if (m_displayManager.decorFitsSystemWindows()) { + if (QtWindowInsetsController.decorFitsSystemWindows(m_activity)) { Window window = m_activity.getWindow(); - QtDisplayManager.enableSystemBarsBackgroundDrawing(window); - int status = QtDisplayManager.getThemeDefaultStatusBarColor(m_activity); - QtDisplayManager.setStatusBarColor(window, status); - int nav = QtDisplayManager.getThemeDefaultNavigationBarColor(m_activity); - QtDisplayManager.setNavigationBarColor(window, nav); + QtWindowInsetsController.enableSystemBarsBackgroundDrawing(window); + int status = QtWindowInsetsController.getThemeDefaultStatusBarColor(m_activity); + QtWindowInsetsController.setStatusBarColor(window, status); + int nav = QtWindowInsetsController.getThemeDefaultNavigationBarColor(m_activity); + QtWindowInsetsController.setNavigationBarColor(window, nav); } // Don't override color scheme if the app has it set explicitly. if (canOverrideColorSchemeHint()) { boolean isLight = uiMode == Configuration.UI_MODE_NIGHT_NO; - QtDisplayManager.setStatusBarColorHint(m_activity, isLight); - QtDisplayManager.setNavigationBarColorHint(m_activity, isLight); + QtWindowInsetsController.setStatusBarColorHint(m_activity, isLight); + QtWindowInsetsController.setNavigationBarColorHint(m_activity, isLight); } switch (uiMode) { diff --git a/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java index 8736dfab771..2cabb951813 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtDisplayManager.java @@ -7,7 +7,6 @@ import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.content.res.TypedArray; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Build; @@ -15,22 +14,13 @@ import android.util.DisplayMetrics; import android.util.Size; import android.view.Display; import android.view.Surface; -import android.view.View; -import android.view.WindowInsets; import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; import android.view.WindowMetrics; -import android.view.WindowInsetsController; -import android.view.Window; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import android.graphics.Color; -import android.util.TypedValue; -import android.content.res.Resources.Theme; - import android.util.Log; class QtDisplayManager @@ -47,9 +37,6 @@ class QtDisplayManager static native void handleScreenDensityChanged(double density); // screen methods - private boolean m_isFullScreen = false; - private boolean m_expandedToCutout = false; - private static int m_previousRotation = -1; private final DisplayManager.DisplayListener m_displayListener; @@ -136,264 +123,6 @@ class QtDisplayManager } @SuppressWarnings("deprecation") - void setSystemUiVisibilityPreAndroidR(View decorView) - { - int systemUiVisibility; - - if (m_isFullScreen || m_expandedToCutout) { - systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - if (m_isFullScreen) { - systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - } - } else { - systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; - } - - decorView.setSystemUiVisibility(systemUiVisibility); - } - - void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) - { - if (m_isFullScreen == isFullScreen && m_expandedToCutout == expandedToCutout) - return; - - m_isFullScreen = isFullScreen; - m_expandedToCutout = expandedToCutout; - - Window window = m_activity.getWindow(); - View decorView = window.getDecorView(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - int cutoutMode; - if (m_isFullScreen || m_expandedToCutout) { - window.setDecorFitsSystemWindows(false); - cutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - } else { - window.setDecorFitsSystemWindows(true); - cutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; - } - LayoutParams layoutParams = window.getAttributes(); - layoutParams.layoutInDisplayCutoutMode = cutoutMode; - window.setAttributes(layoutParams); - - final WindowInsetsController insetsControl = window.getInsetsController(); - if (insetsControl != null) { - int sysBarsBehavior; - if (m_isFullScreen) { - insetsControl.hide(WindowInsets.Type.systemBars()); - sysBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - } else { - insetsControl.show(WindowInsets.Type.systemBars()); - sysBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT; - } - insetsControl.setSystemBarsBehavior(sysBarsBehavior); - } - } else { - setSystemUiVisibilityPreAndroidR(decorView); - } - - if (!isFullScreen && !edgeToEdgeEnabled(m_activity)) { - // These are needed to operate on system bar colors - window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - - // Handle transparent status and navigation bars - if (m_expandedToCutout) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - window.setStatusBarColor(Color.TRANSPARENT); - window.setNavigationBarColor(Color.TRANSPARENT); - } else { - // Android 9 and prior doesn't add the semi-transparent bars - // to avoid low contrast system icons, so try to mimick it - // by taking the current color and only increase the opacity. - int statusBarColor = window.getStatusBarColor(); - int transparentStatusBar = statusBarColor & 0x00FFFFFF; - window.setStatusBarColor(transparentStatusBar); - - int navigationBarColor = window.getNavigationBarColor(); - int semiTransparentNavigationBar = navigationBarColor & 0x7FFFFFFF; - window.setNavigationBarColor(semiTransparentNavigationBar); - } - } else { - // Restore theme's system bars colors - Theme theme = m_activity.getTheme(); - TypedValue typedValue = new TypedValue(); - - theme.resolveAttribute(android.R.attr.statusBarColor, typedValue, true); - int defaultStatusBarColor = typedValue.data; - window.setStatusBarColor(defaultStatusBarColor); - - theme.resolveAttribute(android.R.attr.navigationBarColor, typedValue, true); - int defaultNavigationBarColor = typedValue.data; - window.setNavigationBarColor(defaultNavigationBarColor); - } - } - - decorView.post(() -> decorView.requestApplyInsets()); - } - - private static boolean edgeToEdgeEnabled(Activity activity) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) - return true; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) - return false; - int[] attrs = new int[] { android.R.attr.windowOptOutEdgeToEdgeEnforcement }; - TypedArray ta = activity.getTheme().obtainStyledAttributes(attrs); - try { - return !ta.getBoolean(0, false); - } finally { - ta.recycle(); - } - } - - boolean isFullScreen() - { - return m_isFullScreen; - } - - boolean expandedToCutout() - { - return m_expandedToCutout; - } - - boolean decorFitsSystemWindows() - { - return !isFullScreen() && !expandedToCutout(); - } - - void reinstateFullScreen() - { - if (m_isFullScreen) { - m_isFullScreen = false; - setSystemUiVisibility(true, m_expandedToCutout); - } - } - - /* - * Convenience method to call deprecated API prior to Android R (30). - */ - @SuppressWarnings ("deprecation") - private static void setSystemUiVisibility(View decorView, int flags) - { - decorView.setSystemUiVisibility(flags); - } - - /* - * Set the status bar color scheme hint so that the system decides how to color the icons. - */ - @UsedFromNativeCode - static void setStatusBarColorHint(Activity activity, boolean isLight) - { - Window window = activity.getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - WindowInsetsController controller = window.getInsetsController(); - if (controller != null) { - int lightStatusBarMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - int appearance = isLight ? lightStatusBarMask : 0; - controller.setSystemBarsAppearance(appearance, lightStatusBarMask); - } - } else { - @SuppressWarnings("deprecation") - int currentFlags = window.getDecorView().getSystemUiVisibility(); - @SuppressWarnings("deprecation") - int lightStatusBarMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - int appearance = isLight - ? currentFlags | lightStatusBarMask - : currentFlags & ~lightStatusBarMask; - setSystemUiVisibility(window.getDecorView(), appearance); - } - } - - /* - * Set the navigation bar color scheme hint so that the system decides how to color the icons. - */ - @UsedFromNativeCode - static void setNavigationBarColorHint(Activity activity, boolean isLight) - { - Window window = activity.getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - WindowInsetsController controller = window.getInsetsController(); - if (controller != null) { - int lightNavigationBarMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; - int appearance = isLight ? lightNavigationBarMask : 0; - controller.setSystemBarsAppearance(appearance, lightNavigationBarMask); - } - } else { - @SuppressWarnings("deprecation") - int currentFlags = window.getDecorView().getSystemUiVisibility(); - @SuppressWarnings("deprecation") - int lightNavigationBarMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - int appearance = isLight - ? currentFlags | lightNavigationBarMask - : currentFlags & ~lightNavigationBarMask; - setSystemUiVisibility(window.getDecorView(), appearance); - } - } - - static int resolveColorAttribute(Activity activity, int attribute) - { - Theme theme = activity.getTheme(); - Resources resources = activity.getResources(); - TypedValue tv = new TypedValue(); - - if (theme.resolveAttribute(attribute, tv, true)) { - if (tv.resourceId != 0) - return resources.getColor(tv.resourceId, theme); - if (tv.type >= TypedValue.TYPE_FIRST_COLOR_INT && tv.type <= TypedValue.TYPE_LAST_COLOR_INT) - return tv.data; - } - - return -1; - } - - @SuppressWarnings("deprecation") - static int getThemeDefaultStatusBarColor(Activity activity) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return -1; - return resolveColorAttribute(activity, android.R.attr.statusBarColor); - } - - @SuppressWarnings("deprecation") - static int getThemeDefaultNavigationBarColor(Activity activity) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return -1; - return resolveColorAttribute(activity, android.R.attr.navigationBarColor); - } - - static void enableSystemBarsBackgroundDrawing(Window window) - { - // These are needed to operate on system bar colors - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - @SuppressWarnings("deprecation") - final int translucentFlags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; - window.clearFlags(translucentFlags); - } - - @SuppressWarnings("deprecation") - static void setStatusBarColor(Window window, int color) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return; - window.setStatusBarColor(color); - } - - @SuppressWarnings("deprecation") - static void setNavigationBarColor(Window window, int color) - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) - return; - window.setNavigationBarColor(color); - } - - @SuppressWarnings("deprecation") static Display getDisplay(Context context) { Activity activity = (Activity) context; diff --git a/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java index ca271c5dd62..e0c6c00a8fd 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java @@ -9,6 +9,7 @@ import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.ResultReceiver; import android.text.method.MetaKeyKeyListener; import android.util.DisplayMetrics; @@ -166,7 +167,7 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt } else { if (m_imm == null) return; - m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler()) { + m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler(Looper.getMainLooper())) { @Override @SuppressWarnings("fallthrough") protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -278,7 +279,7 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt activity.getWindow().getInsetsController().hide(Type.ime()); } else { m_imm.hideSoftInputFromWindow(m_currentEditText.getWindowToken(), 0, - new ResultReceiver(new Handler()) { + new ResultReceiver(new Handler(Looper.getMainLooper())) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { switch (resultCode) { diff --git a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java index 8fd0213eb91..6d7c0624272 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java @@ -345,7 +345,7 @@ abstract class QtLoader { if (metadata == null || !metadata.containsKey(key)) return ""; - return String.valueOf(metadata.get(key)); + return String.valueOf(metadata.getString(key)); } @SuppressLint("DiscouragedApi") diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java index 080bb787a2d..7e4632f6792 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java @@ -361,8 +361,11 @@ public class QtNative if (m_stateDetails.isStarted) return; + getQtThread().run(() -> initAndroidQpaPlugin()); final String qtParams = mainLib + " " + params; getQtThread().post(() -> { startQtNativeApplication(qtParams); }); + waitForServiceSetup(); + setStarted(true); } @UsedFromNativeCode @@ -428,7 +431,9 @@ public class QtNative } // application methods + static native boolean initAndroidQpaPlugin(); static native void startQtNativeApplication(String params); + static native void waitForServiceSetup(); static native void terminateQtNativeApplication(); static native boolean updateNativeActivity(); // application methods diff --git a/src/android/jar/src/org/qtproject/qt/android/QtView.java b/src/android/jar/src/org/qtproject/qt/android/QtView.java index b60c58013f5..e245f731ce8 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtView.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtView.java @@ -25,11 +25,11 @@ abstract class QtView extends ViewGroup implements QtNative.AppStateDetailsListe void onQtWindowLoaded(); } - protected QtWindow m_window; - protected long m_windowReference; - protected long m_parentWindowReference; - protected QtWindowListener m_windowListener; - protected final QtEmbeddedViewInterface m_viewInterface; + private QtWindow m_window; + private long m_windowReference; + private long m_parentWindowReference; + private QtWindowListener m_windowListener; + private final QtEmbeddedViewInterface m_viewInterface; // Implement in subclass to handle the creation of the QWindow and its parent container. // TODO could we take care of the parent window creation and parenting outside of the // window creation method to simplify things if user would extend this? Preferably without @@ -164,10 +164,43 @@ abstract class QtView extends ViewGroup implements QtNative.AppStateDetailsListe m_windowReference = windowReference; } - long windowReference() { + long getWindowReference() { return m_windowReference; } + void setQtWindow(QtWindow qtWindow) { + m_window = qtWindow; + } + + QtWindow getQtWindow() { + return m_window; + } + + long getParentWindowReference() + { + return m_parentWindowReference; + } + + void setParentWindowReference(long reference) + { + m_parentWindowReference = reference; + } + + QtWindowListener getWindowListener() + { + return m_windowListener; + } + + void setWindowListener(QtWindowListener listner) + { + m_windowListener = listner; + } + + QtEmbeddedViewInterface getViewInterface() + { + return m_viewInterface; + } + // Set the visibility of the underlying QWindow. If visible is true, showNormal() is called. // If false, the window is hidden. void setWindowVisible(boolean visible) { @@ -203,10 +236,6 @@ abstract class QtView extends ViewGroup implements QtNative.AppStateDetailsListe setWindowReference(0L); } - QtWindow getQtWindow() { - return m_window; - } - @Override public void onAppStateDetailsChanged(QtNative.ApplicationStateDetails details) { if (!details.isStarted) { diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindowInsetsController.java b/src/android/jar/src/org/qtproject/qt/android/QtWindowInsetsController.java new file mode 100644 index 00000000000..a3d0a0df8e5 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindowInsetsController.java @@ -0,0 +1,358 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +package org.qtproject.qt.android; + +import android.app.Activity; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.os.Build; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowInsetsController; +import android.view.Window; + +import android.graphics.Color; +import android.util.TypedValue; +import android.content.res.Resources.Theme; + +class QtWindowInsetsController +{ + /* + * Convenience method to call deprecated API prior to Android R (30). + */ + @SuppressWarnings ("deprecation") + private static void setDecorFitsSystemWindows(Window window, boolean enable) + { + final int sdk = Build.VERSION.SDK_INT; + if (sdk < Build.VERSION_CODES.R || sdk > Build.VERSION_CODES.VANILLA_ICE_CREAM) + return; + window.setDecorFitsSystemWindows(enable); + } + + private static void useCutoutShortEdges(Window window, boolean enabled) + { + if (window == null) + return; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + WindowManager.LayoutParams layoutParams = window.getAttributes(); + layoutParams.layoutInDisplayCutoutMode = enabled + ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + : WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; + window.setAttributes(layoutParams); + } + } + + @UsedFromNativeCode + static void showNormal(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + setDecorFitsSystemWindows(window, true); + WindowInsetsController ctrl = window.getInsetsController(); + if (ctrl != null) { + ctrl.show(WindowInsets.Type.systemBars()); + ctrl.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT); + } + } else { + @SuppressWarnings("deprecation") + int flags = View.SYSTEM_UI_FLAG_VISIBLE; // clear all flags + setSystemUiVisibility(decor, flags); + } + + setTransparentSystemBars(activity, false); + useCutoutShortEdges(window, false); + + decor.post(() -> decor.requestApplyInsets()); + } + + /* + * Make system bars transparent for Andorid versions prior to Android 15. + */ + @SuppressWarnings("deprecation") + private static void setTransparentSystemBars(Activity activity, boolean transparent) + { + Window window = activity.getWindow(); + if (window == null) + return; + + if (edgeToEdgeEnabled(activity)) + return; + + // These are needed to operate on system bar colors + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + + if (transparent) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + window.setStatusBarColor(Color.TRANSPARENT); + window.setNavigationBarColor(Color.TRANSPARENT); + } else { + // Android 9 and prior doesn't add the semi-transparent bars + // to avoid low contrast system icons, so try to mimick it + // by taking the current color and only increase the opacity. + int statusBarColor = window.getStatusBarColor(); + int transparentStatusBar = statusBarColor & 0x00FFFFFF; + window.setStatusBarColor(transparentStatusBar); + + int navigationBarColor = window.getNavigationBarColor(); + int semiTransparentNavigationBar = navigationBarColor & 0x7FFFFFFF; + window.setNavigationBarColor(semiTransparentNavigationBar); + } + } else { + // Restore theme's system bars colors + int defaultStatusBarColor = getThemeDefaultStatusBarColor(activity); + window.setStatusBarColor(defaultStatusBarColor); + + int defaultNavigationBarColor = getThemeDefaultNavigationBarColor(activity); + window.setNavigationBarColor(defaultNavigationBarColor); + } + } + + @UsedFromNativeCode + static void showExpanded(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + setDecorFitsSystemWindows(window, false); + WindowInsetsController ctrl = window.getInsetsController(); + if (ctrl != null) { + ctrl.show(WindowInsets.Type.systemBars()); + ctrl.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT); + } + } else { + @SuppressWarnings("deprecation") + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + setSystemUiVisibility(decor, flags); + } + + setTransparentSystemBars(activity, true); + useCutoutShortEdges(window, true); + + decor.post(() -> decor.requestApplyInsets()); + } + + @UsedFromNativeCode + public static void showFullScreen(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + setDecorFitsSystemWindows(window, false); + WindowInsetsController ctrl = window.getInsetsController(); + if (ctrl != null) { + ctrl.hide(WindowInsets.Type.systemBars()); + ctrl.setSystemBarsBehavior( + WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + } else { + @SuppressWarnings("deprecation") + int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + setSystemUiVisibility(decor, flags); + } + + useCutoutShortEdges(window, true); + + decor.post(() -> decor.requestApplyInsets()); + } + + private static boolean edgeToEdgeEnabled(Activity activity) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) + return true; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) + return false; + int[] attrs = new int[] { android.R.attr.windowOptOutEdgeToEdgeEnforcement }; + TypedArray ta = activity.getTheme().obtainStyledAttributes(attrs); + try { + return !ta.getBoolean(0, false); + } finally { + ta.recycle(); + } + } + + static boolean isFullScreen(Activity activity) + { + Window window = activity.getWindow(); + if (window == null) + return false; + + final View decor = window.getDecorView(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets(); + if (insets != null) + return !insets.isVisible(WindowInsets.Type.statusBars()); + } else { + @SuppressWarnings("deprecation") + int flags = decor.getSystemUiVisibility(); + @SuppressWarnings("deprecation") + int immersiveMask = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + return (flags & immersiveMask) == immersiveMask; + } + + return false; + } + + static boolean isExpandedClientArea(Activity activity) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return edgeToEdgeEnabled(activity); + + @SuppressWarnings("deprecation") + int statusBarColor = activity.getWindow().getStatusBarColor(); + // If the status bar is not fully opaque assume we have expanded client + // area and we're drawing under it. + int statusBarAlpha = statusBarColor >>> 24; + return statusBarAlpha != 0xFF; + } + + static boolean decorFitsSystemWindows(Activity activity) + { + return !isFullScreen(activity) && !isExpandedClientArea(activity); + } + + static void restoreFullScreenVisibility(Activity activity) + { + if (isFullScreen(activity)) + showFullScreen(activity); + } + + /* + * Convenience method to call deprecated API prior to Android R (30). + */ + @SuppressWarnings ("deprecation") + private static void setSystemUiVisibility(View decorView, int flags) + { + decorView.setSystemUiVisibility(flags); + } + + /* + * Set the status bar color scheme hint so that the system decides how to color the icons. + */ + @UsedFromNativeCode + static void setStatusBarColorHint(Activity activity, boolean isLight) + { + Window window = activity.getWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowInsetsController controller = window.getInsetsController(); + if (controller != null) { + int lightStatusBarMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + int appearance = isLight ? lightStatusBarMask : 0; + controller.setSystemBarsAppearance(appearance, lightStatusBarMask); + } + } else { + @SuppressWarnings("deprecation") + int currentFlags = window.getDecorView().getSystemUiVisibility(); + @SuppressWarnings("deprecation") + int lightStatusBarMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + int appearance = isLight + ? currentFlags | lightStatusBarMask + : currentFlags & ~lightStatusBarMask; + setSystemUiVisibility(window.getDecorView(), appearance); + } + } + + /* + * Set the navigation bar color scheme hint so that the system decides how to color the icons. + */ + @UsedFromNativeCode + static void setNavigationBarColorHint(Activity activity, boolean isLight) + { + Window window = activity.getWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowInsetsController controller = window.getInsetsController(); + if (controller != null) { + int lightNavigationBarMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; + int appearance = isLight ? lightNavigationBarMask : 0; + controller.setSystemBarsAppearance(appearance, lightNavigationBarMask); + } + } else { + @SuppressWarnings("deprecation") + int currentFlags = window.getDecorView().getSystemUiVisibility(); + @SuppressWarnings("deprecation") + int lightNavigationBarMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + int appearance = isLight + ? currentFlags | lightNavigationBarMask + : currentFlags & ~lightNavigationBarMask; + setSystemUiVisibility(window.getDecorView(), appearance); + } + } + + private static int resolveColorAttribute(Activity activity, int attribute) + { + Theme theme = activity.getTheme(); + Resources resources = activity.getResources(); + TypedValue tv = new TypedValue(); + + if (theme.resolveAttribute(attribute, tv, true)) { + if (tv.resourceId != 0) + return resources.getColor(tv.resourceId, theme); + if (tv.type >= TypedValue.TYPE_FIRST_COLOR_INT && tv.type <= TypedValue.TYPE_LAST_COLOR_INT) + return tv.data; + } + + return -1; + } + + @SuppressWarnings("deprecation") + static int getThemeDefaultStatusBarColor(Activity activity) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return -1; + return resolveColorAttribute(activity, android.R.attr.statusBarColor); + } + + @SuppressWarnings("deprecation") + static int getThemeDefaultNavigationBarColor(Activity activity) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return -1; + return resolveColorAttribute(activity, android.R.attr.navigationBarColor); + } + + static void enableSystemBarsBackgroundDrawing(Window window) + { + // These are needed to operate on system bar colors + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + @SuppressWarnings("deprecation") + final int translucentFlags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + window.clearFlags(translucentFlags); + } + + @SuppressWarnings("deprecation") + static void setStatusBarColor(Window window, int color) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return; + window.setStatusBarColor(color); + } + + @SuppressWarnings("deprecation") + static void setNavigationBarColor(Window window, int color) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) + return; + window.setNavigationBarColor(color); + } +} diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java b/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java index d42b3e6821e..1cd36f06f5c 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindowInterface.java @@ -7,5 +7,4 @@ interface QtWindowInterface { default void removeTopLevelWindow(final int id) { } default void bringChildToFront(final int id) { } default void bringChildToBack(int id) { } - default void setSystemUiVisibility(boolean isFullScreen, boolean expandedToCutout) { } } diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index d8743a1c976..337703bda3a 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -175,10 +175,12 @@ qt_internal_add_module(Core kernel/qfunctions_p.h kernel/qiterable.cpp kernel/qiterable.h kernel/qiterable_impl.h kernel/qmath.cpp kernel/qmath.h + kernel/qmetaassociation.cpp kernel/qmetacontainer.cpp kernel/qmetacontainer.h kernel/qmetaobject.cpp kernel/qmetaobject.h kernel/qmetaobject_p.h kernel/qmetaobject_moc_p.h kernel/qmetaobjectbuilder.cpp kernel/qmetaobjectbuilder_p.h + kernel/qmetasequence.cpp kernel/qmetatype.cpp kernel/qmetatype.h kernel/qmetatype_p.h kernel/qmimedata.cpp kernel/qmimedata.h kernel/qtmetamacros.h kernel/qtmocconstants.h kernel/qtmochelpers.h 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/qmetaassociation.cpp b/src/corelib/kernel/qmetaassociation.cpp new file mode 100644 index 00000000000..dc239424e6d --- /dev/null +++ b/src/corelib/kernel/qmetaassociation.cpp @@ -0,0 +1,299 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtCore/qmetacontainer.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QMetaAssociation + \inmodule QtCore + \since 6.0 + \brief The QMetaAssociation class allows type erased access to associative containers. + + \ingroup objectmodel + + \compares equality + + The class provides a number of primitive container operations, using void* + as operands. This way, you can manipulate a generic container retrieved from + a Variant without knowing its type. + + QMetaAssociation covers both, containers with mapped values such as QMap or + QHash, and containers that only hold keys such as QSet. + + The void* arguments to the various methods are typically created by using + a \l QVariant of the respective container or value type, and calling + its \l QVariant::data() or \l QVariant::constData() methods. However, you + can also pass plain pointers to objects of the container or value type. + + Iterator invalidation follows the rules given by the underlying containers + and is not expressed in the API. Therefore, for a truly generic container, + any iterators should be considered invalid after any write operation. + + \sa QMetaContainer, QMetaSequence, QIterable, QIterator +*/ + +/*! + \fn template<typename C> QMetaAssociation QMetaAssociation::fromContainer() + \since 6.0 + + Returns the QMetaAssociation corresponding to the type given as template parameter. +*/ + +/*! + Returns the meta type for keys in the container. + */ +QMetaType QMetaAssociation::keyMetaType() const +{ + if (auto iface = d()) + return QMetaType(iface->keyMetaType); + return QMetaType(); +} + +/*! + Returns the meta type for mapped values in the container. + */ +QMetaType QMetaAssociation::mappedMetaType() const +{ + if (auto iface = d()) + return QMetaType(iface->mappedMetaType); + return QMetaType(); +} + +/*! + \fn bool QMetaAssociation::canInsertKey() const + + Returns \c true if keys can be added to the container using \l insertKey(), + otherwise returns \c false. + + \sa insertKey() + */ + +/*! + \fn void QMetaAssociation::insertKey(void *container, const void *key) const + + Inserts the \a key into the \a container if possible. If the container has + mapped values a default-create mapped value is associated with the \a key. + + \sa canInsertKey() + */ + +/*! + \fn bool QMetaAssociation::canRemoveKey() const + + Returns \c true if keys can be removed from the container using + \l removeKey(), otherwise returns \c false. + + \sa removeKey() + */ + +/*! + \fn void QMetaAssociation::removeKey(void *container, const void *key) const + + Removes the \a key and its associated mapped value from the \a container if + possible. + + \sa canRemoveKey() + */ + +/*! + \fn bool QMetaAssociation::canContainsKey() const + + Returns \c true if the container can be queried for keys using + \l containsKey(), otherwise returns \c false. + */ + +/*! + \fn bool QMetaAssociation::containsKey(const void *container, const void *key) const + + Returns \c true if the \a container can be queried for keys and contains the + \a key, otherwise returns \c false. + + \sa canContainsKey() + */ + +/*! + \fn bool QMetaAssociation::canGetMappedAtKey() const + + Returns \c true if the container can be queried for values using + \l mappedAtKey(), otherwise returns \c false. + */ + +/*! + \fn void QMetaAssociation::mappedAtKey(const void *container, const void *key, void *mapped) const + + Retrieves the mapped value associated with the \a key in the \a container + and places it in the memory location pointed to by \a mapped, if that is + possible. + + \sa canGetMappedAtKey() + */ + +/*! + \fn bool QMetaAssociation::canSetMappedAtKey() const + + Returns \c true if mapped values can be modified in the container using + \l setMappedAtKey(), otherwise returns \c false. + + \sa setMappedAtKey() + */ + +/*! + \fn void QMetaAssociation::setMappedAtKey(void *container, const void *key, const void *mapped) const + + Overwrites the value associated with the \a key in the \a container using + the \a mapped value passed as argument if that is possible. + + \sa canSetMappedAtKey() + */ + +/*! + \fn bool QMetaAssociation::canGetKeyAtIterator() const + + Returns \c true if a key can be retrieved from a non-const iterator using + \l keyAtIterator(), otherwise returns \c false. + + \sa keyAtIterator() + */ + +/*! + \fn void QMetaAssociation::keyAtIterator(const void *iterator, void *key) const + + Retrieves the key pointed to by the non-const \a iterator and stores it + in the memory location pointed to by \a key, if possible. + + \sa canGetKeyAtIterator(), begin(), end(), createIteratorAtKey() + */ + +/*! + \fn bool QMetaAssociation::canGetKeyAtConstIterator() const + + Returns \c true if a key can be retrieved from a const iterator using + \l keyAtConstIterator(), otherwise returns \c false. + + \sa keyAtConstIterator() + */ + +/*! + \fn void QMetaAssociation::keyAtConstIterator(const void *iterator, void *key) const + + Retrieves the key pointed to by the const \a iterator and stores it + in the memory location pointed to by \a key, if possible. + + \sa canGetKeyAtConstIterator(), constBegin(), constEnd(), createConstIteratorAtKey() + */ + +/*! + \fn bool QMetaAssociation::canGetMappedAtIterator() const + + Returns \c true if a mapped value can be retrieved from a non-const + iterator using \l mappedAtIterator(), otherwise returns \c false. + + \sa mappedAtIterator() + */ + +/*! + \fn void QMetaAssociation::mappedAtIterator(const void *iterator, void *mapped) const + + Retrieves the mapped value pointed to by the non-const \a iterator and + stores it in the memory location pointed to by \a mapped, if possible. + + \sa canGetMappedAtIterator(), begin(), end(), createIteratorAtKey() + */ + +/*! + \fn bool QMetaAssociation::canGetMappedAtConstIterator() const + + Returns \c true if a mapped value can be retrieved from a const iterator + using \l mappedAtConstIterator(), otherwise returns \c false. + + \sa mappedAtConstIterator() + */ + +/*! + \fn void QMetaAssociation::mappedAtConstIterator(const void *iterator, void *mapped) const + + Retrieves the mapped value pointed to by the const \a iterator and + stores it in the memory location pointed to by \a mapped, if possible. + + \sa canGetMappedAtConstIterator(), constBegin(), constEnd(), createConstIteratorAtKey() + */ + +/*! + \fn bool QMetaAssociation::canSetMappedAtIterator() const + + Returns \c true if a mapped value can be set via a non-const iterator using + \l setMappedAtIterator(), otherwise returns \c false. + + \sa setMappedAtIterator() + */ + +/*! + \fn void QMetaAssociation::setMappedAtIterator(const void *iterator, const void *mapped) const + + Writes the \a mapped value to the container location pointed to by the + non-const \a iterator, if possible. + + \sa canSetMappedAtIterator(), begin(), end(), createIteratorAtKey() + */ + +/*! + \fn bool QMetaAssociation::canCreateIteratorAtKey() const + + Returns \c true if an iterator pointing to an entry in the container can be + created using \l createIteratorAtKey(), otherwise returns false. + + \sa createIteratorAtKey() + */ + +/*! + \fn void *QMetaAssociation::createIteratorAtKey(void *container, const void *key) const + + Returns a non-const iterator pointing to the entry of \a key in the + \a container, if possible. If the entry doesn't exist, creates a non-const + iterator pointing to the end of the \a container. If no non-const iterator + can be created, returns \c nullptr. + + The non-const iterator has to be destroyed using destroyIterator(). + + \sa canCreateIteratorAtKey(), begin(), end(), destroyIterator() + */ + +/*! + \fn bool QMetaAssociation::canCreateConstIteratorAtKey() const + + Returns \c true if a const iterator pointing to an entry in the container + can be created using \l createConstIteratorAtKey(), otherwise returns false. + */ + +/*! + \fn void *QMetaAssociation::createConstIteratorAtKey(const void *container, const void *key) const + + Returns a const iterator pointing to the entry of \a key in the + \a container, if possible. If the entry doesn't exist, creates a const + iterator pointing to the end of the \a container. If no const iterator can + be created, returns \c nullptr. + + The const iterator has to be destroyed using destroyConstIterator(). + + \sa canCreateConstIteratorAtKey(), constBegin(), constEnd(), destroyConstIterator() + */ + +/*! + \fn bool QMetaAssociation::operator==(const QMetaAssociation &lhs, const QMetaAssociation &rhs) + + Returns \c true if the QMetaAssociation \a lhs represents the same container type + as the QMetaAssociation \a rhs, otherwise returns \c false. +*/ + +/*! + \fn bool QMetaAssociation::operator!=(const QMetaAssociation &lhs, const QMetaAssociation &rhs) + + Returns \c true if the QMetaAssociation \a lhs represents a different container + type than the QMetaAssociation \a rhs, otherwise returns \c false. +*/ + + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmetacontainer.cpp b/src/corelib/kernel/qmetacontainer.cpp index 3a1a33a478a..4b4ea06d8b9 100644 --- a/src/corelib/kernel/qmetacontainer.cpp +++ b/src/corelib/kernel/qmetacontainer.cpp @@ -7,37 +7,6 @@ QT_BEGIN_NAMESPACE /*! - \class QMetaSequence - \inmodule QtCore - \since 6.0 - \brief The QMetaSequence class allows type erased access to sequential containers. - - \ingroup objectmodel - - \compares equality - - The class provides a number of primitive container operations, using void* - as operands. This way, you can manipulate a generic container retrieved from - a Variant without knowing its type. - - The void* arguments to the various methods are typically created by using - a \l QVariant of the respective container or value type, and calling - its \l QVariant::data() or \l QVariant::constData() methods. However, you - can also pass plain pointers to objects of the container or value type. - - Iterator invalidation follows the rules given by the underlying containers - and is not expressed in the API. Therefore, for a truly generic container, - any iterators should be considered invalid after any write operation. -*/ - -/*! - \fn template<typename C> QMetaSequence QMetaSequence::fromContainer() - \since 6.0 - - Returns the QMetaSequence corresponding to the type given as template parameter. -*/ - -/*! \class QMetaContainer \inmodule QtCore \since 6.0 @@ -66,7 +35,7 @@ QT_BEGIN_NAMESPACE specializations of input iterators. This method will also return \c true if the container provides one of those. - QMetaSequence assumes that const and non-const iterators for the same + QMetaContainer assumes that const and non-const iterators for the same container have the same iterator traits. */ bool QMetaContainer::hasInputIterator() const @@ -83,7 +52,7 @@ bool QMetaContainer::hasInputIterator() const specializations of forward iterators. This method will also return \c true if the container provides one of those. - QMetaSequence assumes that const and non-const iterators for the same + QMetaContainer assumes that const and non-const iterators for the same container have the same iterator traits. */ bool QMetaContainer::hasForwardIterator() const @@ -99,7 +68,7 @@ bool QMetaContainer::hasForwardIterator() const std::bidirectional_iterator_tag and std::random_access_iterator_tag, respectively. Otherwise returns \c false. - QMetaSequence assumes that const and non-const iterators for the same + QMetaContainer assumes that const and non-const iterators for the same container have the same iterator traits. */ bool QMetaContainer::hasBidirectionalIterator() const @@ -114,7 +83,7 @@ bool QMetaContainer::hasBidirectionalIterator() const iterator as defined by std::random_access_iterator_tag, otherwise returns \c false. - QMetaSequence assumes that const and non-const iterators for the same + QMetaContainer assumes that const and non-const iterators for the same container have the same iterator traits. */ bool QMetaContainer::hasRandomAccessIterator() const @@ -125,147 +94,6 @@ bool QMetaContainer::hasRandomAccessIterator() const } /*! - Returns the meta type for values stored in the container. - */ -QMetaType QMetaSequence::valueMetaType() const -{ - if (auto iface = d()) - return QMetaType(iface->valueMetaType); - return QMetaType(); -} - -/*! - Returns \c true if the underlying container is sortable, otherwise returns - \c false. A container is considered sortable if values added to it are - placed in a defined location. Inserting into or adding to a sortable - container will always succeed. Inserting into or adding to an unsortable - container may not succeed, for example if the container is a QSet that - already contains the value being inserted. - - \sa addValue(), insertValueAtIterator(), canAddValueAtBegin(), - canAddValueAtEnd(), canRemoveValueAtBegin(), canRemoveValueAtEnd() - */ -bool QMetaSequence::isSortable() const -{ - if (auto iface = d()) { - return (iface->addRemoveCapabilities - & (QtMetaContainerPrivate::CanAddAtBegin | QtMetaContainerPrivate::CanAddAtEnd)) - && (iface->addRemoveCapabilities - & (QtMetaContainerPrivate::CanRemoveAtBegin - | QtMetaContainerPrivate::CanRemoveAtEnd)); - } - return false; -} - -/*! - Returns \c true if values added using \l addValue() can be placed at the - beginning of the container, otherwise returns \c false. - - \sa addValueAtBegin(), canAddValueAtEnd() - */ -bool QMetaSequence::canAddValueAtBegin() const -{ - if (auto iface = d()) { - return iface->addValueFn - && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanAddAtBegin; - } - return false; -} - -/*! - Adds \a value to the beginning of \a container if possible. If - \l canAddValueAtBegin() returns \c false, the \a value is not added. - - \sa canAddValueAtBegin(), isSortable(), removeValueAtBegin() - */ -void QMetaSequence::addValueAtBegin(void *container, const void *value) const -{ - if (canAddValueAtBegin()) - d()->addValueFn(container, value, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin); -} - -/*! - Returns \c true if values can be removed from the beginning of the container - using \l removeValue(), otherwise returns \c false. - - \sa removeValueAtBegin(), canRemoveValueAtEnd() - */ -bool QMetaSequence::canRemoveValueAtBegin() const -{ - if (auto iface = d()) { - return iface->removeValueFn - && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanRemoveAtBegin; - } - return false; -} - -/*! - Removes a value from the beginning of \a container if possible. If - \l canRemoveValueAtBegin() returns \c false, the value is not removed. - - \sa canRemoveValueAtBegin(), isSortable(), addValueAtBegin() - */ -void QMetaSequence::removeValueAtBegin(void *container) const -{ - if (canRemoveValueAtBegin()) - d()->removeValueFn(container, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin); -} - -/*! - Returns \c true if values added using \l addValue() can be placed at the - end of the container, otherwise returns \c false. - - \sa addValueAtEnd(), canAddValueAtBegin() - */ -bool QMetaSequence::canAddValueAtEnd() const -{ - if (auto iface = d()) { - return iface->addValueFn - && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanAddAtEnd; - } - return false; -} - -/*! - Adds \a value to the end of \a container if possible. If - \l canAddValueAtEnd() returns \c false, the \a value is not added. - - \sa canAddValueAtEnd(), isSortable(), removeValueAtEnd() - */ -void QMetaSequence::addValueAtEnd(void *container, const void *value) const -{ - if (canAddValueAtEnd()) - d()->addValueFn(container, value, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd); -} - -/*! - Returns \c true if values can be removed from the end of the container - using \l removeValue(), otherwise returns \c false. - - \sa removeValueAtEnd(), canRemoveValueAtBegin() - */ -bool QMetaSequence::canRemoveValueAtEnd() const -{ - if (auto iface = d()) { - return iface->removeValueFn - && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanRemoveAtEnd; - } - return false; -} - -/*! - Removes a value from the end of \a container if possible. If - \l canRemoveValueAtEnd() returns \c false, the value is not removed. - - \sa canRemoveValueAtEnd(), isSortable(), addValueAtEnd() - */ -void QMetaSequence::removeValueAtEnd(void *container) const -{ - if (canRemoveValueAtEnd()) - d()->removeValueFn(container, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd); -} - -/*! Returns \c true if the container can be queried for its size, \c false otherwise. @@ -309,122 +137,6 @@ void QMetaContainer::clear(void *container) const } /*! - Returns \c true if values can be retrieved from the container by index, - otherwise \c false. - - \sa valueAtIndex() - */ -bool QMetaSequence::canGetValueAtIndex() const -{ - if (auto iface = d()) - return iface->valueAtIndexFn; - return false; -} - -/*! - Retrieves the value at \a index in the \a container and places it in the - memory location pointed to by \a result, if that is possible. - - \sa canGetValueAtIndex() - */ -void QMetaSequence::valueAtIndex(const void *container, qsizetype index, void *result) const -{ - if (canGetValueAtIndex()) - d()->valueAtIndexFn(container, index, result); -} - -/*! - Returns \c true if a value can be written to the container by index, - otherwise \c false. - - \sa setValueAtIndex() -*/ -bool QMetaSequence::canSetValueAtIndex() const -{ - if (auto iface = d()) - return iface->setValueAtIndexFn; - return false; -} - -/*! - Overwrites the value at \a index in the \a container using the \a value - passed as parameter if that is possible. - - \sa canSetValueAtIndex() - */ -void QMetaSequence::setValueAtIndex(void *container, qsizetype index, const void *value) const -{ - if (canSetValueAtIndex()) - d()->setValueAtIndexFn(container, index, value); -} - -/*! - Returns \c true if values can be added to the container, \c false - otherwise. - - \sa addValue(), isSortable() - */ -bool QMetaSequence::canAddValue() const -{ - if (auto iface = d()) - return iface->addValueFn; - return false; -} - -/*! - Adds \a value to the \a container if possible. If \l canAddValue() - returns \c false, the \a value is not added. Else, if - \l canAddValueAtEnd() returns \c true, the \a value is added - to the end of the \a container. Else, if - \l canAddValueAtBegin() returns \c true, the \a value is added to - the beginning of the container. Else, the value is added in an unspecified - place or not at all. The latter is the case for adding values to an - unordered container, for example \l QSet. - - \sa canAddValue(), canAddValueAtBegin(), - canAddValueAtEnd(), isSortable(), removeValue() - */ -void QMetaSequence::addValue(void *container, const void *value) const -{ - if (canAddValue()) { - d()->addValueFn(container, value, - QtMetaContainerPrivate::QMetaSequenceInterface::Unspecified); - } -} - -/*! - Returns \c true if values can be removed from the container, \c false - otherwise. - - \sa removeValue(), isSortable() - */ -bool QMetaSequence::canRemoveValue() const -{ - if (auto iface = d()) - return iface->removeValueFn; - return false; -} - -/*! - Removes a value from the \a container if possible. If - \l canRemoveValue() returns \c false, no value is removed. Else, if - \l canRemoveValueAtEnd() returns \c true, the last value in - the \a container is removed. Else, if \l canRemoveValueAtBegin() - returns \c true, the first value in the \a container is removed. Else, - an unspecified value or nothing is removed. - - \sa canRemoveValue(), canRemoveValueAtBegin(), - canRemoveValueAtEnd(), isSortable(), addValue() - */ -void QMetaSequence::removeValue(void *container) const -{ - if (canRemoveValue()) { - d()->removeValueFn(container, - QtMetaContainerPrivate::QMetaSequenceInterface::Unspecified); - } -} - -/*! Returns \c true if the underlying container offers a non-const iterator, \c false otherwise. @@ -456,7 +168,7 @@ void *QMetaContainer::begin(void *container) const { return hasIterator() ? d_ptr->createIteratorFn( - container, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin) + container, QtMetaContainerPrivate::QMetaContainerInterface::AtBegin) : nullptr; } @@ -473,7 +185,7 @@ void *QMetaContainer::end(void *container) const { return hasIterator() ? d_ptr->createIteratorFn( - container, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd) + container, QtMetaContainerPrivate::QMetaContainerInterface::AtEnd) : nullptr; } @@ -541,137 +253,6 @@ qsizetype QMetaContainer::diffIterator(const void *i, const void *j) const } /*! - Returns \c true if the underlying container can retrieve the value pointed - to by a non-const iterator, \c false otherwise. - - \sa hasIterator(), valueAtIterator() - */ -bool QMetaSequence::canGetValueAtIterator() const -{ - if (auto iface = d()) - return iface->valueAtIteratorFn; - return false; -} - -/*! - Retrieves the value pointed to by the non-const \a iterator and stores it - in the memory location pointed to by \a result, if possible. - - \sa canGetValueAtIterator(), begin(), end() - */ -void QMetaSequence::valueAtIterator(const void *iterator, void *result) const -{ - if (canGetValueAtIterator()) - d()->valueAtIteratorFn(iterator, result); -} - -/*! - Returns \c true if the underlying container can write to the value pointed - to by a non-const iterator, \c false otherwise. - - \sa hasIterator(), setValueAtIterator() - */ -bool QMetaSequence::canSetValueAtIterator() const -{ - if (auto iface = d()) - return iface->setValueAtIteratorFn; - return false; -} - -/*! - Writes \a value to the value pointed to by the non-const \a iterator, if - possible. - - \sa canSetValueAtIterator(), begin(), end() - */ -void QMetaSequence::setValueAtIterator(const void *iterator, const void *value) const -{ - if (canSetValueAtIterator()) - d()->setValueAtIteratorFn(iterator, value); -} - -/*! - Returns \c true if the underlying container can insert a new value, taking - the location pointed to by a non-const iterator into account. - - \sa hasIterator(), insertValueAtIterator() - */ -bool QMetaSequence::canInsertValueAtIterator() const -{ - if (auto iface = d()) - return iface->insertValueAtIteratorFn; - return false; -} - -/*! - Inserts \a value into the \a container, if possible, taking the non-const - \a iterator into account. If \l canInsertValueAtIterator() returns - \c false, the \a value is not inserted. Else if \l isSortable() returns - \c true, the value is inserted before the value pointed to by - \a iterator. Else, the \a value is inserted at an unspecified place or not - at all. In the latter case, the \a iterator is taken as a hint. If it points - to the correct place for the \a value, the operation may be faster than a - \l addValue() without iterator. - - \sa canInsertValueAtIterator(), isSortable(), begin(), end() - */ -void QMetaSequence::insertValueAtIterator(void *container, const void *iterator, - const void *value) const -{ - if (canInsertValueAtIterator()) - d()->insertValueAtIteratorFn(container, iterator, value); -} - -/*! - Returns \c true if the value pointed to by a non-const iterator can be - erased, \c false otherwise. - - \sa hasIterator(), eraseValueAtIterator() - */ -bool QMetaSequence::canEraseValueAtIterator() const -{ - if (auto iface = d()) - return iface->eraseValueAtIteratorFn; - return false; -} - -/*! - Erases the value pointed to by the non-const \a iterator from the - \a container, if possible. - - \sa canEraseValueAtIterator(), begin(), end() - */ -void QMetaSequence::eraseValueAtIterator(void *container, const void *iterator) const -{ - if (canEraseValueAtIterator()) - d()->eraseValueAtIteratorFn(container, iterator); -} - -/*! - Returns \c true if a range between two iterators can be erased from the - container, \c false otherwise. - */ -bool QMetaSequence::canEraseRangeAtIterator() const -{ - if (auto iface = d()) - return iface->eraseRangeAtIteratorFn; - return false; -} - -/*! - Erases the range of values between the iterators \a iterator1 and - \a iterator2 from the \a container, if possible. - - \sa canEraseValueAtIterator(), begin(), end() - */ -void QMetaSequence::eraseRangeAtIterator(void *container, const void *iterator1, - const void *iterator2) const -{ - if (canEraseRangeAtIterator()) - d()->eraseRangeAtIteratorFn(container, iterator1, iterator2); -} - -/*! Returns \c true if the underlying container offers a const iterator, \c false otherwise. @@ -704,7 +285,7 @@ void *QMetaContainer::constBegin(const void *container) const { return hasConstIterator() ? d_ptr->createConstIteratorFn( - container, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin) + container, QtMetaContainerPrivate::QMetaContainerInterface::AtBegin) : nullptr; } @@ -721,7 +302,7 @@ void *QMetaContainer::constEnd(const void *container) const { return hasConstIterator() ? d_ptr->createConstIteratorFn( - container, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd) + container, QtMetaContainerPrivate::QMetaContainerInterface::AtEnd) : nullptr; } @@ -788,68 +369,4 @@ qsizetype QMetaContainer::diffConstIterator(const void *i, const void *j) const return hasConstIterator() ? d_ptr->diffConstIteratorFn(i, j) : 0; } -/*! - Returns \c true if the underlying container can retrieve the value pointed - to by a const iterator, \c false otherwise. - - \sa hasConstIterator(), valueAtConstIterator() - */ -bool QMetaSequence::canGetValueAtConstIterator() const -{ - if (auto iface = d()) - return iface->valueAtConstIteratorFn; - return false; -} - -/*! - Retrieves the value pointed to by the const \a iterator and stores it - in the memory location pointed to by \a result, if possible. - - \sa canGetValueAtConstIterator(), constBegin(), constEnd() - */ -void QMetaSequence::valueAtConstIterator(const void *iterator, void *result) const -{ - if (canGetValueAtConstIterator()) - d()->valueAtConstIteratorFn(iterator, result); -} - -/*! - \fn bool QMetaSequence::operator==(const QMetaSequence &lhs, const QMetaSequence &rhs) - \since 6.0 - - Returns \c true if the QMetaSequence \a lhs represents the same container type - as the QMetaSequence \a rhs, otherwise returns \c false. -*/ - -/*! - \fn bool QMetaSequence::operator!=(const QMetaSequence &lhs, const QMetaSequence &rhs) - \since 6.0 - - Returns \c true if the QMetaSequence \a lhs represents a different container - type than the QMetaSequence \a rhs, otherwise returns \c false. -*/ - - -/*! - \internal - Returns the meta type for keys in the container. - */ -QMetaType QMetaAssociation::keyMetaType() const -{ - if (auto iface = d()) - return QMetaType(iface->keyMetaType); - return QMetaType(); -} - -/*! - \internal - Returns the meta type for mapped values in the container. - */ -QMetaType QMetaAssociation::mappedMetaType() const -{ - if (auto iface = d()) - return QMetaType(iface->mappedMetaType); - return QMetaType(); -} - QT_END_NAMESPACE 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/qmetasequence.cpp b/src/corelib/kernel/qmetasequence.cpp new file mode 100644 index 00000000000..1d3f3dfd080 --- /dev/null +++ b/src/corelib/kernel/qmetasequence.cpp @@ -0,0 +1,471 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qmetacontainer.h" +#include "qmetatype.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QMetaSequence + \inmodule QtCore + \since 6.0 + \brief The QMetaSequence class allows type erased access to sequential containers. + + \ingroup objectmodel + + \compares equality + + The class provides a number of primitive container operations, using void* + as operands. This way, you can manipulate a generic container retrieved from + a Variant without knowing its type. + + The void* arguments to the various methods are typically created by using + a \l QVariant of the respective container or value type, and calling + its \l QVariant::data() or \l QVariant::constData() methods. However, you + can also pass plain pointers to objects of the container or value type. + + Iterator invalidation follows the rules given by the underlying containers + and is not expressed in the API. Therefore, for a truly generic container, + any iterators should be considered invalid after any write operation. +*/ + +/*! + \fn template<typename C> QMetaSequence QMetaSequence::fromContainer() + \since 6.0 + + Returns the QMetaSequence corresponding to the type given as template parameter. +*/ + +/*! + Returns the meta type for values stored in the container. + */ +QMetaType QMetaSequence::valueMetaType() const +{ + if (auto iface = d()) + return QMetaType(iface->valueMetaType); + return QMetaType(); +} + +/*! + Returns \c true if the underlying container is sortable, otherwise returns + \c false. A container is considered sortable if values added to it are + placed in a defined location. Inserting into or adding to a sortable + container will always succeed. Inserting into or adding to an unsortable + container may not succeed, for example if the container is a QSet that + already contains the value being inserted. + + \sa addValue(), insertValueAtIterator(), canAddValueAtBegin(), + canAddValueAtEnd(), canRemoveValueAtBegin(), canRemoveValueAtEnd() + */ +bool QMetaSequence::isSortable() const +{ + if (auto iface = d()) { + return (iface->addRemoveCapabilities + & (QtMetaContainerPrivate::CanAddAtBegin | QtMetaContainerPrivate::CanAddAtEnd)) + && (iface->addRemoveCapabilities + & (QtMetaContainerPrivate::CanRemoveAtBegin + | QtMetaContainerPrivate::CanRemoveAtEnd)); + } + return false; +} + +/*! + Returns \c true if values added using \l addValue() can be placed at the + beginning of the container, otherwise returns \c false. + + \sa addValueAtBegin(), canAddValueAtEnd() + */ +bool QMetaSequence::canAddValueAtBegin() const +{ + if (auto iface = d()) { + return iface->addValueFn + && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanAddAtBegin; + } + return false; +} + +/*! + Adds \a value to the beginning of \a container if possible. If + \l canAddValueAtBegin() returns \c false, the \a value is not added. + + \sa canAddValueAtBegin(), isSortable(), removeValueAtBegin() + */ +void QMetaSequence::addValueAtBegin(void *container, const void *value) const +{ + if (canAddValueAtBegin()) + d()->addValueFn(container, value, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin); +} + +/*! + Returns \c true if values can be removed from the beginning of the container + using \l removeValue(), otherwise returns \c false. + + \sa removeValueAtBegin(), canRemoveValueAtEnd() + */ +bool QMetaSequence::canRemoveValueAtBegin() const +{ + if (auto iface = d()) { + return iface->removeValueFn + && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanRemoveAtBegin; + } + return false; +} + +/*! + Removes a value from the beginning of \a container if possible. If + \l canRemoveValueAtBegin() returns \c false, the value is not removed. + + \sa canRemoveValueAtBegin(), isSortable(), addValueAtBegin() + */ +void QMetaSequence::removeValueAtBegin(void *container) const +{ + if (canRemoveValueAtBegin()) + d()->removeValueFn(container, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin); +} + +/*! + Returns \c true if values added using \l addValue() can be placed at the + end of the container, otherwise returns \c false. + + \sa addValueAtEnd(), canAddValueAtBegin() + */ +bool QMetaSequence::canAddValueAtEnd() const +{ + if (auto iface = d()) { + return iface->addValueFn + && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanAddAtEnd; + } + return false; +} + +/*! + Adds \a value to the end of \a container if possible. If + \l canAddValueAtEnd() returns \c false, the \a value is not added. + + \sa canAddValueAtEnd(), isSortable(), removeValueAtEnd() + */ +void QMetaSequence::addValueAtEnd(void *container, const void *value) const +{ + if (canAddValueAtEnd()) + d()->addValueFn(container, value, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd); +} + +/*! + Returns \c true if values can be removed from the end of the container + using \l removeValue(), otherwise returns \c false. + + \sa removeValueAtEnd(), canRemoveValueAtBegin() + */ +bool QMetaSequence::canRemoveValueAtEnd() const +{ + if (auto iface = d()) { + return iface->removeValueFn + && iface->addRemoveCapabilities & QtMetaContainerPrivate::CanRemoveAtEnd; + } + return false; +} + +/*! + Removes a value from the end of \a container if possible. If + \l canRemoveValueAtEnd() returns \c false, the value is not removed. + + \sa canRemoveValueAtEnd(), isSortable(), addValueAtEnd() + */ +void QMetaSequence::removeValueAtEnd(void *container) const +{ + if (canRemoveValueAtEnd()) + d()->removeValueFn(container, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd); +} + +/*! + Returns \c true if values can be retrieved from the container by index, + otherwise \c false. + + \sa valueAtIndex() + */ +bool QMetaSequence::canGetValueAtIndex() const +{ + if (auto iface = d()) + return iface->valueAtIndexFn; + return false; +} + +/*! + Retrieves the value at \a index in the \a container and places it in the + memory location pointed to by \a result, if that is possible. + + \sa canGetValueAtIndex() + */ +void QMetaSequence::valueAtIndex(const void *container, qsizetype index, void *result) const +{ + if (canGetValueAtIndex()) + d()->valueAtIndexFn(container, index, result); +} + +/*! + Returns \c true if a value can be written to the container by index, + otherwise \c false. + + \sa setValueAtIndex() +*/ +bool QMetaSequence::canSetValueAtIndex() const +{ + if (auto iface = d()) + return iface->setValueAtIndexFn; + return false; +} + +/*! + Overwrites the value at \a index in the \a container using the \a value + passed as parameter if that is possible. + + \sa canSetValueAtIndex() + */ +void QMetaSequence::setValueAtIndex(void *container, qsizetype index, const void *value) const +{ + if (canSetValueAtIndex()) + d()->setValueAtIndexFn(container, index, value); +} + +/*! + Returns \c true if values can be added to the container, \c false + otherwise. + + \sa addValue(), isSortable() + */ +bool QMetaSequence::canAddValue() const +{ + if (auto iface = d()) + return iface->addValueFn; + return false; +} + +/*! + Adds \a value to the \a container if possible. If \l canAddValue() + returns \c false, the \a value is not added. Else, if + \l canAddValueAtEnd() returns \c true, the \a value is added + to the end of the \a container. Else, if + \l canAddValueAtBegin() returns \c true, the \a value is added to + the beginning of the container. Else, the value is added in an unspecified + place or not at all. The latter is the case for adding values to an + unordered container, for example \l QSet. + + \sa canAddValue(), canAddValueAtBegin(), + canAddValueAtEnd(), isSortable(), removeValue() + */ +void QMetaSequence::addValue(void *container, const void *value) const +{ + if (canAddValue()) { + d()->addValueFn(container, value, + QtMetaContainerPrivate::QMetaSequenceInterface::Unspecified); + } +} + +/*! + Returns \c true if values can be removed from the container, \c false + otherwise. + + \sa removeValue(), isSortable() + */ +bool QMetaSequence::canRemoveValue() const +{ + if (auto iface = d()) + return iface->removeValueFn; + return false; +} + +/*! + Removes a value from the \a container if possible. If + \l canRemoveValue() returns \c false, no value is removed. Else, if + \l canRemoveValueAtEnd() returns \c true, the last value in + the \a container is removed. Else, if \l canRemoveValueAtBegin() + returns \c true, the first value in the \a container is removed. Else, + an unspecified value or nothing is removed. + + \sa canRemoveValue(), canRemoveValueAtBegin(), + canRemoveValueAtEnd(), isSortable(), addValue() + */ +void QMetaSequence::removeValue(void *container) const +{ + if (canRemoveValue()) { + d()->removeValueFn(container, + QtMetaContainerPrivate::QMetaSequenceInterface::Unspecified); + } +} + + +/*! + Returns \c true if the underlying container can retrieve the value pointed + to by a non-const iterator, \c false otherwise. + + \sa hasIterator(), valueAtIterator() + */ +bool QMetaSequence::canGetValueAtIterator() const +{ + if (auto iface = d()) + return iface->valueAtIteratorFn; + return false; +} + +/*! + Retrieves the value pointed to by the non-const \a iterator and stores it + in the memory location pointed to by \a result, if possible. + + \sa canGetValueAtIterator(), begin(), end() + */ +void QMetaSequence::valueAtIterator(const void *iterator, void *result) const +{ + if (canGetValueAtIterator()) + d()->valueAtIteratorFn(iterator, result); +} + +/*! + Returns \c true if the underlying container can write to the value pointed + to by a non-const iterator, \c false otherwise. + + \sa hasIterator(), setValueAtIterator() + */ +bool QMetaSequence::canSetValueAtIterator() const +{ + if (auto iface = d()) + return iface->setValueAtIteratorFn; + return false; +} + +/*! + Writes \a value to the value pointed to by the non-const \a iterator, if + possible. + + \sa canSetValueAtIterator(), begin(), end() + */ +void QMetaSequence::setValueAtIterator(const void *iterator, const void *value) const +{ + if (canSetValueAtIterator()) + d()->setValueAtIteratorFn(iterator, value); +} + +/*! + Returns \c true if the underlying container can insert a new value, taking + the location pointed to by a non-const iterator into account. + + \sa hasIterator(), insertValueAtIterator() + */ +bool QMetaSequence::canInsertValueAtIterator() const +{ + if (auto iface = d()) + return iface->insertValueAtIteratorFn; + return false; +} + +/*! + Inserts \a value into the \a container, if possible, taking the non-const + \a iterator into account. If \l canInsertValueAtIterator() returns + \c false, the \a value is not inserted. Else if \l isSortable() returns + \c true, the value is inserted before the value pointed to by + \a iterator. Else, the \a value is inserted at an unspecified place or not + at all. In the latter case, the \a iterator is taken as a hint. If it points + to the correct place for the \a value, the operation may be faster than a + \l addValue() without iterator. + + \sa canInsertValueAtIterator(), isSortable(), begin(), end() + */ +void QMetaSequence::insertValueAtIterator(void *container, const void *iterator, + const void *value) const +{ + if (canInsertValueAtIterator()) + d()->insertValueAtIteratorFn(container, iterator, value); +} + +/*! + Returns \c true if the value pointed to by a non-const iterator can be + erased, \c false otherwise. + + \sa hasIterator(), eraseValueAtIterator() + */ +bool QMetaSequence::canEraseValueAtIterator() const +{ + if (auto iface = d()) + return iface->eraseValueAtIteratorFn; + return false; +} + +/*! + Erases the value pointed to by the non-const \a iterator from the + \a container, if possible. + + \sa canEraseValueAtIterator(), begin(), end() + */ +void QMetaSequence::eraseValueAtIterator(void *container, const void *iterator) const +{ + if (canEraseValueAtIterator()) + d()->eraseValueAtIteratorFn(container, iterator); +} + +/*! + Returns \c true if a range between two iterators can be erased from the + container, \c false otherwise. + */ +bool QMetaSequence::canEraseRangeAtIterator() const +{ + if (auto iface = d()) + return iface->eraseRangeAtIteratorFn; + return false; +} + +/*! + Erases the range of values between the iterators \a iterator1 and + \a iterator2 from the \a container, if possible. + + \sa canEraseValueAtIterator(), begin(), end() + */ +void QMetaSequence::eraseRangeAtIterator(void *container, const void *iterator1, + const void *iterator2) const +{ + if (canEraseRangeAtIterator()) + d()->eraseRangeAtIteratorFn(container, iterator1, iterator2); +} + + +/*! + Returns \c true if the underlying container can retrieve the value pointed + to by a const iterator, \c false otherwise. + + \sa hasConstIterator(), valueAtConstIterator() + */ +bool QMetaSequence::canGetValueAtConstIterator() const +{ + if (auto iface = d()) + return iface->valueAtConstIteratorFn; + return false; +} + +/*! + Retrieves the value pointed to by the const \a iterator and stores it + in the memory location pointed to by \a result, if possible. + + \sa canGetValueAtConstIterator(), constBegin(), constEnd() + */ +void QMetaSequence::valueAtConstIterator(const void *iterator, void *result) const +{ + if (canGetValueAtConstIterator()) + d()->valueAtConstIteratorFn(iterator, result); +} + +/*! + \fn bool QMetaSequence::operator==(const QMetaSequence &lhs, const QMetaSequence &rhs) + \since 6.0 + + Returns \c true if the QMetaSequence \a lhs represents the same container type + as the QMetaSequence \a rhs, otherwise returns \c false. +*/ + +/*! + \fn bool QMetaSequence::operator!=(const QMetaSequence &lhs, const QMetaSequence &rhs) + \since 6.0 + + Returns \c true if the QMetaSequence \a lhs represents a different container + type than the QMetaSequence \a rhs, otherwise returns \c false. +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index fe510898429..560a8c7d789 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 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 9740edbf299..37bf3f99ae1 100644 --- a/src/corelib/kernel/qwinregistry.cpp +++ b/src/corelib/kernel/qwinregistry.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #define UMDF_USING_NTSTATUS // Avoid ntstatus redefinitions @@ -190,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/kernel/qwinregistry_p.h b/src/corelib/kernel/qwinregistry_p.h index 5643a35363b..fd6d6dc853f 100644 --- a/src/corelib/kernel/qwinregistry_p.h +++ b/src/corelib/kernel/qwinregistry_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:trivial-impl-only #ifndef QWINREGISTRY_H #define QWINREGISTRY_H @@ -41,12 +42,17 @@ public: ~QWinRegistryKey(); QWinRegistryKey(QWinRegistryKey &&other) noexcept +#if 1 // QTBUG-140725 + = delete; + void operator=(QWinRegistryKey &&) = delete; +#else : m_key(std::exchange(other.m_key, nullptr)) {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QWinRegistryKey) void swap(QWinRegistryKey &other) noexcept { qt_ptr_swap(m_key, other.m_key); } +#endif [[nodiscard]] bool isValid() const { return m_key != nullptr; } 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/qcborarray.cpp b/src/corelib/serialization/qcborarray.cpp index 9a566b999b4..de6edcb75e6 100644 --- a/src/corelib/serialization/qcborarray.cpp +++ b/src/corelib/serialization/qcborarray.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2018 Intel Corporation. // 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 "qcborarray.h" #include "qcborvalue_p.h" diff --git a/src/corelib/serialization/qcborcommon_p.h b/src/corelib/serialization/qcborcommon_p.h index b80451be4bc..66c95ee887f 100644 --- a/src/corelib/serialization/qcborcommon_p.h +++ b/src/corelib/serialization/qcborcommon_p.h @@ -1,6 +1,7 @@ // Copyright (C) 2018 Intel Corporation. // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:critical reason:data-parser #ifndef QCBORCOMMON_P_H #define QCBORCOMMON_P_H diff --git a/src/corelib/serialization/qcbormap.cpp b/src/corelib/serialization/qcbormap.cpp index 32d6d563ac4..9c17e07b70b 100644 --- a/src/corelib/serialization/qcbormap.cpp +++ b/src/corelib/serialization/qcbormap.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2018 Intel Corporation. // 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 "qcbormap.h" #include "qcborvalue_p.h" diff --git a/src/corelib/serialization/qcborstream.h b/src/corelib/serialization/qcborstream.h index 7850d266639..e51296f3cca 100644 --- a/src/corelib/serialization/qcborstream.h +++ b/src/corelib/serialization/qcborstream.h @@ -1,6 +1,7 @@ // Copyright (C) 2019 The Qt Company Ltd. // Copyright (C) 2018 Intel Corporation. // 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:header-decls-only #ifndef QCBORSTREAM_H #define QCBORSTREAM_H 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/serialization/qjsonparser_p.h b/src/corelib/serialization/qjsonparser_p.h index 6b70af38152..8951bb3129b 100644 --- a/src/corelib/serialization/qjsonparser_p.h +++ b/src/corelib/serialization/qjsonparser_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:header-decls-only #ifndef QJSONPARSER_P_H #define QJSONPARSER_P_H diff --git a/src/corelib/serialization/qjsonwriter_p.h b/src/corelib/serialization/qjsonwriter_p.h index 446ed906e21..10b89f3c106 100644 --- a/src/corelib/serialization/qjsonwriter_p.h +++ b/src/corelib/serialization/qjsonwriter_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:header-decls-only #ifndef QJSONWRITER_P_H #define QJSONWRITER_P_H 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/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index ccd944a0824..3e08df2b5ac 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -1291,6 +1291,9 @@ bool QThread::event(QEvent *event) This function does not stop any event loop running on the thread and does not terminate it in any way. + This function has no effect on the main thread, and does nothing if the thread + is not currently running. + \sa isInterruptionRequested() */ diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h index 611c8e4b5e7..2714c67b093 100644 --- a/src/corelib/time/qtimezoneprivate_p.h +++ b/src/corelib/time/qtimezoneprivate_p.h @@ -162,7 +162,7 @@ public: QByteArray ianaId; qsizetype nameLength = 0; QTimeZone::TimeType timeType = QTimeZone::GenericTime; - operator bool() { return nameLength > 0; } + operator bool() const { return nameLength > 0; } }; static NamePrefixMatch findLongNamePrefix(QStringView text, const QLocale &locale, std::optional<qint64> atEpochMillis = std::nullopt); diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index c20abd12c23..80641c4a281 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,146 @@ 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. + using Category = typename std::iterator_traits<InputIterator>::iterator_category; + constexpr bool IsFwdIt = std::is_convertible_v<Category, std::forward_iterator_tag>; + + 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; + } + } + + assign_impl(first, last, dst, dend, proj, Category{}); + } + + template <typename InputIterator, typename Projection> + void assign_impl(InputIterator first, InputIterator last, T *dst, T *dend, Projection proj, + std::input_iterator_tag) + { + 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 + 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(); + } + + template <typename InputIterator, typename Projection> + void assign_impl(InputIterator first, InputIterator last, T *dst, T *, Projection proj, + std::forward_iterator_tag) + { + constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>; + const qsizetype n = std::distance(first, last); + if constexpr (IsIdentity && !QTypeInfo<T>::isComplex) { + // For non-complex types, we prefer a single std::copy() -> memcpy() + // call. We can do that because either the default constructor is + // trivial (so the lifetime has started) or the copy constructor is + // (and won't care what the stored value is). + std::copy(first, last, dst); + } else { + // overwrite existing elements and create new + qsizetype i = 0; + qsizetype size = this->size; + for ( ; i < size; ++i) { + *dst = std::invoke(proj, *first); // overwrite existing element + ++first; + ++dst; + } + for ( ; i < n; ++i) { + q20::construct_at(dst, std::invoke(proj, *first)); + ++first; + ++dst; + } + if (i < size) + std::destroy_n(dst, size - i); + } + this->size = n; + } }; } // namespace QtPrivate diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 7fa6f2e7dd9..52984e40f31 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -7,9 +7,6 @@ #include <QtCore/qarraydataops.h> #include <QtCore/qcontainertools_impl.h> -#include <QtCore/q20functional.h> -#include <QtCore/q20memory.h> - QT_BEGIN_NAMESPACE template <class T> @@ -320,98 +317,6 @@ public: this->ptr = res; } - template <typename InputIterator, typename Projection = q20::identity> - void assign(InputIterator first, InputIterator last, Projection proj = {}) - { - // This function only provides the basic exception guarantee. - constexpr bool IsFwdIt = std::is_convertible_v< - typename std::iterator_traits<InputIterator>::iterator_category, - std::forward_iterator_tag>; - constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>; - - if constexpr (IsFwdIt) { - const qsizetype n = std::distance(first, last); - if (needsDetach() || n > constAllocatedCapacity()) { - QArrayDataPointer allocated(detachCapacity(n)); - swap(allocated); - } - } else if (needsDetach()) { - QArrayDataPointer allocated(allocatedCapacity()); - swap(allocated); - // We don't want to copy data that we know we'll overwrite - } - - auto offset = freeSpaceAtBegin(); - const auto capacityBegin = begin() - offset; - const auto prependBufferEnd = begin(); - - if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) { - // If construction can throw, and we have freeSpaceAtBegin(), - // it's easiest to just clear the container and start fresh. - // The alternative would be to keep track of two active, disjoint ranges. - if (offset) { - (*this)->truncate(0); - setBegin(capacityBegin); - offset = 0; - } - } - - auto dst = capacityBegin; - const auto dend = end(); - if (offset) { // avoids dead stores - setBegin(capacityBegin); // undo prepend optimization - - // By construction, the following loop is nothrow! - // (otherwise, we can't reach here) - // Assumes InputIterator operations don't throw. - // (but we can't statically assert that, as these operations - // have preconditons, so typically aren't noexcept) - while (true) { - if (dst == prependBufferEnd) { // ran out of prepend buffer space - size += offset; - // we now have a contiguous buffer, continue with the main loop: - break; - } - if (first == last) { // ran out of elements to assign - std::destroy(prependBufferEnd, dend); - size = dst - begin(); - return; - } - // construct element in prepend buffer - q20::construct_at(dst, std::invoke(proj, *first)); - ++dst; - ++first; - } - } - - while (true) { - if (first == last) { // ran out of elements to assign - std::destroy(dst, dend); - break; - } - if (dst == dend) { // ran out of existing elements to overwrite - if constexpr (IsFwdIt && IsIdentity) { - dst = std::uninitialized_copy(first, last, dst); - break; - } else if constexpr (IsFwdIt && !IsIdentity - && std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) { - for (; first != last; ++dst, ++first) // uninitialized_copy with projection - q20::construct_at(dst, std::invoke(proj, *first)); - break; - } else { - do { - (*this)->emplace(size, std::invoke(proj, *first)); - } while (++first != last); - return; // size() is already correct (and dst invalidated)! - } - } - *dst = std::invoke(proj, *first); // overwrite existing element - ++dst; - ++first; - } - size = dst - begin(); - } - QArrayDataPointer sliced(qsizetype pos, qsizetype n) const & { QArrayDataPointer result(n); diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index fea5bdfa906..092ff46b084 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -235,6 +235,8 @@ public: // when not called from the static hash() function, this function needs to be // called with finalizeMutex held (finalize() will do that): void finalizeUnchecked() noexcept; + QSpan<uchar> finalizeUnchecked(QSpan<uchar> buffer) noexcept; + // END functions that need to be called with finalizeMutex held QByteArrayView resultView() const noexcept { return result.toByteArrayView(); } static bool supportsAlgorithm(QCryptographicHash::Algorithm method); @@ -268,7 +270,7 @@ public: explicit EVP(QCryptographicHash::Algorithm method); void reset() noexcept; - void finalizeUnchecked(HashResult &result) noexcept; + void finalizeUnchecked(QSpan<uchar> buffer) noexcept; }; #endif @@ -281,7 +283,7 @@ public: void reset(QCryptographicHash::Algorithm method) noexcept; void addData(QCryptographicHash::Algorithm method, QByteArrayView data) noexcept; - void finalizeUnchecked(QCryptographicHash::Algorithm method, HashResult &result) noexcept; + void finalizeUnchecked(QCryptographicHash::Algorithm method, QSpan<uchar> buffer) noexcept; Sha1State sha1Context; #ifdef USING_OPENSSL30 @@ -297,7 +299,7 @@ public: SHA3Context sha3Context; enum class Sha3Variant { Sha3, Keccak }; - static void sha3Finish(SHA3Context &ctx, HashResult &result, Sha3Variant sha3Variant); + static void sha3Finish(SHA3Context &ctx, QSpan<uchar> result, Sha3Variant sha3Variant); blake2b_state blake2bContext; blake2s_state blake2sContext; } state; @@ -308,7 +310,7 @@ public: const QCryptographicHash::Algorithm method; }; -void QCryptographicHashPrivate::State::sha3Finish(SHA3Context &ctx, HashResult &result, +void QCryptographicHashPrivate::State::sha3Finish(SHA3Context &ctx, QSpan<uchar> result, Sha3Variant sha3Variant) { /* @@ -981,9 +983,23 @@ void QCryptographicHashPrivate::finalizeUnchecked() noexcept state.finalizeUnchecked(method, result); } +/*! + \internal + + Must be called with finalizeMutex held, except when called from the static + hash() function, where no sharing can take place. +*/ +QSpan<uchar> QCryptographicHashPrivate::finalizeUnchecked(QSpan<uchar> buffer) noexcept +{ + buffer = buffer.first(hashLengthInternal(method)); + state.finalizeUnchecked(method, buffer); + Q_ASSERT(result.size() == 0); // internal buffer wasn't used + return buffer; +} + #ifdef USING_OPENSSL30 void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method, - HashResult &result) noexcept + QSpan<uchar> result) noexcept { switch (method) { case QCryptographicHash::Keccak_224: @@ -1030,7 +1046,7 @@ void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Alg } } -void QCryptographicHashPrivate::EVP::finalizeUnchecked(HashResult &result) noexcept +void QCryptographicHashPrivate::EVP::finalizeUnchecked(QSpan<uchar> result) noexcept { if (!initializationFailed) { EVP_MD_CTX_ptr copy = EVP_MD_CTX_ptr(EVP_MD_CTX_new()); @@ -1043,7 +1059,7 @@ void QCryptographicHashPrivate::EVP::finalizeUnchecked(HashResult &result) noexc #else // USING_OPENSSL30 void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method, - HashResult &result) noexcept + QSpan<uchar> result) noexcept { switch (method) { case QCryptographicHash::Sha1: { @@ -1166,12 +1182,8 @@ QByteArrayView QCryptographicHash::hashInto(QSpan<std::byte> buffer, QCryptographicHashPrivate hash(method); for (QByteArrayView part : data) hash.addData(part); - hash.finalizeUnchecked(); // no mutex needed: no-one but us has access to 'hash' - auto result = hash.resultView(); - Q_ASSERT(buffer.size() >= result.size()); - // ### optimize: have the method directly write into `buffer` - memcpy(buffer.data(), result.data(), result.size()); - return buffer.first(result.size()); + auto span = QSpan{reinterpret_cast<uchar *>(buffer.data()), buffer.size()}; + return hash.finalizeUnchecked(span); // no mutex needed: no-one but us has access to 'hash' } /*! diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 7a93ec688ef..93f7ddb9465 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -446,10 +446,8 @@ public: static constexpr qsizetype maxSize() { return Data::maxSize(); } constexpr qsizetype size() const noexcept { -#if __has_cpp_attribute(assume) constexpr size_t MaxSize = maxSize(); - [[assume(size_t(d.size) <= MaxSize)]]; -#endif + Q_PRESUME(size_t(d.size) <= MaxSize); return d.size; } constexpr qsizetype count() const noexcept { return size(); } @@ -578,7 +576,7 @@ public: template <typename InputIterator, if_input_iterator<InputIterator> = true> QList &assign(InputIterator first, InputIterator last) - { d.assign(first, last); return *this; } + { d->assign(first, last); return *this; } QList &assign(std::initializer_list<T> l) { return assign(l.begin(), l.end()); } diff --git a/src/corelib/tools/qshareddata.h b/src/corelib/tools/qshareddata.h index 7d0cec8c899..595efd7e3bf 100644 --- a/src/corelib/tools/qshareddata.h +++ b/src/corelib/tools/qshareddata.h @@ -4,16 +4,35 @@ #ifndef QSHAREDDATA_H #define QSHAREDDATA_H -#include <QtCore/qglobal.h> #include <QtCore/qatomic.h> #include <QtCore/qcompare.h> #include <QtCore/qhashfunctions.h> - QT_BEGIN_NAMESPACE - template <class T> class QSharedDataPointer; +template <class T> class QExplicitlySharedDataPointer; + +namespace QtPrivate { +template <template <typename> class P, typename T> struct QSharedDataPointerTraits; +template <typename T> struct QSharedDataPointerTraits<QSharedDataPointer, T> +{ + static constexpr bool ImplicitlyDetaches = true; + using Type = T; + using pointer = T *; + // for const-qualified functions: + using constT = const T; +}; + +template <typename T> struct QSharedDataPointerTraits<QExplicitlySharedDataPointer, T> +{ + static constexpr bool ImplicitlyDetaches = false; + using Type = T; + using pointer = T *; + // for const-qualified functions: + using constT = T; +}; +} class QSharedData { @@ -30,41 +49,38 @@ public: struct QAdoptSharedDataTag { explicit constexpr QAdoptSharedDataTag() = default; }; -template <typename T> -class QSharedDataPointer +// CRTP common base class for both QSharedDataPointer and QExplicitlySharedDataPointer +template <template <typename> class P, typename T> class QSharedDataPointerBase { +#ifndef Q_QDOC + using Self = P<T>; + using Traits = QtPrivate::QSharedDataPointerTraits<P, T>; + using constT = typename Traits::constT; + +protected: + constexpr QSharedDataPointerBase(T *ptr = nullptr) noexcept : d(ptr) {} + public: - typedef T Type; - typedef T *pointer; + // When adding anything public to this class, make sure to add the doc version to + // both QSharedDataPointer and QExplicitlySharedDataPointer. + + using Type = T; + using pointer = T *; void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } - T &operator*() { detach(); return *(d.get()); } - const T &operator*() const { return *(d.get()); } - T *operator->() { detach(); return d.get(); } - const T *operator->() const noexcept { return d.get(); } - operator T *() { detach(); return d.get(); } + T &operator*() { implicitlyDetach(); return *(d.get()); } + constT &operator*() const { return *(d.get()); } + T *operator->() { implicitlyDetach(); return d.get(); } + constT *operator->() const noexcept { return d.get(); } + operator T *() { implicitlyDetach(); return d.get(); } operator const T *() const noexcept { return d.get(); } - T *data() { detach(); return d.get(); } - T *get() { detach(); return d.get(); } + T *data() { implicitlyDetach(); return d.get(); } + T *get() { implicitlyDetach(); return d.get(); } const T *data() const noexcept { return d.get(); } const T *get() const noexcept { return d.get(); } const T *constData() const noexcept { return d.get(); } T *take() noexcept { return std::exchange(d, nullptr).get(); } - Q_NODISCARD_CTOR - QSharedDataPointer() noexcept : d(nullptr) { } - ~QSharedDataPointer() { if (d && !d->ref.deref()) delete d.get(); } - - Q_NODISCARD_CTOR - explicit QSharedDataPointer(T *data) noexcept : d(data) - { if (d) d->ref.ref(); } - Q_NODISCARD_CTOR - QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) - {} - Q_NODISCARD_CTOR - QSharedDataPointer(const QSharedDataPointer &o) noexcept : d(o.d) - { if (d) d->ref.ref(); } - void reset(T *ptr = nullptr) noexcept { if (ptr != d.get()) { @@ -72,10 +88,97 @@ public: ptr->ref.ref(); T *old = std::exchange(d, Qt::totally_ordered_wrapper(ptr)).get(); if (old && !old->ref.deref()) - delete old; + destroy(old); } } + operator bool () const noexcept { return d != nullptr; } + bool operator!() const noexcept { return d == nullptr; } + + void swap(Self &other) noexcept + { qt_ptr_swap(d, other.d); } + +private: + // The concrete class MUST override these, otherwise we will be calling + // ourselves. + T *clone() { return static_cast<Self *>(this)->clone(); } + template <typename... Args> static T *create(Args &&... args) + { return Self::create(std::forward(args)...); } + static void destroy(T *ptr) { Self::destroy(ptr); } + + void implicitlyDetach() + { + if constexpr (Traits::ImplicitlyDetaches) + static_cast<Self *>(this)->detach(); + } + + friend bool comparesEqual(const QSharedDataPointerBase &lhs, const QSharedDataPointerBase &rhs) noexcept + { return lhs.d == rhs.d; } + friend Qt::strong_ordering + compareThreeWay(const QSharedDataPointerBase &lhs, const QSharedDataPointerBase &rhs) noexcept + { return Qt::compareThreeWay(lhs.d, rhs.d); } + + friend bool comparesEqual(const QSharedDataPointerBase &lhs, const T *rhs) noexcept + { return lhs.d == rhs; } + friend Qt::strong_ordering + compareThreeWay(const QSharedDataPointerBase &lhs, const T *rhs) noexcept + { return Qt::compareThreeWay(lhs.d, rhs); } + + friend bool comparesEqual(const QSharedDataPointerBase &lhs, std::nullptr_t) noexcept + { return lhs.d == nullptr; } + friend Qt::strong_ordering + compareThreeWay(const QSharedDataPointerBase &lhs, std::nullptr_t) noexcept + { return Qt::compareThreeWay(lhs.d, nullptr); } + + friend size_t qHash(const QSharedDataPointerBase &ptr, size_t seed = 0) noexcept + { return qHash(ptr.data(), seed); } + +protected: + void detach_helper(); + + Qt::totally_ordered_wrapper<T *> d; +#endif // !Q_QDOC +}; + +template <typename T> +class QSharedDataPointer : public QSharedDataPointerBase<QSharedDataPointer, T> +{ + using Base = QSharedDataPointerBase<QSharedDataPointer, T>; + friend Base; +public: + typedef T Type; + typedef T *pointer; + + void detach() { Base::detach(); } +#ifdef Q_QDOC + T &operator*(); + const T &operator*() const; + T *operator->(); + const T *operator->() const noexcept; + operator T *(); + operator const T *() const noexcept; + T *data(); + T *get(); + const T *data() const noexcept; + const T *get() const noexcept; + const T *constData() const noexcept; + T *take() noexcept; +#endif + + Q_NODISCARD_CTOR + QSharedDataPointer() noexcept : Base(nullptr) { } + ~QSharedDataPointer() { if (d && !d->ref.deref()) destroy(d.get()); } + + Q_NODISCARD_CTOR + explicit QSharedDataPointer(T *data) noexcept : Base(data) + { if (d) d->ref.ref(); } + Q_NODISCARD_CTOR + QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : Base(data) + {} + Q_NODISCARD_CTOR + QSharedDataPointer(const QSharedDataPointer &o) noexcept : Base(o.d.get()) + { if (d) d->ref.ref(); } + QSharedDataPointer &operator=(const QSharedDataPointer &o) noexcept { reset(o.d.get()); @@ -87,76 +190,80 @@ public: return *this; } Q_NODISCARD_CTOR - QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {} + QSharedDataPointer(QSharedDataPointer &&o) noexcept + : Base(std::exchange(o.d, nullptr).get()) + {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer) - operator bool () const noexcept { return d != nullptr; } - bool operator!() const noexcept { return d == nullptr; } +#ifdef Q_QDOC + void reset(T *ptr = nullptr) noexcept; - void swap(QSharedDataPointer &other) noexcept - { qt_ptr_swap(d, other.d); } + operator bool () const noexcept; + bool operator!() const noexcept; + + void swap(QSharedDataPointer &other) noexcept; +#else + using Base::reset; + using Base::swap; +#endif protected: T *clone(); + template <typename... Args> static T *create(Args &&... args) + { return new T(std::forward(args)...); } + static void destroy(T *ptr) { delete ptr; } private: - friend bool comparesEqual(const QSharedDataPointer &lhs, const QSharedDataPointer &rhs) noexcept - { return lhs.d == rhs.d; } - friend Qt::strong_ordering - compareThreeWay(const QSharedDataPointer &lhs, const QSharedDataPointer &rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs.d); } Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer) - - friend bool comparesEqual(const QSharedDataPointer &lhs, const T *rhs) noexcept - { return lhs.d == rhs; } - friend Qt::strong_ordering - compareThreeWay(const QSharedDataPointer &lhs, const T *rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs); } Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer, T*) - - friend bool comparesEqual(const QSharedDataPointer &lhs, std::nullptr_t) noexcept - { return lhs.d == nullptr; } - friend Qt::strong_ordering - compareThreeWay(const QSharedDataPointer &lhs, std::nullptr_t) noexcept - { return Qt::compareThreeWay(lhs.d, nullptr); } Q_DECLARE_STRONGLY_ORDERED(QSharedDataPointer, std::nullptr_t) - void detach_helper(); - - Qt::totally_ordered_wrapper<T *> d; + using Base::d; }; template <typename T> -class QExplicitlySharedDataPointer +class QExplicitlySharedDataPointer : public QSharedDataPointerBase<QExplicitlySharedDataPointer, T> { + using Base = QSharedDataPointerBase<QExplicitlySharedDataPointer, T>; + friend Base; public: typedef T Type; typedef T *pointer; - T &operator*() const { return *(d.get()); } - T *operator->() noexcept { return d.get(); } - T *operator->() const noexcept { return d.get(); } + // override to make explicit. Can use explicit(!ImplicitlyShared) once we + // can depend on C++20. explicit operator T *() { return d.get(); } explicit operator const T *() const noexcept { return d.get(); } + + // override to make const. There is no const(cond), but we could use + // requires(!ImplicitlyShared) T *data() const noexcept { return d.get(); } T *get() const noexcept { return d.get(); } - const T *constData() const noexcept { return d.get(); } - T *take() noexcept { return std::exchange(d, nullptr).get(); } - void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } +#ifdef Q_QDOC + T &operator*() const; + T *operator->() noexcept; + T *operator->() const noexcept; + T *data() const noexcept; + T *get() const noexcept; + const T *constData() const noexcept; + T *take() noexcept; +#endif + + void detach() { Base::detach(); } Q_NODISCARD_CTOR - QExplicitlySharedDataPointer() noexcept : d(nullptr) { } + QExplicitlySharedDataPointer() noexcept : Base(nullptr) { } ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d.get(); } Q_NODISCARD_CTOR - explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data) + explicit QExplicitlySharedDataPointer(T *data) noexcept : Base(data) { if (d) d->ref.ref(); } Q_NODISCARD_CTOR - QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) + QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : Base(data) {} Q_NODISCARD_CTOR - QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : d(o.d) + QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : Base(o.d.get()) { if (d) d->ref.ref(); } template<typename X> @@ -165,20 +272,9 @@ public: #ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST #error This macro has been removed in Qt 6.9. #endif - : d(o.data()) + : Base(o.data()) { if (d) d->ref.ref(); } - void reset(T *ptr = nullptr) noexcept - { - if (ptr != d) { - if (ptr) - ptr->ref.ref(); - T *old = std::exchange(d, Qt::totally_ordered_wrapper(ptr)).get(); - if (old && !old->ref.deref()) - delete old; - } - } - QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept { reset(o.d.get()); @@ -190,72 +286,52 @@ public: return *this; } Q_NODISCARD_CTOR - QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {} + QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept + : Base(std::exchange(o.d, nullptr).get()) + {} QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer) - operator bool () const noexcept { return d != nullptr; } - bool operator!() const noexcept { return d == nullptr; } +#ifdef Q_QDOC + void reset(T *ptr = nullptr) noexcept; - void swap(QExplicitlySharedDataPointer &other) noexcept - { qt_ptr_swap(d, other.d); } + operator bool () const noexcept; + bool operator!() const noexcept; + + void swap(QExplicitlySharedDataPointer &other) noexcept; +#else + using Base::swap; + using Base::reset; +#endif protected: T *clone(); + template <typename... Args> static T *create(Args &&... args) + { return new T(std::forward(args)...); } + static void destroy(T *ptr) { delete ptr; } private: - friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, - const QExplicitlySharedDataPointer &rhs) noexcept - { return lhs.d == rhs.d; } - friend Qt::strong_ordering - compareThreeWay(const QExplicitlySharedDataPointer &lhs, - const QExplicitlySharedDataPointer &rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs.d); } Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer) - - friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, const T *rhs) noexcept - { return lhs.d == rhs; } - friend Qt::strong_ordering - compareThreeWay(const QExplicitlySharedDataPointer &lhs, const T *rhs) noexcept - { return Qt::compareThreeWay(lhs.d, rhs); } Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, const T*) - - friend bool comparesEqual(const QExplicitlySharedDataPointer &lhs, std::nullptr_t) noexcept - { return lhs.d == nullptr; } - friend Qt::strong_ordering - compareThreeWay(const QExplicitlySharedDataPointer &lhs, std::nullptr_t) noexcept - { return Qt::compareThreeWay(lhs.d, nullptr); } Q_DECLARE_STRONGLY_ORDERED(QExplicitlySharedDataPointer, std::nullptr_t) - void detach_helper(); - - Qt::totally_ordered_wrapper<T *> d; + using Base::d; }; // Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time. template <typename T> Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone() { - return new T(*d); -} - -template <typename T> -Q_OUTOFLINE_TEMPLATE void QSharedDataPointer<T>::detach_helper() -{ - T *x = clone(); - x->ref.ref(); - if (!d.get()->ref.deref()) - delete d.get(); - d.reset(x); + return new T(*this->d); } template <typename T> Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone() { - return new T(*d.get()); + return new T(*this->d.get()); } -template <typename T> -Q_OUTOFLINE_TEMPLATE void QExplicitlySharedDataPointer<T>::detach_helper() +template <template <typename> class P, typename T> Q_OUTOFLINE_TEMPLATE void +QSharedDataPointerBase<P, T>::detach_helper() { T *x = clone(); x->ref.ref(); @@ -272,17 +348,6 @@ template <typename T> void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) noexcept { p1.swap(p2); } -template <typename T> -size_t qHash(const QSharedDataPointer<T> &ptr, size_t seed = 0) noexcept -{ - return qHash(ptr.data(), seed); -} -template <typename T> -size_t qHash(const QExplicitlySharedDataPointer<T> &ptr, size_t seed = 0) noexcept -{ - return qHash(ptr.data(), seed); -} - template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_RELOCATABLE_TYPE); template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_RELOCATABLE_TYPE); diff --git a/src/gui/accessible/linux/qspiaccessiblebridge.cpp b/src/gui/accessible/linux/qspiaccessiblebridge.cpp index 1ee1a435ca5..11b3bc57471 100644 --- a/src/gui/accessible/linux/qspiaccessiblebridge.cpp +++ b/src/gui/accessible/linux/qspiaccessiblebridge.cpp @@ -184,6 +184,12 @@ static RoleMapping map[] = { //: Role of an accessible object { QAccessible::CheckBox, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, //: Role of an accessible object +#if ATSPI_ROLE_COUNT >= 132 + { QAccessible::Switch, ATSPI_ROLE_SWITCH, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "switch") }, +#else + { QAccessible::Switch, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, +#endif + //: Role of an accessible object { QAccessible::RadioButton, ATSPI_ROLE_RADIO_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "radio button") }, //: Role of an accessible object { QAccessible::ComboBox, ATSPI_ROLE_COMBO_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "combo box") }, diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index 97f7eb94e2a..eeb06c535b8 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -342,6 +342,7 @@ Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core"); \value Splitter A splitter distributing available space between its child widgets. \value StaticText Static text, such as labels for other widgets. \value StatusBar A status bar. + \value [since 6.11] Switch A switch that can be toggled on or off. \value Table A table representing data in a grid of rows and columns. \value Terminal A terminal or command line interface. \value TitleBar The title bar caption of a window. diff --git a/src/gui/accessible/qaccessible_base.h b/src/gui/accessible/qaccessible_base.h index 0a1a305b76d..31b97880ffc 100644 --- a/src/gui/accessible/qaccessible_base.h +++ b/src/gui/accessible/qaccessible_base.h @@ -267,6 +267,7 @@ public: WebDocument = 0x00000084, Section = 0x00000085, Notification = 0x00000086, + Switch = 0x00000087, // IAccessible2 roles // IA2_ROLE_CANVAS = 0x401, // An object that can be drawn into and to manage events from the objects drawn into it diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp index 5c723cdb289..9edb0dff4fd 100644 --- a/src/gui/accessible/qaccessibleobject.cpp +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -119,14 +119,14 @@ QWindow *QAccessibleApplication::window() const return nullptr; } -// all toplevel windows except popups and the desktop +// all toplevel windows except popups static QObjectList topLevelObjects() { QObjectList list; const QWindowList tlw(QGuiApplication::topLevelWindows()); for (int i = 0; i < tlw.size(); ++i) { QWindow *w = tlw.at(i); - if (w->type() != Qt::Popup && w->type()) { + if (w->type() != Qt::Popup) { if (QAccessibleInterface *root = w->accessibleRoot()) { if (root->object()) list.append(root->object()); 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/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 8e21c92f9b3..8f29fb503b0 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -352,8 +352,8 @@ QIconTheme::QIconTheme(const QString &themeName) QFile themeIndex; const QStringList iconDirs = QIcon::themeSearchPaths(); - for ( int i = 0 ; i < iconDirs.size() ; ++i) { - QDir iconDir(iconDirs[i]); + for (const auto &dirName : iconDirs) { + QDir iconDir(dirName); QString themeDir = iconDir.path() + u'/' + themeName; QFileInfo themeDirInfo(themeDir); @@ -479,7 +479,7 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, const QString pngIconName = iconNameFallback + ".png"_L1; // Add all relevant files - for (int i = 0; i < contentDirs.size(); ++i) { + for (qsizetype i = 0; i < contentDirs.size(); ++i) { QList<QIconDirInfo> subDirs = theme.keyList(); // Try to reduce the amount of subDirs by looking in the GTK+ cache in order to save @@ -504,8 +504,7 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, } QString contentDir = contentDirs.at(i) + u'/'; - for (int j = 0; j < subDirs.size() ; ++j) { - const QIconDirInfo &dirInfo = subDirs.at(j); + for (const auto &dirInfo : std::as_const(subDirs)) { if (searchingGenericFallback && (dirInfo.context == QIconDirInfo::Applications || dirInfo.context == QIconDirInfo::MimeTypes)) @@ -544,9 +543,9 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName, << "skipping visited" << visited; // Search recursively through inherited themes - for (int i = 0 ; i < parents.size() ; ++i) { + for (const auto &parent : parents) { - const QString parentTheme = parents.at(i).trimmed(); + const QString parentTheme = parent.trimmed(); if (!visited.contains(parentTheme)) // guard against recursion info = findIconHelper(parentTheme, iconName, visited, QIconLoader::NoFallBack); diff --git a/src/gui/image/qplatformpixmap.cpp b/src/gui/image/qplatformpixmap.cpp index a2977360951..d1eab7f6ed3 100644 --- a/src/gui/image/qplatformpixmap.cpp +++ b/src/gui/image/qplatformpixmap.cpp @@ -36,7 +36,6 @@ QPlatformPixmap::QPlatformPixmap(PixelType pixelType, int objectId) h(0), d(0), is_null(true), - ref(0), detach_no(0), type(pixelType), id(objectId), diff --git a/src/gui/image/qplatformpixmap.h b/src/gui/image/qplatformpixmap.h index be86bf8850f..5621afa4da5 100644 --- a/src/gui/image/qplatformpixmap.h +++ b/src/gui/image/qplatformpixmap.h @@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE class QImageReader; -class Q_GUI_EXPORT QPlatformPixmap +class Q_GUI_EXPORT QPlatformPixmap : public QSharedData { public: enum PixelType { @@ -113,10 +113,7 @@ private: friend class QPixmap; friend class QX11PlatformPixmap; friend class QImagePixmapCleanupHooks; // Needs to set is_cached - friend class QOpenGLTextureCache; //Needs to check the reference count - friend class QExplicitlySharedDataPointer<QPlatformPixmap>; - QAtomicInt ref; int detach_no; PixelType type; diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 098d0331327..e44d9b1468b 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -2626,6 +2626,7 @@ void QGuiApplicationPrivate::processEnterEvent(QWindowSystemInterfacePrivate::En } currentMouseWindow = e->enter; + lastCursorPosition = e->globalPos; // TODO later: EnterEvent must report _which_ mouse entered the window; for now we assume primaryPointingDevice() QEnterEvent event(e->localPos, e->localPos, e->globalPos); diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index c7b6e4ebff3..bb71f8fb6fc 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -1298,7 +1298,10 @@ QString QKeySequencePrivate::keyName(Qt::Key key, QKeySequence::SequenceFormat f bool nativeText = (format == QKeySequence::NativeText); QString p; - if (key && key < Qt::Key_Escape && key != Qt::Key_Space) { + if (nativeText && (key > 0x00 && key <= 0x1f)) { + // Map C0 control codes to the corresponding Control Pictures + p = QChar::fromUcs2(0x2400 + key); + } else if (key && key < Qt::Key_Escape && key != Qt::Key_Space) { if (!QChar::requiresSurrogates(key)) { p = QChar::fromUcs2(key).toUpper(); } else { diff --git a/src/gui/kernel/qplatformwindow_p.h b/src/gui/kernel/qplatformwindow_p.h index c446ac760c0..24c0fd7c431 100644 --- a/src/gui/kernel/qplatformwindow_p.h +++ b/src/gui/kernel/qplatformwindow_p.h @@ -125,6 +125,14 @@ struct Q_GUI_EXPORT QWaylandWindow : public QObject public: QT_DECLARE_NATIVE_INTERFACE(QWaylandWindow, 1, QWindow) + enum WindowType { + Default, + ToolTip, + ComboBox, + Menu, + SubMenu, + }; + virtual wl_surface *surface() const = 0; virtual void setCustomMargins(const QMargins &margins) = 0; virtual void requestXdgActivationToken(uint serial) = 0; @@ -136,6 +144,10 @@ public: return role ? *role : nullptr; } virtual void setSessionRestoreId(const QString &role) = 0; + + virtual void setExtendedWindowType(WindowType windowType) = 0; + virtual void setParentControlGeometry(const QRect &parentAnchor) = 0; + Q_SIGNALS: void surfaceCreated(); void surfaceDestroyed(); 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/qpainter.cpp b/src/gui/painting/qpainter.cpp index 3b64a8ecf73..a3f9f069b69 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1527,14 +1527,14 @@ void QPainterPrivate::initFrom(const QPaintDevice *device) Q_Q(QPainter); device->initPainter(q); +} - if (extended) { - extended->penChanged(); - } else if (engine) { - engine->setDirty(QPaintEngine::DirtyPen); - engine->setDirty(QPaintEngine::DirtyBrush); - engine->setDirty(QPaintEngine::DirtyFont); - } +void QPainterPrivate::setEngineDirtyFlags(QSpan<const QPaintEngine::DirtyFlags> flags) +{ + if (!engine) + return; + for (const QPaintEngine::DirtyFlags f : flags) + engine->setDirty(f); } /*! @@ -1801,14 +1801,16 @@ bool QPainter::begin(QPaintDevice *pd) d->engine->setActive(begun); } - // Copy painter properties from original paint device, - // required for QPixmap::grabWidget() - if (d->original_device->devType() == QInternal::Widget) { + switch (d->original_device->devType()) { + case QInternal::Widget: d->initFrom(d->original_device); - } else { + break; + + default: d->state->layoutDirection = Qt::LayoutDirectionAuto; // make sure we have a font compatible with the paintdevice d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device()); + break; } QRect systemRect = d->engine->systemRect(); @@ -1834,6 +1836,15 @@ bool QPainter::begin(QPaintDevice *pd) d->state->emulationSpecifier = 0; + switch (d->original_device->devType()) { + case QInternal::Widget: + // for widgets we've aleady initialized the painter above + break; + default: + d->initFrom(d->original_device); + break; + } + return true; } diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index dd4653a5788..a6f93134ca0 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -15,6 +15,7 @@ // We mean it. // +#include <QtCore/qspan.h> #include <QtCore/qvarlengtharray.h> #include <QtGui/private/qtguiglobal_p.h> #include "QtGui/qbrush.h" @@ -242,6 +243,8 @@ public: std::unique_ptr<QEmulationPaintEngine> emulationEngine; QPaintEngineEx *extended = nullptr; QBrush colorBrush; // for fill with solid color + + Q_GUI_EXPORT void setEngineDirtyFlags(QSpan<const QPaintEngine::DirtyFlags>); }; Q_GUI_EXPORT void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation); diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 79799ca2ece..0d435c95048 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -1154,7 +1154,8 @@ void QDashStroker::processCurrentSubpath() elen -= std::floor(elen * invSumLength) * sumLength; // Update dash offset. while (!done) { - qreal dpos = pos + dashes[idash] - doffset - estart; + // parentheses to avoid float rounding issues: qreal(4) + 0.1 - 0.1 - 4 < 0 + qreal dpos = (pos + dashes[idash]) - (doffset + estart); Q_ASSERT(dpos >= 0); @@ -1189,7 +1190,8 @@ void QDashStroker::processCurrentSubpath() bool has_offset = doffset > 0; bool evenDash = (idash & 1) == 0; - qreal dpos = pos + dashes[idash] - doffset - estart; + // parentheses to avoid float rounding issues: qreal(4) + 0.1 - 0.1 - 4 < 0 + qreal dpos = (pos + dashes[idash]) - (doffset + estart); Q_ASSERT(dpos >= 0); diff --git a/src/gui/text/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/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index bcd9aecdea9..120d2ecbc78 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -326,6 +326,11 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co */ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt, int v) { +#ifdef QNATIVESOCKETENGINE_DEBUG +# define perrorDebug(msg) perror("QNativeSocketEnginePrivate::setOption(): " msg) +#else +# define perrorDebug(msg) (void)0 +#endif Q_Q(QNativeSocketEngine); if (!q->isValid()) return false; @@ -337,25 +342,16 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt #if !defined(Q_OS_VXWORKS) int flags = ::fcntl(socketDescriptor, F_GETFL, 0); if (flags == -1) { -#ifdef QNATIVESOCKETENGINE_DEBUG - perror("QNativeSocketEnginePrivate::setOption(): fcntl(F_GETFL) failed"); -#endif + perrorDebug("fcntl(F_GETFL) failed"); return false; } if (::fcntl(socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) { -#ifdef QNATIVESOCKETENGINE_DEBUG - perror("QNativeSocketEnginePrivate::setOption(): fcntl(F_SETFL) failed"); -#endif + perrorDebug("fcntl(F_SETFL) failed"); return false; } #else // Q_OS_VXWORKS - int onoff = 1; - - if (qt_safe_ioctl(socketDescriptor, FIONBIO, &onoff) < 0) { - -#ifdef QNATIVESOCKETENGINE_DEBUG - perror("QNativeSocketEnginePrivate::setOption(): ioctl(FIONBIO, 1) failed"); -#endif + if (qt_safe_ioctl(socketDescriptor, FIONBIO, &v) < 0) { + perrorDebug("ioctl(FIONBIO, 1) failed"); return false; } #endif // Q_OS_VXWORKS @@ -417,6 +413,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt if (n == -1) return false; return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0; +#undef perrorDebug } bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 port) diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index a1edf49da12..6154f4121d2 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/androidjniaccessibility.cpp @@ -470,7 +470,6 @@ namespace QtAndroidAccessibility case QAccessible::Role::Link: { if (state.checkable) - // There is also a android.widget.Switch for which we have no match. return QStringLiteral("android.widget.ToggleButton"); return QStringLiteral("android.widget.Button"); } @@ -478,6 +477,8 @@ namespace QtAndroidAccessibility // As of android/accessibility/utils/Role.java::getRole a CheckBox // is NOT android.widget.CheckBox return QStringLiteral("android.widget.CompoundButton"); + case QAccessible::Role::Switch: + return QStringLiteral("android.widget.Switch"); case QAccessible::Role::Clock: return QStringLiteral("android.widget.TextClock"); case QAccessible::Role::ComboBox: @@ -725,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/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 8c5335d9c31..9059fab757b 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -359,20 +359,41 @@ namespace QtAndroid static bool initJavaReferences(QJniEnvironment &env); -static void initializeBackends() +static bool initAndroidQpaPlugin(JNIEnv *jenv, jobject object) { + Q_UNUSED(jenv) + Q_UNUSED(object) + + // Init all the Java refs, if they haven't already been initialized. They get initialized + // when the library is loaded, but in case Qt is terminated, they are cleared, and in case + // Qt is then started again JNI_OnLoad will not be called again, since the library is already + // loaded - in that case we need to init again here, hence the check. + // TODO QTBUG-130614 QtCore also inits some Java references in qjnihelpers - we probably + // want to reset those, too. + QJniEnvironment qEnv; + if (!qEnv.isValid()) { + qCritical() << "Failed to initialize the JNI Environment"; + return false; + } + + if (!initJavaReferences(qEnv)) + return false; + + m_androidPlatformIntegration = nullptr; + // File engine handler instantiation registers the handler m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler(); m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler(); m_androidApkFileEngineHandler = new QAndroidApkFileEngineHandler(); m_backendRegister = new AndroidBackendRegister(); -} -static bool initCleanupHandshakeSemaphores() -{ - return sem_init(&m_exitSemaphore, 0, 0) != -1 - && sem_init(&m_stopQtSemaphore, 0, 0) != -1; + if (sem_init(&m_exitSemaphore, 0, 0) == -1 && sem_init(&m_stopQtSemaphore, 0, 0) == -1) { + qCritical() << "Failed to init Qt application cleanup semaphores"; + return false; + } + + return true; } static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring paramsString) @@ -391,23 +412,6 @@ static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring param vm->AttachCurrentThread(&env, &args); } - // Init all the Java refs, if they haven't already been initialized. They get initialized - // when the library is loaded, but in case Qt is terminated, they are cleared, and in case - // Qt is then started again JNI_OnLoad will not be called again, since the library is already - // loaded - in that case we need to init again here, hence the check. - // TODO QTBUG-130614 QtCore also inits some Java references in qjnihelpers - we probably - // want to reset those, too. - QJniEnvironment qEnv; - if (!qEnv.isValid()) { - qCritical() << "Failed to initialize the JNI Environment"; - return; - } - if (!initJavaReferences(qEnv)) - return; - - m_androidPlatformIntegration = nullptr; - initializeBackends(); - const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString()); const int argc = argsList.size(); QVarLengthArray<char *> argv(argc + 1); @@ -444,11 +448,6 @@ static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring param return; } - if (!initCleanupHandshakeSemaphores()) { - qCritical() << "Failed to init Qt application cleanup semaphores"; - return; - } - // Register type for invokeMethod() calls. qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation"); @@ -458,13 +457,6 @@ static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring param startQtAndroidPluginCalled.fetchAndAddRelease(1); - QtNative::callStaticMethod("setStarted", true); - - // The service must wait until the QCoreApplication starts, - // otherwise onBind will be called too early. - if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication()) - QtAndroidPrivate::waitForServiceSetup(); - const int ret = m_main(argc, argv.data()); qInfo() << "main() returned" << ret; @@ -540,6 +532,15 @@ static void clearJavaReferences(JNIEnv *env) } } +static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/) +{ + Q_UNUSED(env); + // The service must wait until the QCoreApplication starts otherwise onBind will be + // called too early + if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication()) + QtAndroidPrivate::waitForServiceSetup(); +} + static void terminateQtNativeApplication(JNIEnv *env, jclass /*clazz*/) { // QAndroidEventDispatcherStopper is stopped when the user uses the task manager @@ -730,8 +731,10 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent) } static JNINativeMethod methods[] = { + { "initAndroidQpaPlugin", "()Z", (void *)initAndroidQpaPlugin }, { "startQtNativeApplication", "(Ljava/lang/String;)V", (void *)startQtNativeApplication }, { "terminateQtNativeApplication", "()V", (void *)terminateQtNativeApplication }, + { "waitForServiceSetup", "()V", (void *)waitForServiceSetup }, { "updateApplicationState", "(I)V", (void *)updateApplicationState }, { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult }, { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent }, diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index ecbbddb2e36..1d755139b46 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -82,9 +82,6 @@ static bool hasValidFocusObject() if (!m_androidInputContext) return false; - if (!m_androidInputContext->isInputPanelVisible()) - return false; - const auto focusObject = m_androidInputContext->focusObject(); if (!focusObject) return false; 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/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp index 822a5357107..0d8673aac03 100644 --- a/src/plugins/platforms/android/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp @@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus") -Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager") +Q_DECLARE_JNI_CLASS(QtWindowInsetsController, "org/qtproject/qt/android/QtWindowInsetsController") using namespace Qt::StringLiterals; @@ -448,9 +448,9 @@ void QAndroidPlatformTheme::requestColorScheme(Qt::ColorScheme scheme) const auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>(); iface->runOnAndroidMainThread([=]() { bool isLight = scheme == Qt::ColorScheme::Light; - QtJniTypes::QtDisplayManager::callStaticMethod("setStatusBarColorHint", + QtJniTypes::QtWindowInsetsController::callStaticMethod("setStatusBarColorHint", iface->context().object<QtJniTypes::Activity>(), isLight); - QtJniTypes::QtDisplayManager::callStaticMethod("setNavigationBarColorHint", + QtJniTypes::QtWindowInsetsController::callStaticMethod("setNavigationBarColorHint", iface->context().object<QtJniTypes::Activity>(), isLight); }); } diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp index 937839ace0c..c4245998772 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp @@ -22,6 +22,7 @@ Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface Q_DECLARE_JNI_CLASS(QtInputConnectionListener, "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener") Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtWindowInterface") +Q_DECLARE_JNI_CLASS(QtWindowInsetsController, "org/qtproject/qt/android/QtWindowInsetsController") QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) : QPlatformWindow(window), m_nativeQtWindow(nullptr), @@ -55,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); @@ -122,7 +120,7 @@ void QAndroidPlatformWindow::raise() QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason); return; } - updateSystemUiVisibility(); + updateSystemUiVisibility(window()->windowStates(), window()->flags()); platformScreen()->raise(this); } @@ -166,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(); } } @@ -187,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) @@ -255,15 +244,21 @@ 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) { - const bool isFullScreen = (m_windowState & Qt::WindowFullScreen); - const bool expandedToCutout = (flags & Qt::ExpandedClientAreaHint); - QtAndroid::backendRegister()->callInterface<QtJniTypes::QtWindowInterface, void>( - "setSystemUiVisibility", isFullScreen, expandedToCutout); + auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>(); + iface->runOnAndroidMainThread([=]() { + using namespace QtJniTypes; + auto activity = iface->context().object<Activity>(); + if (states & Qt::WindowFullScreen) + QtWindowInsetsController::callStaticMethod("showFullScreen", activity); + else if (flags & Qt::ExpandedClientAreaHint) + QtWindowInsetsController::callStaticMethod("showExpanded", activity); + else + QtWindowInsetsController::callStaticMethod("showNormal", 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/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 69ee3638ac6..08c9f5d5ba2 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -132,6 +132,7 @@ static void populateRoleMap() roleMap[QAccessible::ComboBox] = NSAccessibilityComboBoxRole; roleMap[QAccessible::RadioButton] = NSAccessibilityRadioButtonRole; roleMap[QAccessible::CheckBox] = NSAccessibilityCheckBoxRole; + roleMap[QAccessible::Switch] = NSAccessibilityCheckBoxRole; roleMap[QAccessible::StaticText] = NSAccessibilityStaticTextRole; roleMap[QAccessible::Table] = NSAccessibilityTableRole; roleMap[QAccessible::StatusBar] = NSAccessibilityStaticTextRole; @@ -204,6 +205,8 @@ NSString *macSubrole(QAccessibleInterface *interface) return NSAccessibilitySecureTextFieldSubrole; if (interface->role() == QAccessible::PageTab) return NSAccessibilityTabButtonSubrole; + if (interface->role() == QAccessible::Switch) + return NSAccessibilitySwitchSubrole; return nil; } @@ -328,8 +331,11 @@ NSString *getTranslatedAction(const QString &qtAction) QString translateAction(NSString *nsAction, QAccessibleInterface *interface) { if ([nsAction compare: NSAccessibilityPressAction] == NSOrderedSame) { - if (interface->role() == QAccessible::CheckBox || interface->role() == QAccessible::RadioButton) + if (interface->role() == QAccessible::CheckBox + || interface->role() == QAccessible::RadioButton + || interface->role() == QAccessible::Switch) { return QAccessibleActionInterface::toggleAction(); + } return QAccessibleActionInterface::pressAction(); } else if ([nsAction compare: NSAccessibilityIncrementAction] == NSOrderedSame) return QAccessibleActionInterface::increaseAction(); 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/qiosplatformaccessibility.h b/src/plugins/platforms/ios/qiosplatformaccessibility.h index 04cddf00f4e..1ccc5bd089a 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.h +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.h @@ -23,7 +23,7 @@ public: private: QMacNotificationObserver m_focusObserver; - QMacAccessibilityElement *m_focusElement; + QT_MANGLE_NAMESPACE(QMacAccessibilityElement) *m_focusElement; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.mm b/src/plugins/platforms/ios/qiosplatformaccessibility.mm index 26f48468c45..a63c75757e4 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.mm +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.mm @@ -12,6 +12,8 @@ #include "qioswindow.h" #include "quiaccessibilityelement.h" +QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + QIOSPlatformAccessibility::QIOSPlatformAccessibility() { m_focusObserver = QMacNotificationObserver( 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/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 23a4c506c24..5024013ba77 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -10,9 +10,6 @@ #import <UIKit/UIKit.h> -class QIOSContext; -class QIOSWindow; - @class QUIView; QT_BEGIN_NAMESPACE @@ -84,7 +81,7 @@ private: QDebug operator<<(QDebug debug, const QIOSWindow *window); #endif -QT_MANGLE_NAMESPACE(QUIView) *quiview_cast(UIView *view); +QUIView *quiview_cast(UIView *view); QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index d10fe40840a..0d6952bddf0 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -115,6 +115,9 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); || iface->role() == QAccessible::RadioButton) return @""; + if (iface->role() == QAccessible::Switch) + return state.checked ? @"1" : @"0"; + return state.checked ? QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_CHECKED).toNSString() : QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_UNCHECKED).toNSString(); @@ -169,6 +172,8 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); || accessibleRole == QAccessible::RadioButton) { if (state.checked) traits |= UIAccessibilityTraitSelected; + } else if (accessibleRole == QAccessible::Switch) { + traits |= UIAccessibilityTraitToggleButton; } else if (accessibleRole == QAccessible::EditableText) { static auto defaultTextFieldTraits = []{ auto *textField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease]; 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 1cf81453fe7..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,18 @@ 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: { + element = document.call<emscripten::val>("createElement", std::string("button")); + setAttribute(element, "type", "button"); + setAttribute(element, "role", "switch"); + if (iface->state().checked) + setAttribute(element, "aria-checked", "true"); + else + setAttribute(element, "aria-checked", "false"); + addEventListener(iface, element, "change"); } break; case QAccessible::RadioButton: { @@ -368,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: @@ -402,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: { @@ -411,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: { @@ -425,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: { @@ -433,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: { @@ -460,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; }(); @@ -531,6 +543,7 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface) { emscripten::val element = getHtmlElement(iface); emscripten::val container = getElementContainer(iface); + if (container.isUndefined() || element.isUndefined()) return; @@ -543,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) { @@ -701,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()); } } @@ -743,6 +760,28 @@ void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event) break; } } + +void QWasmAccessibility::handleSwitchUpdate(QAccessibleEvent *event) +{ + switch (event->type()) { + case QAccessible::Focus: + case QAccessible::NameChanged: { + setHtmlElementTextName(event->accessibleInterface()); + } break; + case QAccessible::StateChanged: { + QAccessibleInterface *accessible = event->accessibleInterface(); + const emscripten::val element = getHtmlElement(accessible); + if (accessible->state().checked) + setAttribute(element, "aria-checked", "true"); + else + setAttribute(element, "aria-checked", "false"); + } break; + default: + qCDebug(lcQpaAccessibility) << "TODO: implement handleSwitchUpdate for event" << event->type(); + break; + } +} + void QWasmAccessibility::handleToolUpdate(QAccessibleEvent *event) { QAccessibleInterface *iface = event->accessibleInterface(); @@ -1152,6 +1191,9 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::CheckBox: handleCheckBoxUpdate(event); break; + case QAccessible::Switch: + handleSwitchUpdate(event); + break; case QAccessible::EditableText: handleLineEditUpdate(event); break; diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.h b/src/plugins/platforms/wasm/qwasmaccessibility.h index ee2be18e5b7..26f3e0e9afe 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.h +++ b/src/plugins/platforms/wasm/qwasmaccessibility.h @@ -81,6 +81,7 @@ private: void handleStaticTextUpdate(QAccessibleEvent *event); void handleButtonUpdate(QAccessibleEvent *event); void handleCheckBoxUpdate(QAccessibleEvent *event); + void handleSwitchUpdate(QAccessibleEvent *event); void handleDialogUpdate(QAccessibleEvent *event); void handleMenuUpdate(QAccessibleEvent *event); void handleToolUpdate(QAccessibleEvent *event); @@ -115,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/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index a1a173f0182..17422bd606d 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -201,7 +201,7 @@ void QWaylandXdgSurface::Toplevel::requestWindowFlags(Qt::WindowFlags flags) delete m_decoration; m_decoration = nullptr; } else { - m_decoration->unsetMode(); + m_decoration->requestMode(QWaylandXdgToplevelDecorationV1::mode_server_side); } } } @@ -697,108 +697,135 @@ void QWaylandXdgSurface::setWindowPosition(const QPoint &position) window()->updateExposure(); } +static QtWayland::xdg_positioner::gravity gravityFromEdge(Qt::Edges edges) +{ + switch (edges) { + case Qt::Edges(): + return QtWayland::xdg_positioner::gravity_none; + case Qt::TopEdge: + return QtWayland::xdg_positioner::gravity_top; + case Qt::TopEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::gravity_top_right; + case Qt::RightEdge: + return QtWayland::xdg_positioner::gravity_right; + case Qt::BottomEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::gravity_bottom_right; + case Qt::BottomEdge: + return QtWayland::xdg_positioner::gravity_bottom; + case Qt::BottomEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::gravity_bottom_left; + case Qt::LeftEdge: + return QtWayland::xdg_positioner::gravity_left; + case Qt::TopEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::gravity_top_left; + } + qCWarning(lcQpaWayland) << "Cannot map positioner gravity " << edges; + return QtWayland::xdg_positioner::gravity_none; +} + +static QtWayland::xdg_positioner::anchor anchorFromEdge(Qt::Edges edges) +{ + switch (edges) { + case Qt::Edges(): + return QtWayland::xdg_positioner::anchor_none; + case Qt::TopEdge: + return QtWayland::xdg_positioner::anchor_top; + case Qt::TopEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::anchor_top_right; + case Qt::RightEdge: + return QtWayland::xdg_positioner::anchor_right; + case Qt::BottomEdge | Qt::RightEdge: + return QtWayland::xdg_positioner::anchor_bottom_right; + case Qt::BottomEdge: + return QtWayland::xdg_positioner::anchor_bottom; + case Qt::BottomEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::anchor_bottom_left; + case Qt::LeftEdge: + return QtWayland::xdg_positioner::anchor_left; + case Qt::TopEdge | Qt::LeftEdge: + return QtWayland::xdg_positioner::anchor_top_left; + } + qCWarning(lcQpaWayland) << "Cannot map positioner anchor" << edges; + return QtWayland::xdg_positioner::anchor_none; +} + std::unique_ptr<QWaylandXdgSurface::Positioner> QWaylandXdgSurface::createPositioner(QWaylandWindow *parent) { std::unique_ptr<Positioner> positioner(new Positioner(m_shell)); - // set_popup expects a position relative to the parent - QRect windowGeometry = m_window->windowContentGeometry(); - QMargins windowMargins = m_window->windowContentMargins() - m_window->clientSideMargins(); - QMargins parentMargins = parent->windowContentMargins() - parent->clientSideMargins(); - // These property overrides may be removed when public API becomes available - QRect placementAnchor = m_window->window()->property("_q_waylandPopupAnchorRect").toRect(); - if (!placementAnchor.isValid()) { - placementAnchor = QRect(m_window->geometry().topLeft() - parent->geometry().topLeft(), QSize(1,1)); - } - placementAnchor.translate(windowMargins.left(), windowMargins.top()); - placementAnchor.translate(-parentMargins.left(), -parentMargins.top()); + // Default case, map the guessed global position to a relative position + QRect placementAnchor = QRect(m_window->geometry().topLeft() - parent->geometry().topLeft(), QSize(1,1)); + Qt::Edges anchor = Qt::TopEdge | Qt::RightEdge; + Qt::Edges gravity = Qt::BottomEdge | Qt::RightEdge; + uint32_t constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_slide_x | QtWayland::xdg_positioner::constraint_adjustment_slide_y; - uint32_t anchor = QtWayland::xdg_positioner::anchor_top_left; + // Override from window type + if (m_window->parentControlGeometry().isValid()) + placementAnchor = m_window->parentControlGeometry(); + + switch (m_window->extendedWindowType()) { + case QNativeInterface::Private::QWaylandWindow::Menu: + case QNativeInterface::Private::QWaylandWindow::WindowType::ComboBox: + anchor = Qt::BottomEdge | Qt::LeftEdge; + gravity = Qt::BottomEdge | Qt::RightEdge; + constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_slide_x | + QtWayland::xdg_positioner::constraint_adjustment_flip_y | QtWayland::xdg_positioner::constraint_adjustment_slide_y; + break; + case QNativeInterface::Private::QWaylandWindow::SubMenu: + anchor = Qt::TopEdge | Qt::RightEdge; + gravity = Qt::BottomEdge | Qt::RightEdge; + constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_flip_x | + QtWayland::xdg_positioner::constraint_adjustment_slide_y; + break; + case QNativeInterface::Private::QWaylandWindow::ToolTip: + anchor = Qt::BottomEdge | Qt::RightEdge; + gravity = Qt::BottomEdge | Qt::RightEdge; + constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_flip_x | QtWayland::xdg_positioner::constraint_adjustment_slide_x | + QtWayland::xdg_positioner::constraint_adjustment_flip_y | QtWayland::xdg_positioner::constraint_adjustment_slide_y; + break; + default: + break; + } + + if (qApp->layoutDirection() == Qt::RightToLeft) { + if (anchor & (Qt::RightEdge | Qt::LeftEdge)) + anchor ^= (Qt::RightEdge | Qt::LeftEdge); + if (gravity & (Qt::RightEdge | Qt::LeftEdge)) + gravity ^= (Qt::RightEdge | Qt::LeftEdge); + } + + // Override with properties fauxAPI + const QVariant placementAnchorVariant = m_window->window()->property("_q_waylandPopupAnchorRect"); + if (placementAnchorVariant.isValid()) + placementAnchor = placementAnchorVariant.toRect(); const QVariant anchorVariant = m_window->window()->property("_q_waylandPopupAnchor"); - if (anchorVariant.isValid()) { - switch (anchorVariant.value<Qt::Edges>()) { - case Qt::Edges(): - anchor = QtWayland::xdg_positioner::anchor_none; - break; - case Qt::TopEdge: - anchor = QtWayland::xdg_positioner::anchor_top; - break; - case Qt::TopEdge | Qt::RightEdge: - anchor = QtWayland::xdg_positioner::anchor_top_right; - break; - case Qt::RightEdge: - anchor = QtWayland::xdg_positioner::anchor_right; - break; - case Qt::BottomEdge | Qt::RightEdge: - anchor = QtWayland::xdg_positioner::anchor_bottom_right; - break; - case Qt::BottomEdge: - anchor = QtWayland::xdg_positioner::anchor_bottom; - break; - case Qt::BottomEdge | Qt::LeftEdge: - anchor = QtWayland::xdg_positioner::anchor_bottom_left; - break; - case Qt::LeftEdge: - anchor = QtWayland::xdg_positioner::anchor_left; - break; - case Qt::TopEdge | Qt::LeftEdge: - anchor = QtWayland::xdg_positioner::anchor_top_left; - break; - } - } - - uint32_t gravity = QtWayland::xdg_positioner::gravity_bottom_right; + if (anchorVariant.isValid()) + anchor = anchorVariant.value<Qt::Edges>(); const QVariant popupGravityVariant = m_window->window()->property("_q_waylandPopupGravity"); - if (popupGravityVariant.isValid()) { - switch (popupGravityVariant.value<Qt::Edges>()) { - case Qt::Edges(): - gravity = QtWayland::xdg_positioner::gravity_none; - break; - case Qt::TopEdge: - gravity = QtWayland::xdg_positioner::gravity_top; - break; - case Qt::TopEdge | Qt::RightEdge: - gravity = QtWayland::xdg_positioner::gravity_top_right; - break; - case Qt::RightEdge: - gravity = QtWayland::xdg_positioner::gravity_right; - break; - case Qt::BottomEdge | Qt::RightEdge: - gravity = QtWayland::xdg_positioner::gravity_bottom_right; - break; - case Qt::BottomEdge: - gravity = QtWayland::xdg_positioner::gravity_bottom; - break; - case Qt::BottomEdge | Qt::LeftEdge: - gravity = QtWayland::xdg_positioner::gravity_bottom_left; - break; - case Qt::LeftEdge: - gravity = QtWayland::xdg_positioner::gravity_left; - break; - case Qt::TopEdge | Qt::LeftEdge: - gravity = QtWayland::xdg_positioner::gravity_top_left; - break; - } - } - - uint32_t constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_slide_x | QtWayland::xdg_positioner::constraint_adjustment_slide_y; + if (popupGravityVariant.isValid()) + gravity = popupGravityVariant.value<Qt::Edges>(); const QVariant constraintAdjustmentVariant = m_window->window()->property("_q_waylandPopupConstraintAdjustment"); - if (constraintAdjustmentVariant.isValid()) { + if (constraintAdjustmentVariant.isValid()) constraintAdjustment = constraintAdjustmentVariant.toUInt(); - } + + // set_popup expects a position relative to the parent + QRect windowGeometry = m_window->windowContentGeometry(); + QMargins windowMargins = m_window->windowContentMargins() - m_window->clientSideMargins(); + QMargins parentMargins = parent->windowContentMargins() - parent->clientSideMargins(); + placementAnchor.translate(windowMargins.left(), windowMargins.top()); + placementAnchor.translate(-parentMargins.left(), -parentMargins.top()); positioner->set_anchor_rect(placementAnchor.x(), placementAnchor.y(), placementAnchor.width(), placementAnchor.height()); - positioner->set_anchor(anchor); - positioner->set_gravity(gravity); + positioner->set_anchor(anchorFromEdge(anchor)); + positioner->set_gravity(gravityFromEdge(gravity)); positioner->set_size(windowGeometry.width(), windowGeometry.height()); positioner->set_constraint_adjustment(constraintAdjustment); return positioner; } - void QWaylandXdgSurface::setIcon(const QIcon &icon) { if (!m_shell->m_topLevelIconManager || !m_toplevel) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 7c300843518..be527b08f4d 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -481,8 +481,9 @@ void QWaylandWindow::setGeometry(const QRect &r) if (mShellSurface && !mInResizeFromApplyConfigure) { const QRect frameGeometry = r.marginsAdded(clientSideMargins()).marginsRemoved(windowContentMargins()); - if (qt_window_private(window())->positionAutomatic) + if (qt_window_private(window())->positionAutomatic || m_popupInfo.parentControlGeometry.isValid()) mShellSurface->setWindowSize(frameGeometry.size()); + else mShellSurface->setWindowGeometry(frameGeometry); } @@ -1945,6 +1946,27 @@ QString QWaylandWindow::sessionRestoreId() const return mSessionRestoreId; } +void QWaylandWindow::setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::WindowType windowType) { + m_popupInfo.extendedWindowType = windowType; +} + +QNativeInterface::Private::QWaylandWindow::WindowType QWaylandWindow::extendedWindowType() const +{ + return m_popupInfo.extendedWindowType; +} + +void QWaylandWindow::setParentControlGeometry(const QRect &parentControlGeometry) { + m_popupInfo.parentControlGeometry = parentControlGeometry; + if (mExposed) { + mShellSurface->setWindowPosition(window()->position()); + } +} + +QRect QWaylandWindow::parentControlGeometry() const +{ + return m_popupInfo.parentControlGeometry; +} + } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 854724daf82..d6b24d0569f 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -255,6 +255,11 @@ public: void setSessionRestoreId(const QString &role) override; QString sessionRestoreId() const; + void setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::WindowType) override; + QNativeInterface::Private::QWaylandWindow::WindowType extendedWindowType() const; + void setParentControlGeometry(const QRect &parentAnchor) override; + QRect parentControlGeometry() const; + public Q_SLOTS: void applyConfigure(); @@ -397,6 +402,11 @@ private: void handleFrameCallback(struct ::wl_callback* callback); const QPlatformWindow *lastParent = nullptr; + struct { + QRect parentControlGeometry; + QNativeInterface::Private::QWaylandWindow::WindowType extendedWindowType = QNativeInterface::Private::QWaylandWindow::Default; + } m_popupInfo; + static QWaylandWindow *mMouseGrab; static QWaylandWindow *mTopPopup; 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/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index fc0e053f396..0144786ce5e 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -82,8 +82,9 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve { if (QAccessibleInterface *accessible = event->accessibleInterface()) { if (event->changedStates().checked || event->changedStates().checkStateMixed) { - // Notifies states changes in checkboxes. - if (accessible->role() == QAccessible::CheckBox) { + // Notifies states changes in checkboxes and switches. + if (accessible->role() == QAccessible::CheckBox + || accessible->role() == QAccessible::Switch) { if (auto provider = providerForAccessible(accessible)) { long toggleState = ToggleState_Off; if (accessible->state().checked) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp index 4146a56b226..b2675d5b884 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp @@ -132,6 +132,7 @@ long roleToControlTypeId(QAccessible::Role role) {QAccessible::EditableText, UIA_EditControlTypeId}, {QAccessible::Button, UIA_ButtonControlTypeId}, {QAccessible::CheckBox, UIA_CheckBoxControlTypeId}, + {QAccessible::Switch, UIA_ButtonControlTypeId}, {QAccessible::RadioButton, UIA_RadioButtonControlTypeId}, {QAccessible::ComboBox, UIA_ComboBoxControlTypeId}, {QAccessible::ProgressBar, UIA_ProgressBarControlTypeId}, 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..25142612c4f 100644 --- a/src/plugins/styles/modernwindows/qwindows11style.cpp +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -1331,11 +1331,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 +1348,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 +1411,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 +1915,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); 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/testlib/qtest.h b/src/testlib/qtest.h index c749cbd492f..431f91d5474 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -181,29 +181,17 @@ inline bool qCompare(quint32 const &t1, quint64 const &t2, const char *actual, } namespace Internal { -template <typename T> -class HasInitMain // SFINAE test for the presence of initMain() -{ -private: - using YesType = char[1]; - using NoType = char[2]; - - template <typename C> static YesType& test( decltype(&C::initMain) ) ; - template <typename C> static NoType& test(...); - -public: - enum { value = sizeof(test<T>(nullptr)) == sizeof(YesType) }; -}; +template <typename T, typename = void> +struct HasInitMain : std::false_type{}; -template<typename T> -typename std::enable_if<HasInitMain<T>::value, void>::type callInitMain() -{ - T::initMain(); -} +template <typename T> +struct HasInitMain<T, std::void_t<decltype(&T::initMain)>> : std::true_type {}; template<typename T> -typename std::enable_if<!HasInitMain<T>::value, void>::type callInitMain() +void callInitMain() { + if constexpr (HasInitMain<T>::value) + T::initMain(); } } // namespace Internal diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 6c7e71294ed..784e69d2486 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -393,6 +393,7 @@ static QString mainSourcePath; static bool inTestFunction = false; #if defined(Q_OS_MACOS) +static std::optional<QTestPrivate::AppNapDisabler> appNapDisabler; static IOPMAssertionID macPowerSavingDisabled = 0; #endif @@ -1881,13 +1882,12 @@ void QTest::qInit(QObject *testObject, int argc, char **argv) QTestPrivate::disableWindowRestore(); // Disable App Nap which may cause tests to stall - QTestPrivate::AppNapDisabler appNapDisabler; + if (!appNapDisabler) + appNapDisabler.emplace(); - if (qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0)) { - IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, - kIOPMAssertionLevelOn, CFSTR("QtTest running tests"), - &macPowerSavingDisabled); - } + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, CFSTR("QtTest running tests"), + &macPowerSavingDisabled); #endif QTestPrivate::parseBlackList(); @@ -2041,6 +2041,7 @@ void QTest::qCleanup() #if defined(Q_OS_MACOS) IOPMAssertionRelease(macPowerSavingDisabled); + appNapDisabler = std::nullopt; #endif } diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index 0e04d10e692..b517d85c5fb 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -328,6 +328,53 @@ static bool processAndroidManifest() return true; } +static QStringList queryDangerousPermissions() +{ + QByteArray output; + const QStringList args({ "shell"_L1, "dumpsys"_L1, "package"_L1, "permissions"_L1 }); + if (!execAdbCommand(args, &output, false)) { + qWarning("Failed to query permissions via dumpsys"); + return {}; + } + + /* + * Permissions section from this command look like: + * + * Permission [android.permission.INTERNET] (c8cafdc): + * sourcePackage=android + * uid=1000 gids=[3003] type=0 prot=normal|instant + * perm=PermissionInfo{5f5bfbb android.permission.INTERNET} + * flags=0x0 + */ + const static QRegularExpression regex("^\\s*Permission\\s+\\[([^\\]]+)\\]\\s+\\(([^)]+)\\):"_L1); + QStringList dangerousPermissions; + QString currentPerm; + + const QStringList lines = QString::fromUtf8(output).split(u'\n'); + for (const QString &line : lines) { + QRegularExpressionMatch match = regex.match(line); + if (match.hasMatch()) { + currentPerm = match.captured(1); + continue; + } + + if (currentPerm.isEmpty()) + continue; + + int protIndex = line.indexOf("prot="_L1); + if (protIndex == -1) + continue; + + QString protectionTypes = line.mid(protIndex + 5).trimmed(); + if (protectionTypes.contains("dangerous"_L1, Qt::CaseInsensitive)) { + dangerousPermissions.append(currentPerm); + currentPerm.clear(); + } + } + + return dangerousPermissions; +} + static void setOutputFile(QString file, QString format) { if (format.isEmpty()) @@ -938,7 +985,11 @@ int main(int argc, char *argv[]) return EXIT_ERROR; } + const QStringList dangerousPermisisons = queryDangerousPermissions(); for (const auto &permission : g_options.permissions) { + if (!dangerousPermisisons.contains(permission)) + continue; + if (!execAdbCommand({ "shell"_L1, "pm"_L1, "grant"_L1, g_options.package, permission }, nullptr)) { qWarning("Unable to grant '%s' to '%s'. Probably the Android version mismatch.", diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 9cfd877d945..64af8c10fc1 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -68,6 +68,8 @@ bool Moc::parseClassHead(ClassDef *def) const QByteArrayView lex = lexemView(); if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") name = lexem(); + else + def->isFinal = true; } def->qualified += name; @@ -85,6 +87,8 @@ bool Moc::parseClassHead(ClassDef *def) const QByteArrayView lex = lexemView(); if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") return false; + else + def->isFinal = true; } if (test(COLON)) { @@ -2055,6 +2059,8 @@ QJsonObject ClassDef::toJson() const cls["className"_L1] = QString::fromUtf8(classname.constData()); cls["qualifiedClassName"_L1] = QString::fromUtf8(qualified.constData()); cls["lineNumber"_L1] = lineNumber; + if (isFinal) + cls["final"_L1] = true; QJsonArray classInfos; for (const auto &info: std::as_const(classInfoList)) { diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index bf55aa7c44f..aafa80d2164 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -207,6 +207,7 @@ struct ClassDef : BaseDef { bool hasQGadget = false; bool hasQNamespace = false; bool requireCompleteMethodTypes = false; + bool isFinal = false; QJsonObject toJson() const; }; diff --git a/src/widgets/doc/src/external-resources.qdoc b/src/widgets/doc/src/external-resources.qdoc index 17459b6a5bc..96117546a29 100644 --- a/src/widgets/doc/src/external-resources.qdoc +++ b/src/widgets/doc/src/external-resources.qdoc @@ -8,7 +8,7 @@ */ /*! - \externalpage http://www.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm + \externalpage https://rk.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm \title Sinclair Spectrum */ /*! diff --git a/src/widgets/doc/src/qtwidgets-examples.qdoc b/src/widgets/doc/src/qtwidgets-examples.qdoc index 45677c471ba..364c985b310 100644 --- a/src/widgets/doc/src/qtwidgets-examples.qdoc +++ b/src/widgets/doc/src/qtwidgets-examples.qdoc @@ -164,3 +164,15 @@ regular expressions for the Widget-based applications. */ +/*! + \group examples-user-input + \ingroup all-examples + \title User Input Examples + \brief Using user input in Qt Widgets applications. + + \image imagegestures-example.png {Application handling touch gestures} + + Qt provides the functionality for handling user input and drag-and-drop in + widget-based applications. + +*/ diff --git a/src/widgets/doc/src/qtwidgets-toc.qdoc b/src/widgets/doc/src/qtwidgets-toc.qdoc index bc447b8bd58..beddf853a22 100644 --- a/src/widgets/doc/src/qtwidgets-toc.qdoc +++ b/src/widgets/doc/src/qtwidgets-toc.qdoc @@ -53,6 +53,7 @@ \li \l{Rich Text Examples} \li \l{Graphics View Examples} \li \l{Widget Tools Examples} + \li \l{User Input Examples} \endlist \endlist diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index e4159ad2cf0..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. @@ -3298,7 +3323,8 @@ void QAbstractItemView::closePersistentEditor(const QModelIndex &index) bool QAbstractItemView::isPersistentEditorOpen(const QModelIndex &index) const { Q_D(const QAbstractItemView); - return d->editorForIndex(index).widget; + QWidget *editor = d->editorForIndex(index).widget; + return editor && d->persistent.contains(editor); } /*! diff --git a/src/widgets/itemviews/qabstractitemview.h b/src/widgets/itemviews/qabstractitemview.h index 63adac8d6f2..973a9b044cb 100644 --- a/src/widgets/itemviews/qabstractitemview.h +++ b/src/widgets/itemviews/qabstractitemview.h @@ -48,6 +48,8 @@ class Q_WIDGETS_EXPORT QAbstractItemView : public QAbstractScrollArea Q_PROPERTY(ScrollMode horizontalScrollMode READ horizontalScrollMode WRITE setHorizontalScrollMode RESET resetHorizontalScrollMode) Q_PROPERTY(int updateThreshold READ updateThreshold WRITE setUpdateThreshold) + Q_PROPERTY(Qt::MatchFlags keyboardSearchFlags READ keyboardSearchFlags + WRITE setKeyboardSearchFlags) public: enum SelectionMode { @@ -182,6 +184,9 @@ public: int updateThreshold() const; void setUpdateThreshold(int threshold); + Qt::MatchFlags keyboardSearchFlags() const; + void setKeyboardSearchFlags(Qt::MatchFlags searchFlags); + void openPersistentEditor(const QModelIndex &index); void closePersistentEditor(const QModelIndex &index); bool isPersistentEditorOpen(const QModelIndex &index) const; diff --git a/src/widgets/itemviews/qabstractitemview_p.h b/src/widgets/itemviews/qabstractitemview_p.h index b24b2d21c33..60799fb8a50 100644 --- a/src/widgets/itemviews/qabstractitemview_p.h +++ b/src/widgets/itemviews/qabstractitemview_p.h @@ -383,6 +383,7 @@ public: QString keyboardInput; QElapsedTimer keyboardInputTime; + Qt::MatchFlags keyboardSearchFlags = Qt::MatchStartsWith | Qt::MatchWrap; bool autoScroll; QBasicTimer autoScrollTimer; diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index da1fbbd60df..84ff04c9f34 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -1030,7 +1030,8 @@ void QTreeView::keyboardSearch(const QString &search) searchFrom = searchFrom.sibling(searchFrom.row(), start.column()); if (searchFrom.parent() == start.parent()) searchFrom = start; - QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString); + QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString, 1, + keyboardSearchFlags()); if (match.size()) { int hitIndex = d->viewIndex(match.at(0)); if (hitIndex >= 0 && hitIndex < startIndex) diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 6fcfcf1b1ef..fa95a1d2538 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1432,8 +1432,8 @@ void QApplicationPrivate::notifyWindowIconChanged() // in case there are any plain QWindows in this QApplication-using // application, also send the notification to them - for (int i = 0; i < windowList.size(); ++i) - QCoreApplication::sendEvent(windowList.at(i), &ev); + for (QWindow *w : std::as_const(windowList)) + QCoreApplication::sendEvent(w, &ev); } /*! @@ -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; @@ -1774,9 +1778,9 @@ void QApplicationPrivate::notifyLayoutDirectionChange() // in case there are any plain QWindows in this QApplication-using // application, also send the notification to them - for (int i = 0; i < windowList.size(); ++i) { + for (QWindow *w: std::as_const(windowList)) { QEvent ev(QEvent::ApplicationLayoutDirectionChange); - QCoreApplication::sendEvent(windowList.at(i), &ev); + QCoreApplication::sendEvent(w, &ev); } } @@ -1863,14 +1867,12 @@ void QApplicationPrivate::setActiveWindow(QWidget* act) QEvent windowActivate(QEvent::WindowActivate); QEvent windowDeactivate(QEvent::WindowDeactivate); - for (int i = 0; i < toBeActivated.size(); ++i) { - QWidget *w = toBeActivated.at(i); + for (QWidget *w : std::as_const(toBeActivated)) { QApplication::sendSpontaneousEvent(w, &windowActivate); QApplication::sendSpontaneousEvent(w, &activationChange); } - for(int i = 0; i < toBeDeactivated.size(); ++i) { - QWidget *w = toBeDeactivated.at(i); + for (QWidget *w : std::as_const(toBeDeactivated)) { QApplication::sendSpontaneousEvent(w, &windowDeactivate); QApplication::sendSpontaneousEvent(w, &activationChange); } @@ -2082,8 +2084,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con } QEvent leaveEvent(QEvent::Leave); - for (int i = 0; i < leaveList.size(); ++i) { - auto *w = leaveList.at(i); + for (QWidget *w : std::as_const(leaveList)) { if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, nullptr)) { QCoreApplication::sendEvent(w, &leaveEvent); if (w->testAttribute(Qt::WA_Hover) && @@ -2125,8 +2126,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con // Whenever we leave an alien widget on X11/QPA, we need to reset its nativeParentWidget()'s cursor. // This is not required on Windows as the cursor is reset on every single mouse move. QWidget *parentOfLeavingCursor = nullptr; - for (int i = 0; i < leaveList.size(); ++i) { - auto *w = leaveList.at(i); + for (QWidget *w : std::as_const(leaveList)) { if (!isAlien(w)) break; if (w->testAttribute(Qt::WA_SetCursor)) { @@ -3085,7 +3085,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) const QPoint offset = w->pos(); w = w->parentWidget(); QMutableTouchEvent::setTarget(touchEvent, w); - for (int i = 0; i < touchEvent->pointCount(); ++i) { + for (qsizetype cnt = touchEvent->pointCount(), i = 0; i < cnt; ++i) { auto &pt = touchEvent->point(i); QMutableEventPoint::setPosition(pt, pt.position() + offset); } @@ -3164,8 +3164,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) res = d->notify_helper(w, &ge); gestureEvent->m_spont = false; eventAccepted = ge.isAccepted(); - for (int i = 0; i < gestures.size(); ++i) { - QGesture *g = gestures.at(i); + for (QGesture *g : std::as_const(gestures)) { // Ignore res [event return value] because handling of multiple gestures // packed into a single QEvent depends on not consuming the event if (eventAccepted || ge.isAccepted(g)) { @@ -3708,7 +3707,7 @@ bool QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEven { bool containsPress = false; - for (int i = 0; i < touchEvent->pointCount(); ++i) { + for (qsizetype cnt = touchEvent->pointCount(), i = 0; i < cnt; ++i) { auto &pt = touchEvent->point(i); QMutableEventPoint::setPosition(pt, widget->mapFromGlobal(pt.globalPosition())); @@ -3768,7 +3767,7 @@ void QApplicationPrivate::activateImplicitTouchGrab(QWidget *widget, QTouchEvent // If the widget dispatched the event further (see QGraphicsProxyWidget), then // there might already be an implicit grabber. Don't override that. A widget that // has partially recognized a gesture needs to grab all points. - for (int i = 0; i < touchEvent->pointCount(); ++i) { + for (qsizetype cnt = touchEvent->pointCount(), i = 0; i < cnt; ++i) { auto &ep = touchEvent->point(i); if (!QMutableEventPoint::target(ep) && (ep.isAccepted() || grabMode == GrabAllPoints)) QMutableEventPoint::setTarget(ep, widget); diff --git a/src/widgets/kernel/qtooltip.cpp b/src/widgets/kernel/qtooltip.cpp index 9e6aaf4b95f..d989feb7f91 100644 --- a/src/widgets/kernel/qtooltip.cpp +++ b/src/widgets/kernel/qtooltip.cpp @@ -20,6 +20,8 @@ #if QT_CONFIG(style_stylesheet) #include <private/qstylesheetstyle_p.h> #endif +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #include <qlabel.h> #include <QtWidgets/private/qlabel_p.h> @@ -386,6 +388,17 @@ void QTipLabel::placeTip(const QPoint &pos, QWidget *w) p += offset; +#if QT_CONFIG(wayland) + create(); + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(windowHandle()->handle())) { + // based on the existing code below, by default position at 'p' stored at the bottom right of our rect + // then flip to the other arbitrary 4x24 space if constrained + const QRect controlGeometry(QRect(p.x() - 4, p.y() - 24, 4, 24)); + waylandWindow->setParentControlGeometry(controlGeometry); + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ToolTip); + } +#endif + QRect screenRect = screen->geometry(); if (p.x() + this->width() > screenRect.x() + screenRect.width()) p.rx() -= 4 + this->width(); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 36446c3e5c4..9499c88af12 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -13144,11 +13144,22 @@ int QWidget::metric(PaintDeviceMetric m) const void QWidget::initPainter(QPainter *painter) const { const QPalette &pal = palette(); - painter->d_func()->state->pen = QPen(pal.brush(foregroundRole()), 1); - painter->d_func()->state->bgBrush = pal.brush(backgroundRole()); + QPainterPrivate *painterPrivate = QPainterPrivate::get(painter); + + painterPrivate->state->pen = QPen(pal.brush(foregroundRole()), 1); + painterPrivate->state->bgBrush = pal.brush(backgroundRole()); QFont f(font(), this); - painter->d_func()->state->deviceFont = f; - painter->d_func()->state->font = f; + painterPrivate->state->deviceFont = f; + painterPrivate->state->font = f; + + painterPrivate->setEngineDirtyFlags({ + QPaintEngine::DirtyPen, + QPaintEngine::DirtyBrush, + QPaintEngine::DirtyFont, + }); + + if (painterPrivate->extended) + painterPrivate->extended->penChanged(); } /*! diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 737ccb0e807..622f1548756 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -591,6 +591,10 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) } } + // Event delivery above might have destroyed this object. See QTBUG-138419. + if (self.isNull()) + return; + if (QApplication::activePopupWidget() != activePopupWidget && QApplicationPrivate::replayMousePress && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) { diff --git a/src/widgets/util/qcompleter.cpp b/src/widgets/util/qcompleter.cpp index 220f600ea41..735b574d293 100644 --- a/src/widgets/util/qcompleter.cpp +++ b/src/widgets/util/qcompleter.cpp @@ -122,6 +122,8 @@ #include "QtGui/qevent.h" #include <private/qapplication_p.h> #include <private/qwidget_p.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #if QT_CONFIG(lineedit) #include "QtWidgets/qlineedit.h" #endif @@ -925,10 +927,15 @@ void QCompleterPrivate::showPopup(const QRect& rect) popup->setGeometry(pos.x(), pos.y(), w, h); if (!popup->isVisible()) { - // Make sure popup has a transient parent set, Wayland needs it. QTBUG-130474 - popup->winId(); // force creation of windowHandle - popup->windowHandle()->setTransientParent(widget->window()->windowHandle()); - +#if QT_CONFIG(wayland) + popup->createWinId(); + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(popup->windowHandle()->handle())) { + popup->windowHandle()->setTransientParent(widget->window()->windowHandle()); + const QRect controlGeometry = QRect(widget->mapTo(widget->topLevelWidget(), QPoint(0,0)), widget->size()); + waylandWindow->setParentControlGeometry(controlGeometry); + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ComboBox); + } +#endif popup->show(); } } diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 6f25b8bde67..2f51b83a49d 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -7,6 +7,9 @@ #include <qstylepainter.h> #include <qpa/qplatformtheme.h> #include <qpa/qplatformmenu.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> + #include <qlineedit.h> #include <qapplication.h> #include <qlistview.h> @@ -2868,6 +2871,17 @@ void QComboBox::showPopup() container->hide(); } } + +#if QT_CONFIG(wayland) + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(container->windowHandle()->handle())) { + const QRect popup(style->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxListBoxPopup, this)); + const QRect controlGeometry = QRect(mapTo(window(), popup.topLeft()), popup.size()); + waylandWindow->setParentControlGeometry(controlGeometry); + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ComboBox); + } +#endif + container->show(); if (!neededHorizontalScrollBar && needHorizontalScrollBar()) { listRect.adjust(0, 0, 0, sb->height()); diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index 055d8f3d11e..1708a53f163 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -572,6 +572,8 @@ bool QDockWidgetGroupWindow::hasNativeDecos() const #endif } +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Waggressive-loop-optimizations") /* The given widget is hovered over this floating group. This function will save the state and create a gap in the actual state. @@ -623,6 +625,7 @@ bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem, const QPoint &mouseP layoutInfo()->apply(opts & QMainWindow::AnimatedDocks); return true; } +QT_WARNING_POP void QDockWidgetGroupWindow::updateCurrentGapRect() { diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 5cda8f33f4c..69548c4e17e 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -40,6 +40,8 @@ #include <private/qaction_p.h> #include <private/qguiapplication_p.h> #include <qpa/qplatformtheme.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #include <private/qstyle_p.h> QT_BEGIN_NAMESPACE @@ -769,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(); } } } @@ -2170,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); } @@ -2535,6 +2538,23 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po } popupScreen = QGuiApplication::screenAt(pos); q->setGeometry(QRect(pos, size)); + +#if QT_CONFIG(wayland) + q->create(); + if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) { + if (causedButton) { + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu); + waylandWindow->setParentControlGeometry(causedButton->geometry()); + } else if (caused) { + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu); + waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction)); + } else if (auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) { + waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu); + waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action)); + } + } +#endif + #if QT_CONFIG(effects) int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll; int vGuess = QEffects::DownScroll; @@ -2952,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/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp index 3804f53cae9..0b65673c393 100644 --- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp +++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp @@ -92,6 +92,7 @@ private slots: void qIsNull(); void for_each(); void qassert(); + void qpresume(); void qtry(); void checkptr(); void qstaticassert(); @@ -266,6 +267,18 @@ void tst_QGlobal::qassert() QVERIFY(passed); } +/*non-static*/ Q_NEVER_INLINE char presumedValue(const char *str) +{ + Q_PRESUME(str); + // an optimizing compiler should delete the str? check + return str ? str[0] : '\0'; +} + +void tst_QGlobal::qpresume() +{ + QCOMPARE(presumedValue("Hello World"), 'H'); +} + void tst_QGlobal::qtry() { int i = 0; diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index 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..291b7136bce 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; 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/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/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index f7c5af53310..9c61cb503fb 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -3250,6 +3250,7 @@ void tst_QWindow::enterLeaveOnWindowShowHide() ++expectedEnter; QTRY_COMPARE_WITH_TIMEOUT(window.numEnterEvents, expectedEnter, 250); QCOMPARE(window.enterPosition, window.mapFromGlobal(QCursor::pos())); + QCOMPARE(QGuiApplicationPrivate::lastCursorPosition, cursorPos); QWindow secondary; secondary.setFlag(windowType); 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/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/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/tools/moc/allmocs_baseline_in.json b/tests/auto/tools/moc/allmocs_baseline_in.json index a0bca54df25..8f7757a7272 100644 --- a/tests/auto/tools/moc/allmocs_baseline_in.json +++ b/tests/auto/tools/moc/allmocs_baseline_in.json @@ -734,6 +734,7 @@ "classes": [ { "className": "FinalTestClassQt", + "final": true, "lineNumber": 15, "object": true, "qualifiedClassName": "FinalTestClassQt", @@ -746,6 +747,7 @@ }, { "className": "ExportedFinalTestClassQt", + "final": true, "lineNumber": 24, "object": true, "qualifiedClassName": "ExportedFinalTestClassQt", @@ -758,6 +760,7 @@ }, { "className": "ExportedFinalTestClassQtX", + "final": true, "lineNumber": 32, "object": true, "qualifiedClassName": "ExportedFinalTestClassQtX", @@ -770,6 +773,7 @@ }, { "className": "FinalTestClassCpp11", + "final": true, "lineNumber": 40, "object": true, "qualifiedClassName": "FinalTestClassCpp11", @@ -782,6 +786,7 @@ }, { "className": "ExportedFinalTestClassCpp11", + "final": true, "lineNumber": 48, "object": true, "qualifiedClassName": "ExportedFinalTestClassCpp11", @@ -794,6 +799,7 @@ }, { "className": "ExportedFinalTestClassCpp11X", + "final": true, "lineNumber": 56, "object": true, "qualifiedClassName": "ExportedFinalTestClassCpp11X", @@ -806,6 +812,7 @@ }, { "className": "SealedTestClass", + "final": true, "lineNumber": 64, "object": true, "qualifiedClassName": "SealedTestClass", @@ -818,6 +825,7 @@ }, { "className": "ExportedSealedTestClass", + "final": true, "lineNumber": 72, "object": true, "qualifiedClassName": "ExportedSealedTestClass", @@ -830,6 +838,7 @@ }, { "className": "ExportedSealedTestClassX", + "final": true, "lineNumber": 80, "object": true, "qualifiedClassName": "ExportedSealedTestClassX", diff --git a/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp b/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp index 5ee85694406..65ac08f5069 100644 --- a/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp +++ b/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp @@ -149,7 +149,7 @@ void tst_xdgdecorationv1::clientSidePreferredByCompositor() QVERIFY(window.frameMargins().isNull()); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_unsetModeRequested); + QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_requestedMode == XdgToplevelDecorationV1::mode_server_side); QVERIFY(window.frameMargins().isNull()); // We're still waiting for a configure exec([&] { toplevelDecoration()->sendConfigure(XdgToplevelDecorationV1::mode_client_side); diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST index 8f8c065560a..7bde411761b 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST @@ -9,3 +9,6 @@ android android [windowOpacity] macos # QTBUG-139950 +[clickFocus] +windows-10 # QTBUG-141386 + diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp index 805fa046a07..3e5c65cf0be 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp +++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp @@ -156,6 +156,7 @@ private slots: void removeIndexWhileEditing(); void focusNextOnHide(); void shiftSelectionAfterModelSetCurrentIndex(); + void QTBUG72333_isPersistentEditorOpen(); private: static QAbstractItemView *viewFromString(const QByteArray &viewType, QWidget *parent = nullptr) @@ -3604,5 +3605,29 @@ void tst_QAbstractItemView::shiftSelectionAfterModelSetCurrentIndex() QCOMPARE(selection.first().bottom(), 2); } +void tst_QAbstractItemView::QTBUG72333_isPersistentEditorOpen() +{ + QStandardItemModel model(1, 1); + model.setData(model.index(0, 0), QStringLiteral("Test")); + + QTableView view; + view.setModel(&model); + view.show(); + + const QModelIndex index = model.index(0, 0); + + view.edit(index); + QVERIFY(view.state() == QAbstractItemView::EditingState); + QVERIFY(!view.isPersistentEditorOpen(index)); + + view.closeEditor(view.indexWidget(index), QAbstractItemDelegate::RevertModelCache); + + view.openPersistentEditor(index); + QVERIFY(view.isPersistentEditorOpen(index)); + + view.closePersistentEditor(index); + QVERIFY(!view.isPersistentEditorOpen(index)); +} + QTEST_MAIN(tst_QAbstractItemView) #include "tst_qabstractitemview.moc" diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 72f5ada4889..8e46876934d 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -11280,7 +11280,7 @@ void tst_QWidget::destroyBackingStore() #endif // QT_BUILD_INTERNAL // Helper function -QWidgetRepaintManager* repaintManager(QWidget &widget) +QWidgetRepaintManager* repaintManager([[maybe_unused]] QWidget &widget) { QWidgetRepaintManager *repaintManager = nullptr; #ifdef QT_BUILD_INTERNAL 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") }; diff --git a/util/unicode/main.cpp b/util/unicode/main.cpp index ffd7d185905..1f31febeaaf 100644 --- a/util/unicode/main.cpp +++ b/util/unicode/main.cpp @@ -3365,11 +3365,13 @@ int main(int, char **) QByteArray normalizationCorrections = createNormalizationCorrections(); QByteArray idnaMapping = createIdnaMapping(); + # REUSE-IgnoreStart QByteArray header = "// Copyright (C) 2020 The Qt Company Ltd.\n" "// SPDX-License-Identifier: Unicode-3.0\n" "// Qt-Security score:significant reason:default\n" "\n"; + # REUSE-IgnoreEnd QByteArray note = "/* This file is autogenerated from the Unicode " DATA_VERSION_S " database. Do not edit */\n\n"; diff --git a/util/xkbdatagen/main.cpp b/util/xkbdatagen/main.cpp index 6dfd765e392..4c9227816b1 100644 --- a/util/xkbdatagen/main.cpp +++ b/util/xkbdatagen/main.cpp @@ -374,13 +374,14 @@ int main(int argc, char **argv) } QList<XKBLayout> layouts = findLayouts(layoutList); - + # REUSE-IgnoreStart // copyright and stuff printf("// Copyright (C) 2016 The Qt Company Ltd.\n" "// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only\n" "// This file is auto-generated, do not edit!\n" "// (Generated using util/xkbdatagen)\n" "\n"); + # REUSE-IgnoreEnd // data structure printf("static struct {\n" |