diff options
| author | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2025-01-29 16:12:28 +0200 |
|---|---|---|
| committer | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2025-01-29 16:12:28 +0200 |
| commit | 7ac842cba18be081ac835bf40ac475ec4c47d30b (patch) | |
| tree | c35c8f45a56adf8df9ba018facce713a99d6074f | |
| parent | 9edb471d3a35b3dc40def86c395789086edaa983 (diff) | |
| parent | 3760c04fdea95b6e6d6ccac7c594993f063096fd (diff) | |
Merge tag 'v6.5.5-lts' into tqtc/lts-6.5-opensource
Qt 6.5.5-lts release
Conflicts solved:
dependencies.yaml
src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml
Change-Id: Ifa297315fa0e6e70d9f26312f9183da4769d18fc
378 files changed, 4190 insertions, 1254 deletions
diff --git a/.cmake.conf b/.cmake.conf index 39f8d82b81..ac0f20fca0 100644 --- a/.cmake.conf +++ b/.cmake.conf @@ -1,4 +1,4 @@ -set(QT_REPO_MODULE_VERSION "6.5.4") +set(QT_REPO_MODULE_VERSION "6.5.5") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1") diff --git a/dependencies.yaml b/dependencies.yaml index ada84fb6ad..84332ce0d2 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,16 +1,16 @@ dependencies: ../tqtc-qtbase: - ref: 8ff0b254e4c3db81254782262d827f7831d15f6b + ref: fdf57f5df57e7d12cf871699d857a71acf272e0c required: true ../tqtc-qtimageformats: - ref: 8170735657e6df87c1275b6ad7ca1e60ed0b9314 + ref: 93317400471b1ef875471bfd719b00fe4fffc4fe required: false ../tqtc-qtlanguageserver: - ref: c24ae032c6991321046bb47ba3b680ddac1c242c + ref: a4240e7eea774e7cc3019e3f1bf3168fd76612bc required: false ../tqtc-qtshadertools: - ref: 5bf9de4a39fe2385998307c3e5ae16df6c0a5bd7 + ref: 8191dce7e16f9bf42476c41a3b7aab9a62b26daa required: false ../tqtc-qtsvg: - ref: 7c01c49a83d851196d990da86c08d59b3c7951f8 + ref: c9ca482a2876fb7d37717e5146eb37f7e26f03da required: false diff --git a/examples/quick/customitems/painteditem/CMakeLists.txt b/examples/quick/customitems/painteditem/CMakeLists.txt index b9f608da0f..d97db9b03e 100644 --- a/examples/quick/customitems/painteditem/CMakeLists.txt +++ b/examples/quick/customitems/painteditem/CMakeLists.txt @@ -45,7 +45,5 @@ install(TARGETS painteditemexample include(../../shared/QtBundleQmlModuleForMacOS.cmake) set(app_target "painteditemexample") -set(qml_plugin_target "qmltextballoon") -set(qml_module_uri "TextBalloon") -add_qml_module_to_macos_app_bundle( - "${app_target}" "${qml_plugin_target}" "${qml_module_uri}") +set(qml_module "qmltextballoon") +add_qml_module_to_macos_app_bundle("${app_target}" "${qml_module}") diff --git a/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc b/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc index 40a959932a..9fc33601d0 100644 --- a/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc +++ b/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc @@ -1,5 +1,5 @@ <RCC> - <qresource prefix="/qt/qml/embeddedinwidgets"> + <qresource prefix="/embeddedinwidgets"> <file>main.qml</file> <file>reflect.frag.qsb</file> </qresource> diff --git a/examples/quick/pointerhandlers/components/QuadPieMenu.qml b/examples/quick/pointerhandlers/components/QuadPieMenu.qml index 9c2364fea2..2e29677909 100644 --- a/examples/quick/pointerhandlers/components/QuadPieMenu.qml +++ b/examples/quick/pointerhandlers/components/QuadPieMenu.qml @@ -9,6 +9,7 @@ TapHandler { signal triggered(string text) id: menuTap + acceptedButtons: Qt.RightButton gesturePolicy: TapHandler.DragWithinBounds onPressedChanged: if (pressed) { impl.x = point.position.x - impl.width / 2 @@ -22,7 +23,10 @@ TapHandler { parent: menuTap.parent width: 100 height: 100 - scale: Math.min(1, Math.max(0, menuTap.timeHeld * 4)) + // with touchscreen or stylus, long-press slowly expands the menu to size + // with mouse or touchpad right-click, it opens instantly + scale: menuTap.point.device.pointerType === PointerDevice.Generic ? + 1 : Math.min(1, Math.max(0, menuTap.timeHeld * 4)) opacity: scale * 2 visible: menuTap.pressed property Shape highlightedShape: null diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp index ecac279a2f..584cecfb00 100644 --- a/examples/quick/quickwidgets/quickwidget/main.cpp +++ b/examples/quick/quickwidgets/quickwidget/main.cpp @@ -63,14 +63,22 @@ MainWindow::MainWindow() setCentralWidget(centralWidget); QMenu *fileMenu = menuBar()->addMenu(tr("&File")); - fileMenu->addAction(tr("Grab framebuffer"), this, &MainWindow::grabFramebuffer); - fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToPixmap); - fileMenu->addAction(tr("Grab via grabToImage"), this, &MainWindow::grabToImage); + auto grabAction = fileMenu->addAction(tr("Grab framebuffer"), this, &MainWindow::grabFramebuffer); + auto renderAction = fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToPixmap); + auto grabToImageAction = fileMenu->addAction(tr("Grab via grabToImage"), this, &MainWindow::grabToImage); fileMenu->addAction(tr("Quit"), qApp, &QCoreApplication::quit); QMenu *windowMenu = menuBar()->addMenu(tr("&Window")); windowMenu->addAction(tr("Add tab widget"), this, [this, centralWidget] { createQuickWidgetsInTabs(centralWidget); }); + + connect(m_quickWidget, &QObject::destroyed, this, + [this, grabAction, renderAction, grabToImageAction] { + m_quickWidget = nullptr; + grabAction->setEnabled(false); + renderAction->setEnabled(false); + grabToImageAction->setEnabled(false); + }); } void MainWindow::createQuickWidgetsInTabs(QMdiArea *mdiArea) @@ -123,6 +131,7 @@ void MainWindow::quickWidgetStatusChanged(QQuickWidget::Status status) { if (status == QQuickWidget::Error) { QStringList errors; + Q_ASSERT(m_quickWidget); const auto widgetErrors = m_quickWidget->errors(); for (const QQmlError &error : widgetErrors) errors.append(error.toString()); @@ -147,12 +156,14 @@ template<class T> void saveToFile(QWidget *parent, T *saveable) void MainWindow::grabFramebuffer() { + Q_ASSERT(m_quickWidget); QImage image = m_quickWidget->grabFramebuffer(); saveToFile(this, &image); } void MainWindow::renderToPixmap() { + Q_ASSERT(m_quickWidget); QPixmap pixmap(m_quickWidget->size()); m_quickWidget->render(&pixmap); saveToFile(this, &pixmap); @@ -165,6 +176,7 @@ void MainWindow::grabToImage() fd.setDefaultSuffix("png"); fd.selectFile("test_grabToImage.png"); if (fd.exec() == QDialog::Accepted) { + Q_ASSERT(m_quickWidget); QMetaObject::invokeMethod(m_quickWidget->rootObject(), "performLayerBasedGrab", Q_ARG(QVariant, fd.selectedFiles().first())); } diff --git a/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc b/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc index 2d4ce46fe7..09f3f3a9fc 100644 --- a/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc +++ b/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc @@ -8,9 +8,9 @@ \ingroup qtquickexamples \brief Shows how to implement a custom geometry in the Qt Quick Scene Graph. - The custom geometry example shows how to create a QQuickItem which + The custom geometry example shows how to create a \l QQuickItem that uses the scene graph API to build a custom geometry for the scene - graph. It does this by creating a BezierCurve item which is made + graph. It does this by creating a \c BezierCurve item, which is made part of the CustomGeometry module and makes use of this in a QML file. @@ -115,7 +115,7 @@ \snippet scenegraph/customgeometry/beziercurve.cpp 6 The scene graph API provides a few commonly used material - implementations. In this example we use the QSGFlatColorMaterial + implementations. In this example we use the QSGFlatColorMaterial, which will fill the shape defined by the geometry with a solid color. Again we pass the ownership of the material to the node, so it can be cleaned up by the scene graph. @@ -130,7 +130,7 @@ \snippet scenegraph/customgeometry/beziercurve.cpp 8 - To fill the geometry, we first extract the vertex array from + To fill the geometry we first extract the vertex array from it. Since we are using one of the default attribute sets, we can use the convenience function QSGGeometry::vertexDataAsPoint2D(). Then we go through each segment and calculate its position and @@ -138,7 +138,7 @@ \snippet scenegraph/customgeometry/beziercurve.cpp 9 - In the end of the function, we return the node so the scene graph + At the end of the function we return the node so the scene graph can render it. \section1 Application Entry-Point @@ -153,9 +153,26 @@ To make use of the BezierCurve item, we need to register it in the QML engine, using the QML_ELEMENT macro. This gives it the name BezierCurve and makes it part of the \c {CustomGeometry 1.0} - module as defined in the customgeometry.pro file: - - \quotefile scenegraph/customgeometry/customgeometry.pro + module as defined in the project's build files: + + \if defined(onlinedocs) + \tab {build-qt-app}{tab-cmake}{CMake}{checked} + \tab {build-qt-app}{tab-qmake}{qmake}{} + \tabcontent {tab-cmake} + \else + \section1 Using CMake + \endif + \quotefile scenegraph/customgeometry/CMakeLists.txt + \if defined(onlinedocs) + \endtabcontent + \tabcontent {tab-qmake} + \else + \section1 Using qmake + \endif + \quotefile scenegraph/customgeometry/customgeometry.pro + \if defined(onlinedocs) + \endtabcontent + \endif As the bezier curve is drawn as line strips, we specify that the view should be multisampled to get antialiasing. This is not diff --git a/examples/quick/scenegraph/customrendernode/main.qml b/examples/quick/scenegraph/customrendernode/main.qml index bd6869cc19..c52132dec0 100644 --- a/examples/quick/scenegraph/customrendernode/main.qml +++ b/examples/quick/scenegraph/customrendernode/main.qml @@ -103,6 +103,11 @@ Item { } + Button { + text: qsTr("Toggle custom item visibility") + onClicked: custom.visible = !custom.visible + } + CustomRender { id: custom width: Math.min(parent.width, parent.height) diff --git a/examples/quick/shared/CMakeLists.txt b/examples/quick/shared/CMakeLists.txt index 704d148025..629a35539e 100644 --- a/examples/quick/shared/CMakeLists.txt +++ b/examples/quick/shared/CMakeLists.txt @@ -42,10 +42,8 @@ include(QtBundleQmlModuleForMacOS.cmake) # Puts the shared qml module plugin and qmldir into the macOS app bundle directory. # Only call this function if your main project has the MACOSX_BUNDLE option set. function(bundle_shared app_target) - set(qml_plugin_target "${PROJECT_NAME}_shared") - set(qml_module_uri "shared") - add_qml_module_to_macos_app_bundle( - "${app_target}" "${qml_plugin_target}" "${qml_module_uri}") + set(qml_module_target "${PROJECT_NAME}_shared") + add_qml_module_to_macos_app_bundle("${app_target}" "${qml_module_target}") endfunction() set(INSTALL_SHAREDDIR "${INSTALL_EXAMPLESDIR}/quick/${PROJECT_NAME}/shared") diff --git a/examples/quick/shared/LauncherList.qml b/examples/quick/shared/LauncherList.qml index 876c828469..f86ab80446 100644 --- a/examples/quick/shared/LauncherList.qml +++ b/examples/quick/shared/LauncherList.qml @@ -174,9 +174,14 @@ Rectangle { id: back source: "images/back.png" anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 2 + anchors.verticalCenterOffset: 1 anchors.left: parent.left - anchors.leftMargin: 16 + anchors.leftMargin: 6 + width: 38 + height: 31 + fillMode: Image.Pad + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter TapHandler { id: tapHandler @@ -188,10 +193,7 @@ Rectangle { } } Rectangle { - anchors.centerIn: back - width: 38 - height: 31 - anchors.verticalCenterOffset: -1 + anchors.fill: parent opacity: tapHandler.pressed ? 1 : 0 Behavior on opacity { NumberAnimation{ duration: 100 }} gradient: Gradient { diff --git a/examples/quick/shared/QtBundleQmlModuleForMacOS.cmake b/examples/quick/shared/QtBundleQmlModuleForMacOS.cmake index 06d1487323..767b64bbfc 100644 --- a/examples/quick/shared/QtBundleQmlModuleForMacOS.cmake +++ b/examples/quick/shared/QtBundleQmlModuleForMacOS.cmake @@ -1,12 +1,18 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -function(add_qml_module_to_macos_app_bundle app_target qml_plugin_target qml_module_uri) +function(add_qml_module_to_macos_app_bundle app_target qml_module) if(QT6_IS_SHARED_LIBS_BUILD AND APPLE) # The application's main.cpp adds an explicit QML import path to look for qml module plugins # under a PlugIns subdirectory of a macOS app bundle. # Copy the qmldir and shared library qml plugin. + qt6_query_qml_module(${qml_module} + QMLDIR qmldir_file + PLUGIN_TARGET qml_plugin_target + URI qml_module_uri + ) + # Ensure the executable depends on the plugin so the plugin is copied # only after it was built. add_dependencies(${app_target} ${qml_plugin_target}) @@ -17,9 +23,6 @@ function(add_qml_module_to_macos_app_bundle app_target qml_plugin_target qml_mod set(dest_module_dir_in_app_bundle "${app_dir}/../PlugIns/${escaped_uri}") - set(qml_plugin_dir "$<TARGET_FILE_DIR:${qml_plugin_target}>") - set(qmldir_file "${qml_plugin_dir}/qmldir") - add_custom_command(TARGET ${app_target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${dest_module_dir_in_app_bundle} COMMAND ${CMAKE_COMMAND} -E copy_if_different diff --git a/examples/quick/shared/SimpleLauncherDelegate.qml b/examples/quick/shared/SimpleLauncherDelegate.qml index c6ca9802d2..a6ac090b34 100644 --- a/examples/quick/shared/SimpleLauncherDelegate.qml +++ b/examples/quick/shared/SimpleLauncherDelegate.qml @@ -9,7 +9,7 @@ Rectangle { property Item exampleItem width: ListView.view.width - height: button.implicitHeight + 22 + height: col.implicitHeight + 22 signal clicked() @@ -22,10 +22,18 @@ Rectangle { GradientStop { position: 1 Behavior on color {ColorAnimation { duration: 100 }} - color: tapHandler.pressed ? "#e0e0e0" : button.containsMouse ? "#f5f5f5" : "#eee" + color: tapHandler.pressed ? "#e0e0e0" : hoverHandler.hovered ? "#f5f5f5" : "#eee" } } + TapHandler { + id: tapHandler + onTapped: container.clicked() + } + HoverHandler { + id: hoverHandler + } + Image { id: image opacity: 0.7 @@ -36,53 +44,28 @@ Rectangle { anchors.rightMargin: 16 } - Item { - id: button - anchors.top: parent.top + Column { + id: col + spacing: 2 anchors.left: parent.left - anchors.bottom: parent.bottom anchors.right:image.left - implicitHeight: col.height - height: implicitHeight - width: buttonLabel.width + 20 - property alias containsMouse: hoverHandler.hovered - - TapHandler { - id: tapHandler - onTapped: container.clicked() - } - HoverHandler { - id: hoverHandler + anchors.margins: 10 + anchors.verticalCenter: parent.verticalCenter + Text { + width: parent.width + text: container.name + color: "black" + font.pixelSize: 22 + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + styleColor: "white" + style: Text.Raised } - - Column { - spacing: 2 - id: col - anchors.verticalCenter: parent.verticalCenter + Text { width: parent.width - Text { - id: buttonLabel - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.right: parent.right - anchors.rightMargin: 10 - text: container.name - color: "black" - font.pixelSize: 22 - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - styleColor: "white" - style: Text.Raised - - } - Text { - id: buttonLabel2 - anchors.left: parent.left - anchors.leftMargin: 10 - text: container.description - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - color: "#666" - font.pixelSize: 12 - } + text: container.description + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + color: "#666" + font.pixelSize: 12 } } diff --git a/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt b/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt index 5c5536aace..159395307f 100644 --- a/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt +++ b/examples/quickcontrols/attachedstyleproperties/CMakeLists.txt @@ -46,7 +46,5 @@ install(TARGETS attachedstylepropertiesexample include(../../quick/shared/QtBundleQmlModuleForMacOS.cmake) set(app_target "attachedstylepropertiesexample") -set(qml_plugin_target "MyStyleplugin") -set(qml_module_uri "MyStyle") -add_qml_module_to_macos_app_bundle( - "${app_target}" "${qml_plugin_target}" "${qml_module_uri}") +set(qml_module_target "MyStyle") +add_qml_module_to_macos_app_bundle("${app_target}" "${qml_module_target}") diff --git a/examples/quickcontrols/contactlist/CMakeLists.txt b/examples/quickcontrols/contactlist/CMakeLists.txt index 2024157a9c..508bccaae9 100644 --- a/examples/quickcontrols/contactlist/CMakeLists.txt +++ b/examples/quickcontrols/contactlist/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) @@ -28,7 +28,7 @@ qt_add_qml_module(contactlistexample "ContactForm.ui.qml" "ContactView.ui.qml" "SectionDelegate.ui.qml" - "contactlist.qml" + "ContactList.qml" "designer/Backend/ContactModel.qml" ) diff --git a/examples/quickcontrols/contactlist/ContactDelegate.ui.qml b/examples/quickcontrols/contactlist/ContactDelegate.ui.qml index 3591b2f3fd..e1e6127bb5 100644 --- a/examples/quickcontrols/contactlist/ContactDelegate.ui.qml +++ b/examples/quickcontrols/contactlist/ContactDelegate.ui.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick @@ -7,14 +7,18 @@ import QtQuick.Controls ItemDelegate { id: delegate - checkable: true + required property string fullName + required property string address + required property string city + required property string number + contentItem: ColumnLayout { spacing: 10 Label { - text: fullName + text: delegate.fullName font.bold: true elide: Text.ElideRight Layout.fillWidth: true @@ -34,7 +38,7 @@ ItemDelegate { } Label { - text: address + text: delegate.address font.bold: true elide: Text.ElideRight Layout.fillWidth: true @@ -46,7 +50,7 @@ ItemDelegate { } Label { - text: city + text: delegate.city font.bold: true elide: Text.ElideRight Layout.fillWidth: true @@ -58,7 +62,7 @@ ItemDelegate { } Label { - text: number + text: delegate.number font.bold: true elide: Text.ElideRight Layout.fillWidth: true @@ -74,6 +78,7 @@ ItemDelegate { PropertyChanges { // TODO: When Qt Design Studio supports generalized grouped properties, change to: // grid.visible: true + // qmllint disable Quick.property-changes-parsed target: grid visible: true } diff --git a/examples/quickcontrols/contactlist/ContactDialog.qml b/examples/quickcontrols/contactlist/ContactDialog.qml index 9bb990f2ad..3f287447ef 100644 --- a/examples/quickcontrols/contactlist/ContactDialog.qml +++ b/examples/quickcontrols/contactlist/ContactDialog.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick @@ -41,5 +41,9 @@ Dialog { id: form } - onAccepted: finished(form.fullName.text, form.address.text, form.city.text, form.number.text) + onAccepted: { + if (form.fullName.text && form.address.text && form.city.text && form.number.text) { + finished(form.fullName.text, form.address.text, form.city.text, form.number.text); + } + } } diff --git a/examples/quickcontrols/contactlist/ContactForm.ui.qml b/examples/quickcontrols/contactlist/ContactForm.ui.qml index 918da57f30..56c9186194 100644 --- a/examples/quickcontrols/contactlist/ContactForm.ui.qml +++ b/examples/quickcontrols/contactlist/ContactForm.ui.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick diff --git a/examples/quickcontrols/contactlist/contactlist.qml b/examples/quickcontrols/contactlist/ContactList.qml index 28f887f2db..121b38f352 100644 --- a/examples/quickcontrols/contactlist/contactlist.qml +++ b/examples/quickcontrols/contactlist/ContactList.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick @@ -17,10 +17,10 @@ ApplicationWindow { ContactDialog { id: contactDialog onFinished: function(fullName, address, city, number) { - if (currentContact === -1) + if (window.currentContact === -1) contactView.model.append(fullName, address, city, number) else - contactView.model.set(currentContact, fullName, address, city, number) + contactView.model.set(window.currentContact, fullName, address, city, number) } } @@ -35,23 +35,23 @@ ApplicationWindow { font.bold: true width: parent.width horizontalAlignment: Qt.AlignHCenter - text: currentContact >= 0 ? contactView.model.get(currentContact).fullName : "" + text: window.currentContact >= 0 ? contactView.model.get(window.currentContact).fullName : "" } MenuItem { text: qsTr("Edit...") - onTriggered: contactDialog.editContact(contactView.model.get(currentContact)) + onTriggered: contactDialog.editContact(contactView.model.get(window.currentContact)) } MenuItem { text: qsTr("Remove") - onTriggered: contactView.model.remove(currentContact) + onTriggered: contactView.model.remove(window.currentContact) } } ContactView { id: contactView anchors.fill: parent - onPressAndHold: { - currentContact = index + onPressAndHold: function(index) { + window.currentContact = index contactMenu.open() } } @@ -63,7 +63,7 @@ ApplicationWindow { anchors.right: parent.right anchors.bottom: parent.bottom onClicked: { - currentContact = -1 + window.currentContact = -1 contactDialog.createContact() } } diff --git a/examples/quickcontrols/contactlist/ContactView.ui.qml b/examples/quickcontrols/contactlist/ContactView.ui.qml index 6e80d05bac..7d5b3cf6c8 100644 --- a/examples/quickcontrols/contactlist/ContactView.ui.qml +++ b/examples/quickcontrols/contactlist/ContactView.ui.qml @@ -1,9 +1,11 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls -import Backend +import contactlist ListView { id: listView @@ -25,6 +27,9 @@ ListView { delegate: ContactDelegate { id: delegate width: listView.width + + required property int index + onPressAndHold: listView.pressAndHold(index) } diff --git a/examples/quickcontrols/contactlist/SectionDelegate.ui.qml b/examples/quickcontrols/contactlist/SectionDelegate.ui.qml index 05b2147288..1ed587abd7 100644 --- a/examples/quickcontrols/contactlist/SectionDelegate.ui.qml +++ b/examples/quickcontrols/contactlist/SectionDelegate.ui.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick @@ -7,9 +7,11 @@ import QtQuick.Controls ToolBar { id: background + required property string section + Label { id: label - text: section + text: background.section anchors.fill: parent horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter diff --git a/examples/quickcontrols/contactlist/contactlist.pro b/examples/quickcontrols/contactlist/contactlist.pro index ceb99cbc47..2fead83186 100644 --- a/examples/quickcontrols/contactlist/contactlist.pro +++ b/examples/quickcontrols/contactlist/contactlist.pro @@ -2,6 +2,12 @@ TEMPLATE = app TARGET = contactlist QT += quick +CONFIG += qmltypes + +QML_IMPORT_PATH = $$pwd/. +QML_IMPORT_NAME = contactlist +QML_IMPORT_MAJOR_VERSION = 1 + HEADERS += \ contactmodel.h @@ -9,15 +15,20 @@ SOURCES += \ main.cpp \ contactmodel.cpp -RESOURCES += \ +qml_resources.files = \ + qmldir \ ContactDelegate.ui.qml \ ContactDialog.qml \ ContactForm.ui.qml \ - contactlist.qml \ + ContactList.qml \ ContactView.ui.qml \ designer/Backend/ContactModel.qml \ SectionDelegate.ui.qml +qml_resources.prefix = /qt/qml/contactlist + +RESOURCES += qml_resources + # Additional import path used to resolve QML modules just for Qt Quick Designer QML_DESIGNER_IMPORT_PATH = $$PWD/designer diff --git a/examples/quickcontrols/contactlist/contactmodel.cpp b/examples/quickcontrols/contactlist/contactmodel.cpp index 1add2e7776..85ebad0515 100644 --- a/examples/quickcontrols/contactlist/contactmodel.cpp +++ b/examples/quickcontrols/contactlist/contactmodel.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "contactmodel.h" diff --git a/examples/quickcontrols/contactlist/contactmodel.h b/examples/quickcontrols/contactlist/contactmodel.h index 564ec0fab3..78a06b8132 100644 --- a/examples/quickcontrols/contactlist/contactmodel.h +++ b/examples/quickcontrols/contactlist/contactmodel.h @@ -1,14 +1,16 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef CONTACTMODEL_H #define CONTACTMODEL_H #include <QAbstractListModel> +#include <QQmlEngine> class ContactModel : public QAbstractListModel { Q_OBJECT + QML_ELEMENT public: enum ContactRole { diff --git a/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml b/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml index c7a3f4817b..850b444d08 100644 --- a/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml +++ b/examples/quickcontrols/contactlist/designer/Backend/ContactModel.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick diff --git a/examples/quickcontrols/contactlist/main.cpp b/examples/quickcontrols/contactlist/main.cpp index 32a88def4c..2f9f15fd36 100644 --- a/examples/quickcontrols/contactlist/main.cpp +++ b/examples/quickcontrols/contactlist/main.cpp @@ -1,19 +1,15 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <QGuiApplication> #include <QQmlApplicationEngine> -#include "contactmodel.h" - int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); - qmlRegisterType<ContactModel>("Backend", 1, 0, "ContactModel"); - QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/contactlist.qml"))); + engine.loadFromModule("contactlist", "ContactList"); return app.exec(); } diff --git a/examples/quickcontrols/contactlist/qmldir b/examples/quickcontrols/contactlist/qmldir new file mode 100644 index 0000000000..2d0af68242 --- /dev/null +++ b/examples/quickcontrols/contactlist/qmldir @@ -0,0 +1,8 @@ +module contactlist + +ContactDelegate 1.0 ContactDelegate.ui.qml +ContactDialog 1.0 ContactDialog.qml +ContactForm 1.0 ContactForm.ui.qml +ContactList 1.0 ContactList.qml +ContactView 1.0 ContactView.ui.qml +SectionDelegate 1.0 SectionDelegate.ui.qml diff --git a/examples/quickcontrols/wearable/CMakeLists.txt b/examples/quickcontrols/wearable/CMakeLists.txt index c293d64ef2..eccbea20aa 100644 --- a/examples/quickcontrols/wearable/CMakeLists.txt +++ b/examples/quickcontrols/wearable/CMakeLists.txt @@ -54,6 +54,6 @@ install(TARGETS wearableexample include(../../quick/shared/QtBundleQmlModuleForMacOS.cmake) set(app_target "wearableexample") -add_qml_module_to_macos_app_bundle("${app_target}" "wearable" "Wearable") -add_qml_module_to_macos_app_bundle("${app_target}" "wearablesettings" "WearableSettings") -add_qml_module_to_macos_app_bundle("${app_target}" "wearablestyle" "WearableStyle") +add_qml_module_to_macos_app_bundle("${app_target}" "wearable") +add_qml_module_to_macos_app_bundle("${app_target}" "wearablesettings") +add_qml_module_to_macos_app_bundle("${app_target}" "wearablestyle") diff --git a/src/3rdparty/masm/qt_attribution.json b/src/3rdparty/masm/qt_attribution.json index aab413ad40..77b144d738 100644 --- a/src/3rdparty/masm/qt_attribution.json +++ b/src/3rdparty/masm/qt_attribution.json @@ -8,17 +8,17 @@ "License": "BSD 2-clause \"Simplified\" License", "LicenseId": "BSD-2-Clause", "LicenseFile": "LICENSE", - "Copyright": "Copyright (C) 2003-2018 Apple Inc. All rights reserved. -Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) -Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved. (http://www.torchmobile.com/) -Copyright (C) 2009, 2010 University of Szeged -Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. -Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. -Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged -Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. -Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. -Copyright (C) 2011 Google Inc. All rights reserved. -Copyright (C) 2013 Samsung Electronics. All rights reserved. -Copyright (C) 2015 Cisco Systems, Inc. All rights reserved. -Copyright (c) 2002-2009 Vivek Thampi" + "Copyright": ["Copyright (C) 2003-2018 Apple Inc. All rights reserved.", + "Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)", + "Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved. (http://www.torchmobile.com/)", + "Copyright (C) 2009, 2010 University of Szeged", + "Copyright (C) 2009-2011 STMicroelectronics. All rights reserved.", + "Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.", + "Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged", + "Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.", + "Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.", + "Copyright (C) 2011 Google Inc. All rights reserved.", + "Copyright (C) 2013 Samsung Electronics. All rights reserved.", + "Copyright (C) 2015 Cisco Systems, Inc. All rights reserved.", + "Copyright (c) 2002-2009 Vivek Thampi"] } diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp index a8990a92b4..e183d434bd 100644 --- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp +++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -114,10 +114,7 @@ void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, if (result == MAP_FAILED) CRASH(); - while (madvise(result, bytes, MADV_DONTNEED)) { - if (errno != EAGAIN) - CRASH(); - } + while (madvise(result, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } if (fd != -1) close(fd); @@ -250,8 +247,10 @@ void OSAllocator::decommit(void* address, size_t bytes) mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); #elif OS(LINUX) while (madvise(address, bytes, MADV_DONTNEED)) { - if (errno != EAGAIN) - CRASH(); + if (errno != EAGAIN) { + memset(address, 0, bytes); // We rely on madvise to zero-out the memory + break; + } } if (mprotect(address, bytes, PROT_NONE)) CRASH(); diff --git a/src/effects/data/shaders/bluritems.frag b/src/effects/data/shaders/bluritems.frag index cf3eab7356..fb94fe6073 100644 --- a/src/effects/data/shaders/bluritems.frag +++ b/src/effects/data/shaders/bluritems.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/effects/data/shaders/multieffect.frag b/src/effects/data/shaders/multieffect.frag index 94099f503f..db8b802660 100644 --- a/src/effects/data/shaders/multieffect.frag +++ b/src/effects/data/shaders/multieffect.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/effects/qquickmultieffect.cpp b/src/effects/qquickmultieffect.cpp index b1908e59bb..e01bac8a99 100644 --- a/src/effects/qquickmultieffect.cpp +++ b/src/effects/qquickmultieffect.cpp @@ -1662,6 +1662,10 @@ void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel) if (!m_shaderEffect) return; + const auto engine = qmlEngine(q); + if (!engine) + return; + // Lowest blur level uses 3 items, highest 5 items. int itemsAmount = blurLevel == 0 ? 0 : blurLevel + 2; @@ -1669,7 +1673,6 @@ void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel) // Add more blur items. // Note that by design blur items are only added and never reduced // during the lifetime of the effect component. - const auto engine = qmlEngine(q); QUrl blurVs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.vert.qsb")); QUrl blurFs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.frag.qsb")); QQmlComponent blurComponent(engine, QUrl(QStringLiteral("qrc:/data/BlurItem.qml"))); diff --git a/src/particles/shaders_ng/imageparticle.frag b/src/particles/shaders_ng/imageparticle.frag index 074771f4fd..383d7f4c98 100644 --- a/src/particles/shaders_ng/imageparticle.frag +++ b/src/particles/shaders_ng/imageparticle.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/plugins/qmllint/quick/quicklintplugin.cpp b/src/plugins/qmllint/quick/quicklintplugin.cpp index dda42b48b4..afed093269 100644 --- a/src/plugins/qmllint/quick/quicklintplugin.cpp +++ b/src/plugins/qmllint/quick/quicklintplugin.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "quicklintplugin.h" @@ -761,11 +761,13 @@ void PropertyChangesValidatorPass::run(const QQmlSA::Element &element) return; QString targetId = u"<id>"_s; - const QString targetBinding = sourceCode(target->sourceLocation()); + const auto targetLocation = target->sourceLocation(); + const QString targetBinding = sourceCode(targetLocation); const QQmlSA::Element targetElement = resolveIdToElement(targetBinding, element); if (!targetElement.isNull()) targetId = targetBinding; + bool hadCustomParsedBindings = false; for (auto it = bindings.begin(), end = bindings.end(); it != end; ++it) { const QString name = it->propertyName(); if (element->hasProperty(name)) @@ -783,12 +785,19 @@ void PropertyChangesValidatorPass::run(const QQmlSA::Element &element) if (binding.length() > 16) binding = binding.left(13) + "..."_L1; + hadCustomParsedBindings = true; emitWarning( "Property \"%1\" is custom-parsed in PropertyChanges. " "You should phrase this binding as \"%2.%1: %3\""_L1 .arg(name, targetId, binding), quickPropertyChangesParsed, bindingLocation); } + + if (hadCustomParsedBindings && !targetElement.isNull()) { + emitWarning("You should remove any bindings on the \"target\" property and avoid " + "custom-parsed bindings in PropertyChanges.", + quickPropertyChangesParsed, targetLocation); + } } QT_END_NAMESPACE diff --git a/src/plugins/qmllint/quick/quicklintplugin.h b/src/plugins/qmllint/quick/quicklintplugin.h index 85b44f376a..1abe0c4c1b 100644 --- a/src/plugins/qmllint/quick/quicklintplugin.h +++ b/src/plugins/qmllint/quick/quicklintplugin.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QUICKLINTPLUGIN_H #define QUICKLINTPLUGIN_H diff --git a/src/qml/Qt6QmlDeploySupport.cmake b/src/qml/Qt6QmlDeploySupport.cmake index 21922c8855..91c47e920e 100644 --- a/src/qml/Qt6QmlDeploySupport.cmake +++ b/src/qml/Qt6QmlDeploySupport.cmake @@ -226,6 +226,16 @@ function(_qt_internal_deploy_qml_imports_for_target) endforeach() endif() + # Install runtime dependencies on Windows. + if(__QT_DEPLOY_SYSTEM_NAME STREQUAL "Windows") + foreach(file IN LISTS __QT_DEPLOY_TARGET_${entry_LINKTARGET}_RUNTIME_DLLS) + if(__QT_DEPLOY_VERBOSE) + message(STATUS "runtime dependency for QML plugin '${entry_PLUGIN}':") + endif() + file(INSTALL ${file} DESTINATION "${QT_DEPLOY_PREFIX}/${QT_DEPLOY_BIN_DIR}") + endforeach() + endif() + if(__QT_DEPLOY_TOOL STREQUAL "GRD" AND __QT_DEPLOY_MUST_ADJUST_PLUGINS_RPATH) # The RPATHs of the installed plugins do not match Qt's original lib directory. # We must set the RPATH to point to QT_DEPLOY_LIBDIR. diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index 9c116a0eee..8161a90b78 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -809,36 +809,68 @@ function(_qt_internal_target_enable_qmllint target) _qt_internal_extend_qml_import_paths(import_args) _qt_internal_get_tool_wrapper_script_path(tool_wrapper) - set(cmd - ${tool_wrapper} - $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint> + + set(qmllint_args --bare ${import_args} ${qrc_args} ${qmllint_files} ) + get_target_property(target_binary_dir ${target} BINARY_DIR) + set(qmllint_dir ${target_binary_dir}/.rcc/qmllint) + set(qmllint_rsp_path ${qmllint_dir}/${target}.rsp) + + file(GENERATE + OUTPUT "${qmllint_rsp_path}" + CONTENT "$<JOIN:${qmllint_args},\n>\n" + ) + + set(cmd + ${tool_wrapper} + $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint> + @${qmllint_rsp_path} + ) + + set(cmd_dummy ${CMAKE_COMMAND} -E echo "Nothing to do for target ${lint_target}.") + # We need this target to depend on all qml type registrations. This is the # only way we can be sure that all *.qmltypes files for any QML modules we # depend on will have been generated. add_custom_target(${lint_target} - COMMAND "$<${have_qmllint_files}:${cmd}>" + COMMAND "$<IF:${have_qmllint_files},${cmd},${cmd_dummy}>" COMMAND_EXPAND_LISTS DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::qmllint ${qmllint_files} + ${qmllint_rsp_path} $<TARGET_NAME_IF_EXISTS:all_qmltyperegistrations> WORKING_DIRECTORY "$<TARGET_PROPERTY:${target},SOURCE_DIR>" ) _qt_internal_assign_to_qmllint_targets_folder(${lint_target}) - set(lint_args "--json" "${CMAKE_BINARY_DIR}/${lint_target}.json") + list(APPEND qmllint_args "--json" "${CMAKE_BINARY_DIR}/${lint_target}.json") + + set(qmllint_rsp_path ${qmllint_dir}/${target}_json.rsp) + + file(GENERATE + OUTPUT "${qmllint_rsp_path}" + CONTENT "$<JOIN:${qmllint_args},\n>\n" + ) + + set(cmd + ${tool_wrapper} + $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint> + @${qmllint_rsp_path} + ) + add_custom_target(${lint_target_json} - COMMAND "$<${have_qmllint_files}:${cmd};${lint_args}>" + COMMAND "$<${have_qmllint_files}:${cmd}>" COMMAND_EXPAND_LISTS DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::qmllint ${qmllint_files} + ${qmllint_rsp_path} $<TARGET_NAME_IF_EXISTS:all_qmltyperegistrations> WORKING_DIRECTORY "$<TARGET_PROPERTY:${target},SOURCE_DIR>" ) @@ -849,18 +881,34 @@ function(_qt_internal_target_enable_qmllint target) get_target_property(module_uri ${target} QT_QML_MODULE_URI) _qt_internal_get_tool_wrapper_script_path(tool_wrapper) + + set(qmllint_args + ${import_args} + ${qrc_args} + --module + ${module_uri} + ) + + set(qmllint_rsp_path ${qmllint_dir}/${target}_module.rsp) + + file(GENERATE + OUTPUT "${qmllint_rsp_path}" + CONTENT "$<JOIN:${qmllint_args},\n>\n" + ) + + set(cmd + ${tool_wrapper} + $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint> + @${qmllint_rsp_path} + ) + add_custom_target(${lint_target_module} - COMMAND - ${tool_wrapper} - $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qmllint> - ${import_args} - ${qrc_args} - --module - ${module_uri} + COMMAND ${cmd} COMMAND_EXPAND_LISTS DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::qmllint ${qmllint_files} + ${qmllint_rsp_path} $<TARGET_NAME_IF_EXISTS:all_qmltyperegistrations> WORKING_DIRECTORY "$<TARGET_PROPERTY:${target},SOURCE_DIR>" ) @@ -935,28 +983,54 @@ function(_qt_internal_add_all_qmllint_target target_var default_target_name lint if("${target_name}" STREQUAL "") set(target_name ${default_target_name}) endif() - if(NOT TARGET ${target_name}) - add_custom_target(${target_name}) - _qt_internal_assign_to_qmllint_targets_folder(${target_name}) - endif() if(CMAKE_GENERATOR MATCHES "^Visual Studio ") # For the Visual Studio generators we cannot use add_dependencies, because this would enable # ${lint_target} in the default build of the solution. See QTBUG-115166 and upstream CMake - # issue #16668 for details. As a work-around, we run the ${lint_target} through 'cmake - # --build' as PRE_BUILD step of the all_qmllint target. - add_custom_command( - TARGET ${target_name} PRE_BUILD - COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" -t ${lint_target} - ) + # issue #16668 for details. Instead, we record ${lint_target} and create an all_qmllint + # target at the end of the top-level directory scope. + if(${CMAKE_VERSION} VERSION_LESS "3.19.0") + if(NOT QT_NO_QMLLINT_CREATION_WARNING) + message(WARNING "Cannot create target ${target_name} with this CMake version. " + "Please upgrade to CMake 3.19.0 or newer. " + "Set QT_NO_QMLLINT_CREATION_WARNING to ON to disable this warning." + ) + endif() + return() + endif() + set(property_name _qt_target_${target_name}_dependencies) + get_property(recorded_targets GLOBAL PROPERTY ${property_name}) + if("${recorded_targets}" STREQUAL "") + cmake_language(EVAL CODE + "cmake_language(DEFER DIRECTORY \"${CMAKE_SOURCE_DIR}\" CALL _qt_internal_add_all_qmllint_target_deferred \"${target_name}\")" + ) + endif() + set_property(GLOBAL APPEND PROPERTY ${property_name} ${lint_target}) # Exclude ${lint_target} from the solution's default build to avoid it being enabled should # the user add a dependency to it. set_property(TARGET ${lint_target} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD ON) else() + if(NOT TARGET ${target_name}) + add_custom_target(${target_name}) + _qt_internal_assign_to_qmllint_targets_folder(${target_name}) + endif() add_dependencies(${target_name} ${lint_target}) endif() endfunction() +# Hack for the Visual Studio generator. Create the all_qmllint target named ${target} and work +# around the lack of a working add_dependencies by calling 'cmake --build' for every dependency. +function(_qt_internal_add_all_qmllint_target_deferred target) + get_property(target_dependencies GLOBAL PROPERTY _qt_target_${target}_dependencies) + set(target_commands "") + foreach(dependency IN LISTS target_dependencies) + list(APPEND target_commands + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" -t ${dependency} + ) + endforeach() + add_custom_target(${target} ${target_commands}) + _qt_internal_assign_to_qmllint_targets_folder(${target}) +endfunction() function(_qt_internal_target_enable_qmlcachegen target output_targets_var qmlcachegen) set(output_targets) @@ -1507,6 +1581,35 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) endfunction() endif() +function(_qt_internal_set_qml_target_multi_config_output_directory target output_directory) + # In multi-config builds we need to make sure that at least one configuration has the dynamic + # plugin that is located next to qmldir file, otherwise QML engine won't be able to load the + # plugin. + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + # We don't care about static plugins here since, they are linked at build time and + # their location doesn't affect the runtime. + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL "SHARED_LIBRARY" OR target_type STREQUAL "MODULE_LIBRARY") + if(NOT "${output_directory}") + set(output_directory "${CMAKE_CURRENT_BINARY_DIR}") + endif() + + list(GET CMAKE_CONFIGURATION_TYPES 0 default_config) + string(JOIN "" output_directory_with_default_config + "$<IF:$<CONFIG:${default_config}>," + "${output_directory}," + "${output_directory}/$<CONFIG>" + ">" + ) + set_target_properties(${target} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${output_directory_with_default_config}" + LIBRARY_OUTPUT_DIRECTORY "${output_directory_with_default_config}" + ) + endif() + endif() +endfunction() + function(qt6_add_qml_plugin target) set(args_option STATIC @@ -1752,6 +1855,8 @@ function(qt6_add_qml_plugin target) ) endif() + _qt_internal_set_qml_target_multi_config_output_directory(${target} "${arg_OUTPUT_DIRECTORY}") + if(NOT arg_NO_GENERATE_PLUGIN_SOURCE) set(generated_cpp_file_name_base "${target}_${arg_CLASS_NAME}") set(register_types_function_name "qml_register_types_${escaped_uri}") @@ -1848,8 +1953,9 @@ function(qt6_target_qml_sources target) message(FATAL_ERROR "Unknown/unexpected arguments: ${arg_UNPARSED_ARGUMENTS}") endif() - if (NOT arg_QML_FILES AND NOT arg_RESOURCES) - if(NOT arg_NO_LINT) + get_target_property(no_lint ${target} QT_QML_MODULE_NO_LINT) + if(NOT arg_QML_FILES AND NOT arg_RESOURCES) + if(NOT arg_NO_LINT AND NOT no_lint) _qt_internal_target_enable_qmllint(${target}) endif() @@ -1868,7 +1974,6 @@ function(qt6_target_qml_sources target) ) endif() - get_target_property(no_lint ${target} QT_QML_MODULE_NO_LINT) get_target_property(no_cachegen ${target} QT_QML_MODULE_NO_CACHEGEN) get_target_property(no_qmldir ${target} QT_QML_MODULE_NO_GENERATE_QMLDIR) get_target_property(resource_prefix ${target} QT_QML_MODULE_RESOURCE_PREFIX) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 78c82a3071..92847a02ad 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -670,6 +670,8 @@ struct Binding } bool evaluatesToString() const { return type() == Type_String || isTranslationBinding(); } + bool isNumberBinding() const { return type() == Type_Number; } + bool valueAsBoolean() const { if (type() == Type_Boolean) diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index c2a4e88fd8..998f3b43d9 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -455,7 +455,7 @@ Declares that any \l QML_ELEMENT, \l QML_NAMED_ELEMENT(), \l QML_ANONYMOUS, \l QML_INTERFACE, \l QML_UNCREATABLE(), \l QML_SINGLETON, \l QML_ADDED_IN_MINOR_VERSION(), \l QML_REMOVED_IN_MINOR_VERSION(), - \l QML_ATTACHED(), \l QML_EXTENDED(), or \l QML_EXTENDED_NAMESPACE() macros + \l QML_EXTENDED(), or \l QML_EXTENDED_NAMESPACE() macros in the enclosing C++ type do not apply to the enclosing type but instead to \a FOREIGN_TYPE. The enclosing type still needs to be registered with the \l {The Meta-Object System}{meta object system} using a \l Q_GADGET or @@ -469,6 +469,9 @@ the element will be named like the struct it is contained in, not the foreign type. See the \l {Extension Objects} for an example. + \note QML_ATTACHED() can currently not be redirected like this. It has to be + specificed in the same type that implements qmlAttachedProperties(). + \include {qualified-class-name.qdocinc} {class name must be qualified} \sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_FOREIGN_NAMESPACE() diff --git a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc index 139f7377d4..548cd78f20 100644 --- a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc @@ -254,13 +254,23 @@ The same declaration can also be given for C++-defined types. See \section2 ComponentBehavior -With this pragma you can restrict components defined in this file to only -create objects within their original context. This holds for inline -components as well as Component elements explicitly or implicitly created -as properties. If a component is bound to its context, you can safely -use IDs from the rest of the file within the component. Otherwise, the -engine and the QML tooling cannot know in advance what type, if any, such -IDs will resolve to at run time. +You may have multiple components defined in the same QML file. The root +scope of the QML file is a component, and you may additionally have +elements of type \l QQmlComponent, explicitly or implicitly created +as properties, or inline components. Those components are nested. Each +of the inner components is within one specific outer component. Most of +the time, IDs defined in an outer component are accessible within all +its nested inner components. You can, however, create elements from a +component in any a different context, with different IDs available. +Doing so breaks the assumption that outer IDs are available. Therefore, +the engine and the QML tooling cannot generally know in advance what +type, if any, such IDs will resolve to at run time. + +With the ComponentBehavior pragma you can restrict all inner components +defined in a file to only create objects within their original context. +If a component is bound to its context, you can safely use IDs from +outer components in the same file within the component. QML tooling will +then assume the outer IDs with their specific types to be available. In order to bind the components to their context specify the \c{Bound} argument: @@ -269,8 +279,33 @@ argument: pragma ComponentBehavior: Bound \endqml -The default is \c{Unbound}. You can also specify it explicitly. In a -future version of Qt the default will change to \c{Bound}. +This implies that, in case of name clashes, IDs defined outside a bound +component override local properties of objects created from the +component. Otherwise it wouldn't actually be safe to use the IDs since +later versions of a module might add more properties to the component. +If the component is not bound, local properties override IDs defined +outside the component, but not IDs defined inside the component. + +The example below prints the \e r property of the ListView object with +the id \e color, not the \e r property of the rectangle's color. + +\qml +pragma ComponentBehavior: Bound +import QtQuick + +ListView { + id: color + property int r: 12 + model: 1 + delegate: Rectangle { + Component.onCompleted: console.log(color.r) + } +} +\endqml + +The default value of \c ComponentBehavior is \c{Unbound}. You can also +specify it explicitly. In a future version of Qt the default will change +to \c{Bound}. Delegate components bound to their context don't receive their own private contexts on instantiation. This means that model data can only diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 4d52842e48..36d1db4c46 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -807,7 +807,7 @@ In order to be consistent with method declarations, you should prefer the type declarations using colons. If the signal has no parameters, the "()" brackets are optional. If parameters -are used, the parameter types must be declared, as for the \c string and \c var +are used, the parameter types must be declared, as for the \c string and \c int arguments for the \c actionPerformed signal above. The allowed parameter types are the same as those listed under \l {Defining Property Attributes} on this page. diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index dd3bb98002..f622c3fb23 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -372,8 +372,11 @@ bool QJSValue::isError() const } /*! - Returns true if this QJSValue is an object of the URL class; + Returns true if this QJSValue is an object of the URL JavaScript class; otherwise returns false. + + \note For a QJSValue that contains a QUrl, this function returns false. + However, \c{toVariant().value<QUrl>()} works in both cases. */ bool QJSValue::isUrl() const { @@ -635,8 +638,11 @@ QVariant QJSValue::toVariant(QJSValue::ObjectConversionBehavior behavior) const if (val.isString()) return QVariant(val.toQString()); if (val.as<QV4::Managed>()) { - return QV4::ExecutionEngine::toVariant( - val, /*typeHint*/ QMetaType{}, behavior == RetainJSObjects); + if (behavior == RetainJSObjects) + return QV4::ExecutionEngine::toVariant( + val, /*typeHint*/ QMetaType{}, /*createJSValueForObjectsAndSymbols=*/ true); + else + return QV4::ExecutionEngine::toVariantLossy(val); } Q_ASSERT(false); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 1cc16bb53a..1675138d4d 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1478,11 +1478,13 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() // Variant conversion code typedef QSet<QV4::Heap::Object *> V4ObjectSet; +enum class JSToQVariantConversionBehavior {Never, Safish, Aggressive }; static QVariant toVariant( - const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols, + const QV4::Value &value, QMetaType typeHint, JSToQVariantConversionBehavior conversionBehavior, V4ObjectSet *visitedObjects); static QObject *qtObjectFromJS(const QV4::Value &value); -static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr); +static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr, + JSToQVariantConversionBehavior behavior = JSToQVariantConversionBehavior::Safish); static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result); static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst); static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst); @@ -1492,8 +1494,7 @@ static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant & return v4->metaTypeToJS(value.metaType(), value.constData()); } -static QVariant toVariant( - const QV4::Value &value, QMetaType metaType, bool createJSValueForObjectsAndSymbols, +static QVariant toVariant(const QV4::Value &value, QMetaType metaType, JSToQVariantConversionBehavior conversionBehavior, V4ObjectSet *visitedObjects) { Q_ASSERT (!value.isEmpty()); @@ -1590,7 +1591,7 @@ static QVariant toVariant( } } - asVariant = toVariant(arrayValue, valueMetaType, false, visitedObjects); + asVariant = toVariant(arrayValue, valueMetaType, JSToQVariantConversionBehavior::Never, visitedObjects); if (valueMetaType == QMetaType::fromType<QVariant>()) { retnAsIterable.metaContainer().addValue(retn.data(), &asVariant); } else { @@ -1655,7 +1656,7 @@ static QVariant toVariant( if (const ArrayBuffer *d = value.as<ArrayBuffer>()) return d->asByteArray(); if (const Symbol *symbol = value.as<Symbol>()) { - return createJSValueForObjectsAndSymbols + return conversionBehavior == JSToQVariantConversionBehavior::Never ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue())) : symbol->descriptiveString(); } @@ -1676,20 +1677,27 @@ static QVariant toVariant( return result; } - if (createJSValueForObjectsAndSymbols) + if (conversionBehavior == JSToQVariantConversionBehavior::Never) return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue())); - return objectToVariant(o, visitedObjects); + return objectToVariant(o, visitedObjects, conversionBehavior); } +QVariant ExecutionEngine::toVariantLossy(const Value &value) +{ + return ::toVariant(value, QMetaType(), JSToQVariantConversionBehavior::Aggressive, nullptr); +} QVariant ExecutionEngine::toVariant( const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols) { - return ::toVariant(value, typeHint, createJSValueForObjectsAndSymbols, nullptr); + auto behavior = createJSValueForObjectsAndSymbols ? JSToQVariantConversionBehavior::Never + : JSToQVariantConversionBehavior::Safish; + return ::toVariant(value, typeHint, behavior, nullptr); } -static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visitedObjects) +static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visitedObjects, + JSToQVariantConversionBehavior conversionBehvior) { QVariantMap map; QV4::Scope scope(o->engine()); @@ -1704,12 +1712,13 @@ static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visited QString key = name->toQStringNoThrow(); map.insert(key, ::toVariant( val, /*type hint*/ QMetaType {}, - /*createJSValueForObjectsAndSymbols*/false, visitedObjects)); + conversionBehvior, visitedObjects)); } return map; } -static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects) +static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects, + JSToQVariantConversionBehavior conversionBehvior) { Q_ASSERT(o); @@ -1737,13 +1746,20 @@ static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObject int length = a->getLength(); for (int ii = 0; ii < length; ++ii) { v = a->get(ii); - list << ::toVariant(v, QMetaType {}, /*createJSValueForObjectsAndSymbols*/false, + list << ::toVariant(v, QMetaType {}, conversionBehvior, visitedObjects); } result = list; - } else if (o->getPrototypeOf() == o->engine()->objectPrototype()->d()) { - result = objectToVariantMap(o, visitedObjects); + } else if (o->getPrototypeOf() == o->engine()->objectPrototype()->d() + || (conversionBehvior == JSToQVariantConversionBehavior::Aggressive && + !o->as<QV4::FunctionObject>())) { + /* FunctionObject is excluded for historical reasons, even though + objects with a custom prototype risk losing information + But the Aggressive path is used only in QJSValue::toVariant + which is documented to be lossy + */ + result = objectToVariantMap(o, visitedObjects, conversionBehvior); } else { // If it's not a plain object, we can only save it as QJSValue. result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue())); @@ -1974,7 +1990,7 @@ QVariantMap ExecutionEngine::variantMapFromJS(const Object *o) Q_ASSERT(o); V4ObjectSet visitedObjects; visitedObjects.insert(o->d()); - return objectToVariantMap(o, &visitedObjects); + return objectToVariantMap(o, &visitedObjects, JSToQVariantConversionBehavior::Safish); } diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 23be23aee2..75c8efd67e 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -644,6 +644,7 @@ public: // variant conversions static QVariant toVariant( const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols = true); + static QVariant toVariantLossy(const QV4::Value &value); QV4::ReturnedValue fromVariant(const QVariant &); QV4::ReturnedValue fromVariant( const QVariant &variant, Heap::Object *parent, int property, uint flags); diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index b17e4f14a8..347613006a 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -195,6 +195,11 @@ public: return data->flags & CompiledData::Unit::ValueTypesCopied; } + bool componentsAreBound() const + { + return data->flags & CompiledData::Unit::ComponentsBound; + } + int objectCount() const { return qmlData->nObjects; } const CompiledObject *objectAt(int index) const { diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 8279fa00f5..d15fb356c4 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -191,6 +191,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags) struct PropertyAttributes { + QT_WARNING_PUSH + QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union union { uchar m_all; struct { @@ -208,6 +210,7 @@ struct PropertyAttributes uchar configurable_set : 1; }; }; + QT_WARNING_POP enum Type { Data = 0, diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 4126a25758..bb8b385a88 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -155,10 +155,13 @@ ReturnedValue convertAndCall( values[0] = nullptr; } - if (const QV4::QObjectWrapper *cppThisObject = thisObject->as<QV4::QObjectWrapper>()) + if (const QV4::QObjectWrapper *cppThisObject = thisObject + ? thisObject->as<QV4::QObjectWrapper>() + : nullptr) { call(cppThisObject->object(), values, types, argc); - else + } else { call(nullptr, values, types, argc); + } ReturnedValue result; if (values[0]) { @@ -255,6 +258,156 @@ ReturnedValue coerceAndCall( return result->asReturnedValue(); } +// Note: \a to is unininitialized here! This is in contrast to most other related functions. +inline void coerce( + ExecutionEngine *engine, QMetaType fromType, const void *from, QMetaType toType, void *to) +{ + if ((fromType.flags() & QMetaType::PointerToQObject) + && (toType.flags() & QMetaType::PointerToQObject)) { + QObject *fromObj = *static_cast<QObject * const*>(from); + *static_cast<QObject **>(to) + = (fromObj && fromObj->metaObject()->inherits(toType.metaObject())) + ? fromObj + : nullptr; + return; + } + + if (toType == QMetaType::fromType<QVariant>()) { + new (to) QVariant(fromType, from); + return; + } + + if (toType == QMetaType::fromType<QJSPrimitiveValue>()) { + new (to) QJSPrimitiveValue(fromType, from); + return; + } + + if (fromType == QMetaType::fromType<QVariant>()) { + const QVariant *fromVariant = static_cast<const QVariant *>(from); + if (fromVariant->metaType() == toType) + toType.construct(to, fromVariant->data()); + else + coerce(engine, fromVariant->metaType(), fromVariant->data(), toType, to); + return; + } + + // TODO: This is expensive. We might establish a direct C++-to-C++ type coercion, like we have + // will generate code that passes the right arguments. + // for JS-to-JS. However, we shouldn't need this very often. Most of the time the compiler + if (toType.flags() & QMetaType::NeedsConstruction) + toType.construct(to); + QV4::Scope scope(engine); + QV4::ScopedValue value(scope, engine->fromData(fromType, from)); + if (!ExecutionEngine::metaTypeFromJS(value, toType, to)) + QMetaType::convert(fromType, from, toType, to); +} + +template<typename TypedFunction, typename Callable> +void coerceAndCall( + ExecutionEngine *engine, const TypedFunction *typedFunction, + void **argv, const QMetaType *types, int argc, Callable call) +{ + const qsizetype numFunctionArguments = typedFunction->parameterCount(); + + Q_ALLOCA_DECLARE(void *, transformedArguments); + Q_ALLOCA_DECLARE(void, transformedResult); + + const QMetaType returnType = typedFunction->returnMetaType(); + const QMetaType frameReturn = types[0]; + bool returnsQVariantWrapper = false; + if (argv[0] && returnType != frameReturn) { + Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); + memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *)); + + if (frameReturn == QMetaType::fromType<QVariant>()) { + QVariant *returnValue = static_cast<QVariant *>(argv[0]); + *returnValue = QVariant(returnType); + transformedResult = transformedArguments[0] = returnValue->data(); + returnsQVariantWrapper = true; + } else if (returnType.sizeOf() > 0) { + Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf()); + transformedArguments[0] = transformedResult; + if (returnType.flags() & QMetaType::NeedsConstruction) + returnType.construct(transformedResult); + } else { + transformedResult = transformedArguments[0] = &argc; // Some non-null marker value + } + } + + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + const bool isValid = argc > i; + const QMetaType frameType = isValid ? types[i + 1] : QMetaType(); + + const QMetaType argumentType = typedFunction->parameterMetaType(i); + if (isValid && argumentType == frameType) + continue; + + if (transformedArguments == nullptr) { + Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); + memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *)); + } + + if (argumentType.sizeOf() == 0) { + transformedArguments[i + 1] = nullptr; + continue; + } + + void *frameVal = isValid ? argv[i + 1] : nullptr; + if (isValid && frameType == QMetaType::fromType<QVariant>()) { + QVariant *variant = static_cast<QVariant *>(frameVal); + + const QMetaType variantType = variant->metaType(); + if (variantType == argumentType) { + // Slightly nasty, but we're allowed to do this. + // We don't want to destruct() the QVariant's data() below. + transformedArguments[i + 1] = argv[i + 1] = variant->data(); + } else { + Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); + coerce(engine, variantType, variant->constData(), argumentType, arg); + transformedArguments[i + 1] = arg; + } + continue; + } + + Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); + + if (isValid) + coerce(engine, frameType, frameVal, argumentType, arg); + else + argumentType.construct(arg); + + transformedArguments[i + 1] = arg; + } + + if (!transformedArguments) { + call(argv, numFunctionArguments); + return; + } + + call(transformedArguments, numFunctionArguments); + + if (transformedResult && !returnsQVariantWrapper) { + if (frameReturn.sizeOf() > 0) { + if (frameReturn.flags() & QMetaType::NeedsDestruction) + frameReturn.destruct(argv[0]); + coerce(engine, returnType, transformedResult, frameReturn, argv[0]); + } + if (returnType.flags() & QMetaType::NeedsDestruction) + returnType.destruct(transformedResult); + } + + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + void *arg = transformedArguments[i + 1]; + if (arg == nullptr) + continue; + if (i >= argc || arg != argv[i + 1]) { + const QMetaType argumentType = typedFunction->parameterMetaType(i); + if (argumentType.flags() & QMetaType::NeedsDestruction) + argumentType.destruct(arg); + } + } +} + } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index d58a712147..712666c6aa 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -39,8 +39,11 @@ void Object::setInternalClass(Heap::InternalClass *ic) // Pick the members of the old IC that are still valid in the new IC. // Order them by index in memberData (or inline data). Scoped<MemberData> newMembers(scope, MemberData::allocate(scope.engine, ic->size)); - for (uint i = 0; i < ic->size; ++i) - newMembers->set(scope.engine, i, get(ic->nameMap.at(i))); + for (uint i = 0; i < ic->size; ++i) { + // Note that some members might have been deleted. The key may be invalid. + const PropertyKey key = ic->nameMap.at(i); + newMembers->set(scope.engine, i, key.isValid() ? get(key) : Encode::undefined()); + } p->internalClass.set(scope.engine, ic); const uint nInline = p->vtable()->nInlineProperties; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 3f5629a693..babd4d4833 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -268,9 +268,33 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty; } + QQmlRefPointer<QQmlContextData> outer = context; while (context) { - if (auto property = searchContextProperties(v4, context, name, hasProperty, base, lookup, originalLookup, ep)) - return *property; + if (outer == context) { + if (auto property = searchContextProperties( + v4, context, name, hasProperty, base, lookup, originalLookup, ep)) { + return *property; + } + + outer = outer->parent(); + + if (const auto cu = context->typeCompilationUnit(); cu && cu->componentsAreBound()) { + // If components are bound in this CU, we can search the whole context hierarchy + // of the file. Bound components' contexts override their local properties. + // You also can't instantiate bound components outside of their creation + // context. Therefore this is safe. + + for (; + outer && outer->typeCompilationUnit() == cu; + outer = outer->parent()) { + if (auto property = searchContextProperties( + v4, outer, name, hasProperty, base, + nullptr, originalLookup, ep)) { + return *property; + } + } + } + } // Search scope object if (scopeObject) { diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 967e359785..50389e06ec 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1100,7 +1100,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase if (!v4) break; - QQmlMetaObject::ArgTypeStorage storage; + QQmlMetaObject::ArgTypeStorage<9> storage; QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr); int argCount = storage.size(); @@ -1382,9 +1382,7 @@ void QObjectWrapper::destroyObject(bool lastCall) if (!o->parent() && !ddata->indestructible) { if (ddata && ddata->ownContext) { Q_ASSERT(ddata->ownContext.data() == ddata->context); - ddata->ownContext->emitDestruction(); - if (ddata->ownContext->contextObject() == o) - ddata->ownContext->setContextObject(nullptr); + ddata->ownContext->deepClearContextObject(o); ddata->ownContext.reset(); ddata->context = nullptr; } @@ -1803,7 +1801,7 @@ static ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPro if (data.hasArguments()) { - QQmlMetaObject::ArgTypeStorage storage; + QQmlMetaObject::ArgTypeStorage<9> storage; bool ok = false; if (data.isConstructor()) @@ -1884,7 +1882,7 @@ static const QQmlPropertyData *ResolveOverloaded( int sumMethodMatchScore = bestSumMatchScore; if (!attempt->isV4Function()) { - QQmlMetaObject::ArgTypeStorage storage; + QQmlMetaObject::ArgTypeStorage<9> storage; int methodArgumentCount = 0; if (attempt->hasArguments()) { if (attempt->isConstructor()) { diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index 9c3e3700ce..c78c1e10b2 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -35,6 +35,8 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrameBase int originalArgumentsCount; int instructionPointer; + QT_WARNING_PUSH + QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union union { struct { Value *savedStackTop; @@ -57,6 +59,7 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrameBase bool returnValueIsUndefined; }; }; + QT_WARNING_POP Kind kind; }; diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 93b2b55375..809693a01b 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -1255,7 +1255,8 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType storeResult = resetObjectProperty(&l, qmlScopeObject); } else { QVariant var(propType); - propType.convert(type, value, propType, var.data()); + QV4::ExecutionEngine *v4 = engine->handle(); + v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data()); storeResult = storeObjectProperty(&l, qmlScopeObject, var.data()); } @@ -1273,7 +1274,8 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType storeResult = resetFallbackProperty(&l, qmlScopeObject); } else { QVariant var(propType); - propType.convert(type, value, propType, var.data()); + QV4::ExecutionEngine *v4 = engine->handle(); + v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data()); storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data()); } break; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index ee6296af0c..2aa6aad840 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -179,7 +179,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) QMetaObjectPrivate::signal(targetMeta, m_index).methodIndex()); int argCount = metaMethod.parameterCount(); - QQmlMetaObject::ArgTypeStorage storage; + QQmlMetaObject::ArgTypeStorage<9> storage; storage.reserve(argCount + 1); storage.append(QMetaType()); // We're not interested in the return value for (int i = 0; i < argCount; ++i) { diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp index 00e3fdd0ed..69b7d78484 100644 --- a/src/qml/qml/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/qqmlbuiltinfunctions.cpp @@ -1754,19 +1754,21 @@ enum ConsoleLogTypes { static QString jsStack(QV4::ExecutionEngine *engine) { QString stack; - QVector<QV4::StackFrame> stackTrace = engine->stackTrace(10); - - for (int i = 0; i < stackTrace.size(); i++) { - const QV4::StackFrame &frame = stackTrace.at(i); - + int i = 0; + for (CppStackFrame *f = engine->currentStackFrame; f && i < 10; f = f->parentFrame(), ++i) { QString stackFrame; - if (frame.column >= 0) { - stackFrame = QStringLiteral("%1 (%2:%3:%4)").arg( - frame.function, frame.source, - QString::number(qAbs(frame.line)), QString::number(frame.column)); + + if (f->isJSTypesFrame() && static_cast<JSTypesStackFrame *>(f)->isTailCalling()) { + stackFrame = QStringLiteral("[elided tail calls]"); } else { - stackFrame = QStringLiteral("%1 (%2:%3)").arg( - frame.function, frame.source, QString::number(qAbs(frame.line))); + const int line = f->lineNumber(); + if (line != f->missingLineNumber()) { + stackFrame = QStringLiteral("%1 (%2:%3)").arg( + f->function(), f->source(), QString::number(qAbs(line))); + } else { + stackFrame = QStringLiteral("%1 (%2)").arg( + f->function(), f->source()); + } } if (i) diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 09c3965c2a..0ddd717974 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -166,10 +166,7 @@ public: QObject *createWithProperties(QObject *parent, const QVariantMap &properties, QQmlContext *context, CreateBehavior behavior = CreateDefault); - bool isBound() const { - return compilationUnit - && (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound); - } + bool isBound() const { return compilationUnit && (compilationUnit->componentsAreBound()); } }; QQmlComponentPrivate::ConstructionState::~ConstructionState() diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h index a541079503..f5d54c8fd2 100644 --- a/src/qml/qml/qqmlcontextdata_p.h +++ b/src/qml/qml/qqmlcontextdata_p.h @@ -128,6 +128,28 @@ public: QObject *contextObject() const { return m_contextObject; } void setContextObject(QObject *contextObject) { m_contextObject = contextObject; } + template<typename HandleSelf, typename HandleLinked> + void deepClearContextObject( + QObject *contextObject, HandleSelf &&handleSelf, HandleLinked &&handleLinked) { + for (QQmlContextData *lc = m_linkedContext.data(); lc; lc = lc->m_linkedContext.data()) { + handleLinked(lc); + if (lc->m_contextObject == contextObject) + lc->m_contextObject = nullptr; + } + + handleSelf(this); + if (m_contextObject == contextObject) + m_contextObject = nullptr; + } + + void deepClearContextObject(QObject *contextObject) + { + deepClearContextObject( + contextObject, + [](QQmlContextData *self) { self->emitDestruction(); }, + [](QQmlContextData *){}); + } + QQmlEngine *engine() const { return m_engine; } void setEngine(QQmlEngine *engine) { m_engine = engine; } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 723b93af40..015a00d53b 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -215,23 +215,16 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) { QObjectPrivate *p = QObjectPrivate::get(o); if (QQmlData *d = QQmlData::get(p)) { + const auto invalidate = [](QQmlContextData *c) {c->invalidate();}; if (d->ownContext) { - for (QQmlRefPointer<QQmlContextData> lc = d->ownContext->linkedContext(); lc; - lc = lc->linkedContext()) { - lc->invalidate(); - if (lc->contextObject() == o) - lc->setContextObject(nullptr); - } - d->ownContext->invalidate(); - if (d->ownContext->contextObject() == o) - d->ownContext->setContextObject(nullptr); + d->ownContext->deepClearContextObject(o, invalidate, invalidate); d->ownContext.reset(); d->context = nullptr; + Q_ASSERT(!d->outerContext || d->outerContext->contextObject() != o); + } else if (d->outerContext && d->outerContext->contextObject() == o) { + d->outerContext->deepClearContextObject(o, invalidate, invalidate); } - if (d->outerContext && d->outerContext->contextObject() == o) - d->outerContext->setContextObject(nullptr); - if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) { // This is somewhat dangerous because another thread might concurrently // try to resolve the dynamic metaobject. In practice this will then @@ -407,9 +400,7 @@ void QQmlData::setQueuedForDeletion(QObject *object) if (QQmlData *ddata = QQmlData::get(object)) { if (ddata->ownContext) { Q_ASSERT(ddata->ownContext.data() == ddata->context); - ddata->context->emitDestruction(); - if (ddata->ownContext->contextObject() == object) - ddata->ownContext->setContextObject(nullptr); + ddata->ownContext->deepClearContextObject(object); ddata->ownContext.reset(); ddata->context = nullptr; } diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp index 319accb768..441fc6200d 100644 --- a/src/qml/qml/qqmlextensionplugin.cpp +++ b/src/qml/qml/qqmlextensionplugin.cpp @@ -166,8 +166,20 @@ void QQmlEngineExtensionPlugin::initializeEngine(QQmlEngine *engine, const char \since 6.2 \relates QQmlEngineExtensionPlugin - Ensures the plugin whose metadata-declaring class is named \a PluginName - is linked into static builds. + Ensures the plugin whose metadata-declaring plugin extension class is named + \a PluginName is linked into static builds. For the modules created using + \l qt_add_qml_module, the default plugin extension class name is computed + from the QML module URI by replacing dots with underscores, unless the + \c CLASS_NAME argument is specified. + + For example: + \badcode + qt_add_qml_module(myplugin + # The plugin extension class name in this case is my_Company_QmlComponents. + URI my.Company.QmlComponents + ... + ) + \endcode \sa Q_IMPORT_PLUGIN */ diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp index e84c5a366e..8de773f7dc 100644 --- a/src/qml/qml/qqmlmetaobject.cpp +++ b/src/qml/qml/qqmlmetaobject.cpp @@ -78,42 +78,4 @@ QMetaType QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteAr return QMetaType(); } -bool QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const -{ - Q_ASSERT(_m && index >= 0); - - QMetaMethod m = _m->method(index); - return methodParameterTypes(m, argStorage, unknownTypeError); -} - -bool QQmlMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy, - QByteArray *unknownTypeError) const -{ - QMetaMethod m = _m->constructor(index); - return methodParameterTypes(m, dummy, unknownTypeError); -} - -bool QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) -{ - Q_ASSERT(argStorage); - - int argc = m.parameterCount(); - argStorage->resize(argc); - for (int ii = 0; ii < argc; ++ii) { - QMetaType type = m.parameterMetaType(ii); - // we treat enumerations as int - if (type.flags().testFlag(QMetaType::IsEnumeration)) - type = QMetaType::fromType<int>(); - if (!type.isValid()) { - if (unknownTypeError) - *unknownTypeError = m.parameterTypeName(ii); - return false; - } - argStorage->operator[](ii) = type; - } - return true; -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetaobject_p.h b/src/qml/qml/qqmlmetaobject_p.h index cc3cc6af79..01164bad71 100644 --- a/src/qml/qml/qqmlmetaobject_p.h +++ b/src/qml/qml/qqmlmetaobject_p.h @@ -34,7 +34,8 @@ class QQmlPropertyData; class Q_QML_EXPORT QQmlMetaObject { public: - typedef QVarLengthArray<QMetaType, 9> ArgTypeStorage; + template<qsizetype Prealloc> + using ArgTypeStorage = QVarLengthArray<QMetaType, Prealloc>; inline QQmlMetaObject() = default; inline QQmlMetaObject(const QObject *); @@ -52,30 +53,116 @@ public: inline const QMetaObject *metaObject() const; QMetaType methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const; + /*! \internal Returns false if one of the types is unknown. Otherwise, fills \a argstorage with the metatypes of the function. */ - bool methodParameterTypes(int index, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const; + template<typename ArgTypeStorage> + bool methodParameterTypes( + int index, ArgTypeStorage *argStorage, QByteArray *unknownTypeError) const + { + Q_ASSERT(_m && index >= 0); + + QMetaMethod m = _m->method(index); + return methodParameterTypes(m, argStorage, unknownTypeError); + } + /*! \internal Returns false if one of the types is unknown. Otherwise, fills \a argstorage with the metatypes of the function. */ - bool constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; + template<typename ArgTypeStorage> + bool constructorParameterTypes( + int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const + { + QMetaMethod m = _m->constructor(index); + return methodParameterTypes(m, dummy, unknownTypeError); + } static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); // static_metacall (on Gadgets) doesn't call the base implementation and therefore // we need a helper to find the correct meta object and property/method index. - static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index); + static void resolveGadgetMethodOrPropertyIndex( + QMetaObject::Call type, const QMetaObject **metaObject, int *index); + + template<typename ArgTypeStorage> + static bool methodParameterTypes( + const QMetaMethod &method, ArgTypeStorage *argStorage, QByteArray *unknownTypeError) + { + Q_ASSERT(argStorage); + + const int argc = method.parameterCount(); + argStorage->resize(argc); + for (int ii = 0; ii < argc; ++ii) { + if (!parameterType(method, ii, unknownTypeError, [argStorage](int ii, QMetaType &&type) { + argStorage->operator[](ii) = std::forward<QMetaType>(type); + })) { + return false; + } + } + return true; + } + + template<typename ArgTypeStorage> + static bool methodReturnAndParameterTypes( + const QMetaMethod &method, ArgTypeStorage *argStorage, QByteArray *unknownTypeError) + { + Q_ASSERT(argStorage); + + const int argc = method.parameterCount(); + argStorage->resize(argc + 1); + + QMetaType type = method.returnMetaType(); + if (type.flags().testFlag(QMetaType::IsEnumeration)) + type = QMetaType::fromType<int>(); + + if (!type.isValid()) { + if (unknownTypeError) + *unknownTypeError = "return type"; + return false; + } + + argStorage->operator[](0) = type; + + for (int ii = 0; ii < argc; ++ii) { + if (!parameterType( + method, ii, unknownTypeError, [argStorage](int ii, QMetaType &&type) { + argStorage->operator[](ii + 1) = std::forward<QMetaType>(type); + })) { + return false; + } + } + + return true; + } - static bool methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError); protected: + template<typename Store> + static bool parameterType( + const QMetaMethod &method, int ii, QByteArray *unknownTypeError, const Store &store) + { + QMetaType type = method.parameterMetaType(ii); + + // we treat enumerations as int + if (type.flags().testFlag(QMetaType::IsEnumeration)) + type = QMetaType::fromType<int>(); + + if (!type.isValid()) { + if (unknownTypeError) + *unknownTypeError = method.parameterTypeName(ii); + return false; + } + + store(ii, std::move(type)); + return true; + } + + const QMetaObject *_m = nullptr; }; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 582c89863f..80db703e73 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -154,7 +154,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI } else { Q_ASSERT(subComponentIndex >= 0); if (flags & CreationFlags::InlineComponent) { - if (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound + if (compilationUnit->componentsAreBound() && compilationUnit != parentContext->typeCompilationUnit()) { recordError({}, tr("Cannot instantiate bound inline component in different file")); phase = ObjectsCreated; @@ -164,7 +164,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI isComponentRoot = true; } else { Q_ASSERT(flags & CreationFlags::NormalObject); - if (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound + if (compilationUnit->componentsAreBound() && sharedState->creationContext != parentContext) { recordError({}, tr("Cannot instantiate bound component " "outside its creation context")); @@ -313,7 +313,10 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QMetaType propertyType = property->propType(); if (property->isEnum()) { - if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) { + if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum) || + // TODO: For historical reasons you can assign any number to an enum property alias + // This can be fixed with an opt-out mechanism, for example a pragma. + (property->isAlias() && binding->isNumberBinding())) { propertyType = QMetaType::fromType<int>(); } else { // ### This should be resolved earlier at compile time and the binding value should be changed accordingly. diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index b201700d2d..7ebc602fff 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -428,13 +428,14 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name, if (result != end) *result = result->toUpper(); - qWarning() - << terminalString - << "is not a properly capitalized signal handler name." - << handlerName - << "would be correct."; - if (findSignalInMetaObject(signalName.toUtf8())) + if (findSignalInMetaObject(signalName.toUtf8())) { + qWarning() + << terminalString + << "is not a properly capitalized signal handler name." + << handlerName + << "would be correct."; return; + } } if (ddata && ddata->propertyCache) { diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index ac28dc8fbe..214c7093fa 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -864,10 +864,10 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor resettable = property->isResettable(); bindable = property->isBindable(); - // Copy type flags - propertyFlags->copyPropertyTypeFlags(property->flags()); if (property->isVarProperty()) propertyFlags->type = QQmlPropertyData::Flags::QVariantType; + else + propertyFlags->copyPropertyTypeFlags(property->flags()); }; // for deep aliases, valueTypeIndex is always set diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index c6783842dc..acb065b9d8 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -364,6 +364,11 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding( if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) return noError; + // TODO: For historical reasons you can assign any number to an enum property alias + // This can be fixed with an opt-out mechanism, for example a pragma. + if (property->isAlias() && binding->isNumberBinding()) + return noError; + QString value = compilationUnit->bindingValueAsString(binding); QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); bool ok; diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 1527a2ea34..03f911f2ff 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -3,16 +3,19 @@ #include "qqmlconnections_p.h" -#include <private/qqmlexpression_p.h> -#include <private/qqmlproperty_p.h> #include <private/qqmlboundsignal_p.h> -#include <qqmlcontext.h> #include <private/qqmlcontext_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qqmlproperty_p.h> #include <private/qqmlvmemetaobject_p.h> -#include <qqmlinfo.h> +#include <private/qv4jscall_p.h> +#include <private/qv4qobjectwrapper_p.h> + +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlinfo.h> -#include <QtCore/qloggingcategory.h> #include <QtCore/qdebug.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qstringlist.h> #include <private/qobject_p.h> @@ -21,10 +24,110 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQmlConnections, "qt.qml.connections") +// This is the equivalent of QQmlBoundSignal for C++ methods as as slots. +// If a type derived from QQmlConnnections is compiled using qmltc, the +// JavaScript functions it contains are turned into C++ methods and we cannot +// use QQmlBoundSignal to connect to those. +struct QQmlConnectionSlotDispatcher : public QtPrivate::QSlotObjectBase +{ + QV4::ExecutionEngine *v4 = nullptr; + QObject *receiver = nullptr; + + // Signals rarely have more than one argument. + QQmlMetaObject::ArgTypeStorage<2> signalMetaTypes; + QQmlMetaObject::ArgTypeStorage<2> slotMetaTypes; + + QMetaObject::Connection connection; + + int slotIndex = -1; + bool enabled = true; + + QQmlConnectionSlotDispatcher( + QV4::ExecutionEngine *v4, QObject *sender, int signalIndex, + QObject *receiver, int slotIndex, bool enabled) + : QtPrivate::QSlotObjectBase(&impl) + , v4(v4) + , receiver(receiver) + , slotIndex(slotIndex) + , enabled(enabled) + { + QMetaMethod signal = sender->metaObject()->method(signalIndex); + QQmlMetaObject::methodReturnAndParameterTypes(signal, &signalMetaTypes, nullptr); + + QMetaMethod slot = receiver->metaObject()->method(slotIndex); + QQmlMetaObject::methodReturnAndParameterTypes(slot, &slotMetaTypes, nullptr); + } + + template<typename ArgTypeStorage> + struct TypedFunction + { + Q_DISABLE_COPY_MOVE(TypedFunction) + public: + TypedFunction(const ArgTypeStorage *storage) : storage(storage) {} + + QMetaType returnMetaType() const { return storage->at(0); } + qsizetype parameterCount() const { return storage->size() - 1; } + QMetaType parameterMetaType(qsizetype i) const { return storage->at(i + 1); } + + private: + const ArgTypeStorage *storage; + }; + + static void impl(int which, QSlotObjectBase *base, QObject *, void **metaArgs, bool *ret) + { + switch (which) { + case Destroy: { + delete static_cast<QQmlConnectionSlotDispatcher *>(base); + break; + } + case Call: { + QQmlConnectionSlotDispatcher *self = static_cast<QQmlConnectionSlotDispatcher *>(base); + QV4::ExecutionEngine *v4 = self->v4; + if (!v4) + break; + + if (!self->enabled) + break; + + TypedFunction typedFunction(&self->slotMetaTypes); + QV4::coerceAndCall( + v4, &typedFunction, metaArgs, + self->signalMetaTypes.data(), self->signalMetaTypes.size() - 1, + [&](void **argv, int) { + self->receiver->metaObject()->metacall( + self->receiver, QMetaObject::InvokeMetaMethod, + self->slotIndex, argv); + }); + + if (v4->hasException) { + QQmlError error = v4->catchExceptionAsQmlError(); + if (QQmlEngine *qmlEngine = v4->qmlEngine()) { + QQmlEnginePrivate::get(qmlEngine)->warning(error); + } else { + QMessageLogger( + qPrintable(error.url().toString()), error.line(), nullptr) + .warning().noquote() + << error.toString(); + } + } + break; + } + case Compare: + // We're not implementing the Compare protocol here. It's insane. + // QQmlConnectionSlotDispatcher compares false to anything. We use + // the regular QObject::disconnect with QMetaObject::Connection. + *ret = false; + break; + case NumOperations: + break; + } + }; +}; + class QQmlConnectionsPrivate : public QObjectPrivate { public: - QList<QQmlBoundSignal*> boundsignals; + QList<QBiPointer<QQmlBoundSignal, QQmlConnectionSlotDispatcher>> boundsignals; QQmlGuard<QObject> target; bool enabled = true; @@ -105,6 +208,22 @@ QQmlConnections::QQmlConnections(QObject *parent) : { } +QQmlConnections::~QQmlConnections() +{ + Q_D(QQmlConnections); + + // The slot dispatchers hold cyclic references to their connections. Clear them. + for (const auto &bound : std::as_const(d->boundsignals)) { + if (QQmlConnectionSlotDispatcher *dispatcher = bound.isT2() ? bound.asT2() : nullptr) { + // No need to explicitly disconnect anymore since 'this' is the receiver. + // But to be safe, explicitly break any cyclic references between the connection + // and the slot object. + dispatcher->connection = {}; + dispatcher->destroyIfLastRef(); + } + } +} + /*! \qmlproperty QtObject QtQml::Connections::target This property holds the object that sends the signal. @@ -136,13 +255,19 @@ void QQmlConnections::setTarget(QObject *obj) if (d->targetSet && d->target == obj) return; d->targetSet = true; // even if setting to 0, it is *set* - for (QQmlBoundSignal *s : std::as_const(d->boundsignals)) { + for (const auto &bound : std::as_const(d->boundsignals)) { // It is possible that target is being changed due to one of our signal // handlers -> use deleteLater(). - if (s->isNotifying()) - (new QQmlBoundSignalDeleter(s))->deleteLater(); - else - delete s; + if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() : nullptr) { + if (signal->isNotifying()) + (new QQmlBoundSignalDeleter(signal))->deleteLater(); + else + delete signal; + } else { + QQmlConnectionSlotDispatcher *dispatcher = bound.asT2(); + QObject::disconnect(std::exchange(dispatcher->connection, {})); + dispatcher->destroyIfLastRef(); + } } d->boundsignals.clear(); d->target = obj; @@ -172,8 +297,12 @@ void QQmlConnections::setEnabled(bool enabled) d->enabled = enabled; - for (QQmlBoundSignal *s : std::as_const(d->boundsignals)) - s->setEnabled(d->enabled); + for (const auto &bound : std::as_const(d->boundsignals)) { + if (QQmlBoundSignal *signal = bound.isT1() ? bound.asT1() : nullptr) + signal->setEnabled(d->enabled); + else + bound.asT2()->enabled = enabled; + } emit enabledChanged(); } @@ -274,33 +403,41 @@ void QQmlConnections::connectSignalsToMethods() ++i) { const QQmlPropertyData *handler = ddata->propertyCache->method(i); - if (!handler || !handler->isVMEFunction()) + if (!handler) continue; const QString propName = handler->name(this); QQmlProperty prop(target, propName); if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { - int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); - auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); - signal->setEnabled(d->enabled); - QV4::Scope scope(engine); QV4::ScopedContext global(scope, engine->rootContext()); - QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this); - Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this - - QV4::ScopedFunctionObject method(scope, vmeMetaObject->vmeMethod(handler->coreIndex())); - - QQmlBoundSignalExpression *expression = - ctxtdata ? new QQmlBoundSignalExpression( - target, signalIndex, ctxtdata, this, - method->as<QV4::FunctionObject>()->function()) - : nullptr; - - signal->takeExpression(expression); - d->boundsignals += signal; + if (QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this)) { + int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); + auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); + signal->setEnabled(d->enabled); + + QV4::ScopedFunctionObject method( + scope, vmeMetaObject->vmeMethod(handler->coreIndex())); + + QQmlBoundSignalExpression *expression = ctxtdata + ? new QQmlBoundSignalExpression( + target, signalIndex, ctxtdata, this, + method->as<QV4::FunctionObject>()->function()) + : nullptr; + + signal->takeExpression(expression); + d->boundsignals += signal; + } else { + QQmlConnectionSlotDispatcher *slot = new QQmlConnectionSlotDispatcher( + scope.engine, target, prop.index(), + this, handler->coreIndex(), d->enabled); + slot->connection = QObjectPrivate::connect( + target, prop.index(), slot, Qt::AutoConnection); + slot->ref(); + d->boundsignals += slot; + } } else if (!d->ignoreUnknownSignals && propName.startsWith(QLatin1String("on")) && propName.size() > 2 && propName.at(2).isUpper()) { diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index bb23caecf5..99d04c6169 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -40,7 +40,8 @@ class Q_QML_PRIVATE_EXPORT QQmlConnections : public QObject, public QQmlParserSt QML_CUSTOMPARSER public: - QQmlConnections(QObject *parent=nullptr); + QQmlConnections(QObject *parent = nullptr); + ~QQmlConnections(); QObject *target() const; void setTarget(QObject *); diff --git a/src/qmlcompiler/qcoloroutput.cpp b/src/qmlcompiler/qcoloroutput.cpp index b79fc86caf..9f64927739 100644 --- a/src/qmlcompiler/qcoloroutput.cpp +++ b/src/qmlcompiler/qcoloroutput.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qcoloroutput_p.h" diff --git a/src/qmlcompiler/qcoloroutput_p.h b/src/qmlcompiler/qcoloroutput_p.h index c53b666802..5919713fcf 100644 --- a/src/qmlcompiler/qcoloroutput_p.h +++ b/src/qmlcompiler/qcoloroutput_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QCOLOROUTPUT_H #define QCOLOROUTPUT_H diff --git a/src/qmlcompiler/qdeferredpointer_p.h b/src/qmlcompiler/qdeferredpointer_p.h index 587cdd14d4..17bb60a2bd 100644 --- a/src/qmlcompiler/qdeferredpointer_p.h +++ b/src/qmlcompiler/qdeferredpointer_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QDEFERREDPOINTER_P_H #define QDEFERREDPOINTER_P_H diff --git a/src/qmlcompiler/qqmljsannotation.cpp b/src/qmlcompiler/qqmljsannotation.cpp index 2d5814f0a7..b21214d372 100644 --- a/src/qmlcompiler/qqmljsannotation.cpp +++ b/src/qmlcompiler/qqmljsannotation.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsannotation_p.h" diff --git a/src/qmlcompiler/qqmljsannotation_p.h b/src/qmlcompiler/qqmljsannotation_p.h index 1a63e14347..e9ee39a0fc 100644 --- a/src/qmlcompiler/qqmljsannotation_p.h +++ b/src/qmlcompiler/qqmljsannotation_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSANNOTATION_P_H #define QQMLJSANNOTATION_P_H diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp index 409c1f2b0a..08b8735ce5 100644 --- a/src/qmlcompiler/qqmljsbasicblocks.cpp +++ b/src/qmlcompiler/qqmljsbasicblocks.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsbasicblocks_p.h" @@ -155,16 +155,6 @@ static bool containsAny(const ContainerA &container, const ContainerB &elements) return false; } -template<typename ContainerA, typename ContainerB> -static bool containsAll(const ContainerA &container, const ContainerB &elements) -{ - for (const auto &element : elements) { - if (!container.contains(element)) - return false; - } - return true; -} - template<class Key, class T, class Compare = std::less<Key>, class KeyContainer = QList<Key>, class MappedContainer = QList<T>> class NewFlatMap @@ -191,7 +181,7 @@ private: struct PendingBlock { - QList<int> conversions; + QQmlJSBasicBlocks::Conversions conversions; int start = -1; bool registerActive = false; }; @@ -278,7 +268,7 @@ void QQmlJSBasicBlocks::populateReaderLocations() auto nextBlock = m_basicBlocks.find(block.start); auto currentBlock = nextBlock++; bool registerActive = block.registerActive; - QList<int> conversions = block.conversions; + Conversions conversions = block.conversions; const auto blockEnd = (nextBlock == m_basicBlocks.end()) ? m_annotations.end() @@ -290,7 +280,7 @@ void QQmlJSBasicBlocks::populateReaderLocations() for (; blockInstr != blockEnd; ++blockInstr) { if (registerActive && blockInstr->second.typeConversions.contains(writtenRegister)) { - conversions.append(blockInstr.key()); + conversions.insert(blockInstr.key()); } for (auto readIt = blockInstr->second.readRegisters.constBegin(), @@ -316,12 +306,26 @@ void QQmlJSBasicBlocks::populateReaderLocations() // If we find that an already processed block has the register activated by this jump, // we need to re-evaluate it. We also need to propagate any newly found conversions. const auto processed = processedBlocks.find(blockStart); - if (processed == processedBlocks.end()) + if (processed == processedBlocks.end()) { blocks.append({conversions, blockStart, registerActive}); - else if (registerActive && !processed->registerActive) + } else if (registerActive && !processed->registerActive) { blocks.append({conversions, blockStart, registerActive}); - else if (!containsAll(processed->conversions, conversions)) - blocks.append({processed->conversions + conversions, blockStart, registerActive}); + } else { + + // TODO: Use unite() once it is fixed. + // We don't use unite() here since it would be more expensive. unite() + // effectively loops on only insert() and insert() does a number of checks + // each time. We trade those checks for calculating the hash twice on each + // iteration. Calculating the hash is very cheap for integers. + Conversions merged = processed->conversions; + for (const int conversion : std::as_const(conversions)) { + if (!merged.contains(conversion)) + merged.insert(conversion); + } + + if (merged.size() > processed->conversions.size()) + blocks.append({std::move(merged), blockStart, registerActive}); + } }; if (!currentBlock->second.jumpIsUnconditional && nextBlock != m_basicBlocks.end()) @@ -407,7 +411,7 @@ void QQmlJSBasicBlocks::adjustTypes() valueType); } - for (const QList<int> &conversions : std::as_const(it->registerReadersAndConversions)) { + for (const auto &conversions : std::as_const(it->registerReadersAndConversions)) { for (int conversion : conversions) liveConversions[conversion].append(it->trackedRegister); } @@ -416,7 +420,7 @@ void QQmlJSBasicBlocks::adjustTypes() } for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) { - for (const QList<int> &conversions : std::as_const(it->registerReadersAndConversions)) { + for (const auto &conversions : std::as_const(it->registerReadersAndConversions)) { for (int conversion : conversions) liveConversions[conversion].append(it->trackedRegister); } @@ -426,6 +430,11 @@ void QQmlJSBasicBlocks::adjustTypes() if (it->trackedTypes.size() != 1) continue; + // Don't adjust renamed values. We only adjust the originals. + const int writeLocation = it.key(); + if (writeLocation >= 0 && m_annotations[writeLocation].isRename) + continue; + m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()); } diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h index 4927c76400..69ca5e8db7 100644 --- a/src/qmlcompiler/qqmljsbasicblocks_p.h +++ b/src/qmlcompiler/qqmljsbasicblocks_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSBASICBLOCKS_P_H #define QQMLJSBASICBLOCKS_P_H @@ -23,6 +23,8 @@ QT_BEGIN_NAMESPACE class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSBasicBlocks : public QQmlJSCompilePass { public: + using Conversions = QSet<int>; + struct BasicBlock { QList<int> jumpOrigins; QList<int> readRegisters; @@ -46,7 +48,7 @@ private: { QList<QQmlJSScope::ConstPtr> trackedTypes; QHash<int, QQmlJSScope::ConstPtr> typeReaders; - QHash<int, QList<int>> registerReadersAndConversions; + QHash<int, Conversions> registerReadersAndConversions; int trackedRegister; }; diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index e50c471082..534f423c8c 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljscodegenerator_p.h" #include "qqmljsmetatypes_p.h" @@ -191,8 +191,8 @@ QT_WARNING_POP result.code += registerIt->variableName + u" = "_s; - const QString originalValue = u"*static_cast<"_s + castTargetName(original) - + u"*>(argumentsPtr["_s + QString::number(argumentIndex) + u"])"_s; + const QString originalValue = u"(*static_cast<"_s + castTargetName(original) + + u"*>(argumentsPtr["_s + QString::number(argumentIndex) + u"]))"_s; if (needsConversion) result.code += conversion(original, argument, originalValue); @@ -2783,10 +2783,18 @@ void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator) void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization, const QString &resultPreparation) { + m_body += u"#ifndef QT_NO_DEBUG\n"_s; + generateSetInstructionPointer(); + m_body += u"#endif\n"_s; + if (!resultPreparation.isEmpty()) m_body += resultPreparation + u";\n"_s; m_body += u"while (!"_s + lookup + u") {\n"_s; + + m_body += u"#ifdef QT_NO_DEBUG\n"_s; generateSetInstructionPointer(); + m_body += u"#endif\n"_s; + m_body += initialization + u";\n"_s; generateExceptionCheck(); if (!resultPreparation.isEmpty()) @@ -3003,7 +3011,7 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, if (m_typeResolver->equals(to, m_typeResolver->uintType())) return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s; if (m_typeResolver->equals(to, m_typeResolver->boolType())) - return u'(' + variable + u" && !std::isnan("_s + variable + u"))"_s; + return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')'; } if (isBoolOrNumber(from) && isBoolOrNumber(to)) diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h index 62671ddcef..d6b52d6617 100644 --- a/src/qmlcompiler/qqmljscodegenerator_p.h +++ b/src/qmlcompiler/qqmljscodegenerator_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSCODEGENERATOR_P_H #define QQMLJSCODEGENERATOR_P_H diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h index b553f9b380..a6860d485d 100644 --- a/src/qmlcompiler/qqmljscompilepass_p.h +++ b/src/qmlcompiler/qqmljscompilepass_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSCOMPILEPASS_P_H #define QQMLJSCOMPILEPASS_P_H diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp index 91d778c20d..c01364c114 100644 --- a/src/qmlcompiler/qqmljscompiler.cpp +++ b/src/qmlcompiler/qqmljscompiler.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljscompiler_p.h" diff --git a/src/qmlcompiler/qqmljscompiler_p.h b/src/qmlcompiler/qqmljscompiler_p.h index 9f6afe0fc5..e9c16b0b42 100644 --- a/src/qmlcompiler/qqmljscompiler_p.h +++ b/src/qmlcompiler/qqmljscompiler_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSCOMPILER_P_H #define QQMLJSCOMPILER_P_H diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp index a9322e90c8..4a3390314c 100644 --- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp +++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsfunctioninitializer_p.h" diff --git a/src/qmlcompiler/qqmljsfunctioninitializer_p.h b/src/qmlcompiler/qqmljsfunctioninitializer_p.h index 9f191a4af8..c2b8d4f1f8 100644 --- a/src/qmlcompiler/qqmljsfunctioninitializer_p.h +++ b/src/qmlcompiler/qqmljsfunctioninitializer_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSFUNCTIONINITIALIAZER_P_H #define QQMLJSFUNCTIONINITIALIAZER_P_H diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp index 615e836bae..94b224ea20 100644 --- a/src/qmlcompiler/qqmljsimporter.cpp +++ b/src/qmlcompiler/qqmljsimporter.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsimporter_p.h" #include "qqmljstypedescriptionreader_p.h" diff --git a/src/qmlcompiler/qqmljsimporter_p.h b/src/qmlcompiler/qqmljsimporter_p.h index 792e26a291..fb89c20351 100644 --- a/src/qmlcompiler/qqmljsimporter_p.h +++ b/src/qmlcompiler/qqmljsimporter_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSIMPORTER_P_H #define QQMLJSIMPORTER_P_H diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index ed79dee08b..e4b1e106df 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsimportvisitor_p.h" #include "qqmljsmetatypes_p.h" diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h index 8565e47cf1..ae68934f27 100644 --- a/src/qmlcompiler/qqmljsimportvisitor_p.h +++ b/src/qmlcompiler/qqmljsimportvisitor_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSIMPORTEDMEMBERSVISITOR_P_H #define QQMLJSIMPORTEDMEMBERSVISITOR_P_H diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp index b69e2996cc..002014e904 100644 --- a/src/qmlcompiler/qqmljslinter.cpp +++ b/src/qmlcompiler/qqmljslinter.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljslinter_p.h" diff --git a/src/qmlcompiler/qqmljslinter_p.h b/src/qmlcompiler/qqmljslinter_p.h index e59d8557e7..cc4986f65d 100644 --- a/src/qmlcompiler/qqmljslinter_p.h +++ b/src/qmlcompiler/qqmljslinter_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QMLJSLINTER_P_H #define QMLJSLINTER_P_H diff --git a/src/qmlcompiler/qqmljslintercodegen.cpp b/src/qmlcompiler/qqmljslintercodegen.cpp index 2b79e34efa..0bcd59b394 100644 --- a/src/qmlcompiler/qqmljslintercodegen.cpp +++ b/src/qmlcompiler/qqmljslintercodegen.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljslintercodegen_p.h" diff --git a/src/qmlcompiler/qqmljslintercodegen_p.h b/src/qmlcompiler/qqmljslintercodegen_p.h index 5cddbea704..b73ec5e01f 100644 --- a/src/qmlcompiler/qqmljslintercodegen_p.h +++ b/src/qmlcompiler/qqmljslintercodegen_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSLINTERCODEGEN_P_H #define QQMLJSLINTERCODEGEN_P_H diff --git a/src/qmlcompiler/qqmljsliteralbindingcheck.cpp b/src/qmlcompiler/qqmljsliteralbindingcheck.cpp index a2d5222b2b..3d760c7efd 100644 --- a/src/qmlcompiler/qqmljsliteralbindingcheck.cpp +++ b/src/qmlcompiler/qqmljsliteralbindingcheck.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsliteralbindingcheck_p.h" diff --git a/src/qmlcompiler/qqmljsliteralbindingcheck_p.h b/src/qmlcompiler/qqmljsliteralbindingcheck_p.h index 92acd0555c..9759c1396e 100644 --- a/src/qmlcompiler/qqmljsliteralbindingcheck_p.h +++ b/src/qmlcompiler/qqmljsliteralbindingcheck_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSLITERALBINDINGCHECK_P_H #define QQMLJSLITERALBINDINGCHECK_P_H diff --git a/src/qmlcompiler/qqmljsloadergenerator.cpp b/src/qmlcompiler/qqmljsloadergenerator.cpp index 6e9fabbf60..f34d03e8da 100644 --- a/src/qmlcompiler/qqmljsloadergenerator.cpp +++ b/src/qmlcompiler/qqmljsloadergenerator.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsloadergenerator_p.h" diff --git a/src/qmlcompiler/qqmljsloadergenerator_p.h b/src/qmlcompiler/qqmljsloadergenerator_p.h index fc207f075b..5054fe4352 100644 --- a/src/qmlcompiler/qqmljsloadergenerator_p.h +++ b/src/qmlcompiler/qqmljsloadergenerator_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSLOADERGENERATOR_P_H #define QQMLJSLOADERGENERATOR_P_H diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp index 45a435b516..d2f3c60a03 100644 --- a/src/qmlcompiler/qqmljslogger.cpp +++ b/src/qmlcompiler/qqmljslogger.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include <qglobal.h> diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h index 56997a7f7e..f580353808 100644 --- a/src/qmlcompiler/qqmljslogger_p.h +++ b/src/qmlcompiler/qqmljslogger_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSLOGGER_P_H #define QQMLJSLOGGER_P_H diff --git a/src/qmlcompiler/qqmljsmetatypes.cpp b/src/qmlcompiler/qqmljsmetatypes.cpp index 4ce69783e6..6d68707b7b 100644 --- a/src/qmlcompiler/qqmljsmetatypes.cpp +++ b/src/qmlcompiler/qqmljsmetatypes.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsmetatypes_p.h" #include "qqmljstyperesolver_p.h" diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h index 823a1849e5..e2e73670bd 100644 --- a/src/qmlcompiler/qqmljsmetatypes_p.h +++ b/src/qmlcompiler/qqmljsmetatypes_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSMETATYPES_P_H #define QQMLJSMETATYPES_P_H @@ -120,7 +120,7 @@ public: Const, }; - QQmlJSMetaParameter(const QString &name, const QString &typeName, + QQmlJSMetaParameter(const QString &name = QString(), const QString &typeName = QString(), Constness typeQualifier = NonConst, QWeakPointer<const QQmlJSScope> type = {}) : m_name(name), m_typeName(typeName), m_type(type), m_typeQualifier(typeQualifier) @@ -135,9 +135,13 @@ public: void setType(QWeakPointer<const QQmlJSScope> type) { m_type = type; } Constness typeQualifier() const { return m_typeQualifier; } void setTypeQualifier(Constness typeQualifier) { m_typeQualifier = typeQualifier; } + bool isPointer() const { return m_isPointer; } void setIsPointer(bool isPointer) { m_isPointer = isPointer; } + bool isList() const { return m_isList; } + void setIsList(bool isList) { m_isList = isList; } + friend bool operator==(const QQmlJSMetaParameter &a, const QQmlJSMetaParameter &b) { return a.m_name == b.m_name && a.m_typeName == b.m_typeName @@ -162,8 +166,11 @@ private: QWeakPointer<const QQmlJSScope> m_type; Constness m_typeQualifier = NonConst; bool m_isPointer = false; + bool m_isList = false; }; +using QQmlJSMetaReturnType = QQmlJSMetaParameter; + class QQmlJSMetaMethod { public: @@ -191,20 +198,18 @@ public: QQmlJSMetaMethod() = default; explicit QQmlJSMetaMethod(QString name, QString returnType = QString()) : m_name(std::move(name)) - , m_returnTypeName(std::move(returnType)) + , m_returnType(QString(), std::move(returnType)) , m_methodType(Method) {} QString methodName() const { return m_name; } void setMethodName(const QString &name) { m_name = name; } - QString returnTypeName() const { return m_returnTypeName; } - QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.toStrongRef(); } - void setReturnTypeName(const QString &type) { m_returnTypeName = type; } - void setReturnType(const QSharedPointer<const QQmlJSScope> &type) - { - m_returnType = type; - } + QQmlJSMetaReturnType returnValue() const { return m_returnType; } + void setReturnValue(const QQmlJSMetaReturnType returnValue) { m_returnType = returnValue; } + QString returnTypeName() const { return m_returnType.typeName(); } + QSharedPointer<const QQmlJSScope> returnType() const { return m_returnType.type(); } + void setReturnTypeName(const QString &type) { m_returnType.setTypeName(type); } QList<QQmlJSMetaParameter> parameters() const { return m_parameters; } @@ -257,7 +262,7 @@ public: friend bool operator==(const QQmlJSMetaMethod &a, const QQmlJSMetaMethod &b) { - return a.m_name == b.m_name && a.m_returnTypeName == b.m_returnTypeName + return a.m_name == b.m_name && a.m_returnType == b.m_returnType && a.m_returnType == b.m_returnType && a.m_parameters == b.m_parameters && a.m_annotations == b.m_annotations && a.m_methodType == b.m_methodType && a.m_methodAccess == b.m_methodAccess && a.m_revision == b.m_revision @@ -274,8 +279,7 @@ public: QtPrivate::QHashCombine combine; seed = combine(seed, method.m_name); - seed = combine(seed, method.m_returnTypeName); - seed = combine(seed, method.m_returnType.toStrongRef().data()); + seed = combine(seed, method.m_returnType); seed = combine(seed, method.m_annotations); seed = combine(seed, method.m_methodType); seed = combine(seed, method.m_methodAccess); @@ -291,8 +295,8 @@ public: private: QString m_name; - QString m_returnTypeName; - QWeakPointer<const QQmlJSScope> m_returnType; + + QQmlJSMetaReturnType m_returnType; QList<QQmlJSMetaParameter> m_parameters; QList<QQmlJSAnnotation> m_annotations; diff --git a/src/qmlcompiler/qqmljsregistercontent.cpp b/src/qmlcompiler/qqmljsregistercontent.cpp index 1573f889a1..f824bb5871 100644 --- a/src/qmlcompiler/qqmljsregistercontent.cpp +++ b/src/qmlcompiler/qqmljsregistercontent.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsregistercontent_p.h" #include "qqmljstyperesolver_p.h" diff --git a/src/qmlcompiler/qqmljsregistercontent_p.h b/src/qmlcompiler/qqmljsregistercontent_p.h index 8a802bd195..9f4f48525b 100644 --- a/src/qmlcompiler/qqmljsregistercontent_p.h +++ b/src/qmlcompiler/qqmljsregistercontent_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSREGISTERCONTENT_P_H #define QQMLJSREGISTERCONTENT_P_H diff --git a/src/qmlcompiler/qqmljsresourcefilemapper.cpp b/src/qmlcompiler/qqmljsresourcefilemapper.cpp index 4213902fb3..15886f89ae 100644 --- a/src/qmlcompiler/qqmljsresourcefilemapper.cpp +++ b/src/qmlcompiler/qqmljsresourcefilemapper.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsresourcefilemapper_p.h" diff --git a/src/qmlcompiler/qqmljsresourcefilemapper_p.h b/src/qmlcompiler/qqmljsresourcefilemapper_p.h index 618cc2edec..92fa3ac440 100644 --- a/src/qmlcompiler/qqmljsresourcefilemapper_p.h +++ b/src/qmlcompiler/qqmljsresourcefilemapper_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSRESOURCEFILEMAPPER_P_H #define QQMLJSRESOURCEFILEMAPPER_P_H diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index ad9c0a4d3d..08631c0547 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsscope_p.h" #include "qqmljstypereader_p.h" @@ -502,24 +502,26 @@ QTypeRevision QQmlJSScope::resolveType( } } - for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) { - const QString returnTypeName = it->returnTypeName(); - if (!it->returnType() && !returnTypeName.isEmpty()) { - const auto returnType = findType(returnTypeName, context, usedTypes); - it->setReturnType(returnType.scope); + const auto resolveParameter = [&](QQmlJSMetaParameter ¶meter) { + if (const QString typeName = parameter.typeName(); + !parameter.type() && !typeName.isEmpty()) { + const auto type = findType(typeName, context, usedTypes); + if (type.scope && type.scope->isReferenceType()) + parameter.setIsPointer(true); + parameter.setType({ (type.scope && parameter.isList()) + ? type.scope->listType() + : type.scope }); } + }; + + for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) { + auto returnValue = it->returnValue(); + resolveParameter(returnValue); + it->setReturnValue(returnValue); auto parameters = it->parameters(); - for (int i = 0, length = parameters.size(); i < length; ++i) { - auto ¶meter = parameters[i]; - if (const QString typeName = parameter.typeName(); - !parameter.type() && !typeName.isEmpty()) { - const auto type = findType(typeName, context, usedTypes); - if (type.scope && type.scope->isReferenceType()) - parameter.setIsPointer(true); - parameter.setType({ type.scope }); - } - } + for (int i = 0, length = parameters.size(); i < length; ++i) + resolveParameter(parameters[i]); it->setParameters(parameters); } diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index 60c84825de..99dad652ab 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSSCOPE_P_H #define QQMLJSSCOPE_P_H diff --git a/src/qmlcompiler/qqmljsscopesbyid_p.h b/src/qmlcompiler/qqmljsscopesbyid_p.h index 1db6821920..8c257cb029 100644 --- a/src/qmlcompiler/qqmljsscopesbyid_p.h +++ b/src/qmlcompiler/qqmljsscopesbyid_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSSCOPESBYID_P_H #define QQMLJSSCOPESBYID_P_H diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp index ead1813afe..2a9a293991 100644 --- a/src/qmlcompiler/qqmljsshadowcheck.cpp +++ b/src/qmlcompiler/qqmljsshadowcheck.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsshadowcheck_p.h" diff --git a/src/qmlcompiler/qqmljsshadowcheck_p.h b/src/qmlcompiler/qqmljsshadowcheck_p.h index 92990a9eeb..f9aa8a06fc 100644 --- a/src/qmlcompiler/qqmljsshadowcheck_p.h +++ b/src/qmlcompiler/qqmljsshadowcheck_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSSHADOWCHECK_P_H #define QQMLJSSHADOWCHECK_P_H diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp index 63fc4762b6..dbe001d6bf 100644 --- a/src/qmlcompiler/qqmljsstoragegeneralizer.cpp +++ b/src/qmlcompiler/qqmljsstoragegeneralizer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsstoragegeneralizer_p.h" diff --git a/src/qmlcompiler/qqmljsstoragegeneralizer_p.h b/src/qmlcompiler/qqmljsstoragegeneralizer_p.h index d04e5ce1dd..1e356a7ce9 100644 --- a/src/qmlcompiler/qqmljsstoragegeneralizer_p.h +++ b/src/qmlcompiler/qqmljsstoragegeneralizer_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSSTORAGEGENERALIZER_P_H #define QQMLJSSTORAGEGENERALIZER_P_H diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp index 8528d39f0f..5084bd2109 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp +++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljstypedescriptionreader_p.h" @@ -299,7 +299,9 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bo } else if (name == QLatin1String("isJavaScriptFunction")) { metaMethod.setIsJavaScriptFunction(true); } else if (name == QLatin1String("isList")) { - // TODO: Theoretically this can happen. QQmlJSMetaMethod should store it. + auto metaReturnType = metaMethod.returnValue(); + metaReturnType.setIsList(true); + metaMethod.setReturnValue(metaReturnType); } else if (name == QLatin1String("isPointer")) { // TODO: We don't need this information. We can probably drop all isPointer members // once we make sure that the type information is always complete. The @@ -429,6 +431,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM QString type; bool isConstant = false; bool isPointer = false; + bool isList = false; for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { UiObjectMember *member = it->member; @@ -450,7 +453,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM } else if (id == QLatin1String("isReadonly")) { // ### unhandled } else if (id == QLatin1String("isList")) { - // ### unhandled + isList = readBoolBinding(script); } else { addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings.")); @@ -460,6 +463,7 @@ void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSM QQmlJSMetaParameter p(name, type); p.setTypeQualifier(isConstant ? QQmlJSMetaParameter::Const : QQmlJSMetaParameter::NonConst); p.setIsPointer(isPointer); + p.setIsList(isList); metaMethod->addParameter(std::move(p)); } diff --git a/src/qmlcompiler/qqmljstypedescriptionreader_p.h b/src/qmlcompiler/qqmljstypedescriptionreader_p.h index 37dd388308..d383448369 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader_p.h +++ b/src/qmlcompiler/qqmljstypedescriptionreader_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSTYPEDESCRIPTIONREADER_P_H #define QQMLJSTYPEDESCRIPTIONREADER_P_H diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index cdcf0b1038..6cb33e3176 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsscope_p.h" #include "qqmljstypepropagator_p.h" @@ -943,9 +943,19 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base) getCurrentSourceLocation()); } - m_state.setHasSideEffects(true); + if (!m_typeResolver->canHoldUndefined(property) + && m_typeResolver->canHoldUndefined(m_state.accumulatorIn())) { + // If the input can be undefined but the property cannot hold it + // we must not coerce the input to the property type: + // * In the case of resettable properties this would suppress a reset + // * In the case of non-resettable properties it would suppress an exception. + // For example, undefined -> string becomes "undefined". + setError(u"Cannot assign potential undefined to %1"_s.arg(property.descriptiveName())); + } + addReadAccumulator(property); addReadRegister(base, callBase); + m_state.setHasSideEffects(true); } void QQmlJSTypePropagator::generate_SetLookup(int index, int base) @@ -2181,6 +2191,7 @@ void QQmlJSTypePropagator::generate_InitializeBlockDeadTemporalZone(int firstReg { Q_UNUSED(firstReg) Q_UNUSED(count) + m_state.setHasSideEffects(true); // Ignore. We reject uninitialized values anyway. } @@ -2300,6 +2311,13 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr) } } + if (!(m_error->isValid() && m_error->isError()) + && instr != QV4::Moth::Instr::Type::DeadTemporalZoneCheck) { + // An instruction needs to have side effects or write to another register otherwise it's a + // noop. DeadTemporalZoneCheck is not needed by the compiler and is ignored. + Q_ASSERT(m_state.hasSideEffects() || m_state.changedRegisterIndex() != -1); + } + if (m_state.changedRegisterIndex() != InvalidRegister) { Q_ASSERT(m_error->isValid() || m_state.changedRegister().isValid()); VirtualRegister &r = m_state.registers[m_state.changedRegisterIndex()]; diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h index 0360671535..0ba5f0303c 100644 --- a/src/qmlcompiler/qqmljstypepropagator_p.h +++ b/src/qmlcompiler/qqmljstypepropagator_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSTYPEPROPAGATOR_P_H #define QQMLJSTYPEPROPAGATOR_P_H diff --git a/src/qmlcompiler/qqmljstypereader.cpp b/src/qmlcompiler/qqmljstypereader.cpp index 86d547d714..2585e6ecc4 100644 --- a/src/qmlcompiler/qqmljstypereader.cpp +++ b/src/qmlcompiler/qqmljstypereader.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljstypereader_p.h" #include "qqmljsimportvisitor_p.h" diff --git a/src/qmlcompiler/qqmljstypereader_p.h b/src/qmlcompiler/qqmljstypereader_p.h index 7fa5ccb1f2..d9b1454b2e 100644 --- a/src/qmlcompiler/qqmljstypereader_p.h +++ b/src/qmlcompiler/qqmljstypereader_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSTYPEREADER_P_H #define QQMLJSTYPEREADER_P_H diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 6f636b9ec3..ee837c6e17 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljstyperesolver_p.h" @@ -647,10 +647,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a, return intType(); if (canConvert(boolType(), uintType())) return uintType(); - if (canConvert(intType(), stringType())) - return stringType(); - if (canConvert(uintType(), stringType())) - return stringType(); + if (isPrimitive(a) && isPrimitive(b)) return jsPrimitiveType(); diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index f8e5ebb344..143d29bec7 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSTYPERESOLVER_P_H #define QQMLJSTYPERESOLVER_P_H diff --git a/src/qmlcompiler/qqmljsutils.cpp b/src/qmlcompiler/qqmljsutils.cpp index 57564efee6..1089e0b06c 100644 --- a/src/qmlcompiler/qqmljsutils.cpp +++ b/src/qmlcompiler/qqmljsutils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsutils_p.h" #include "qqmljstyperesolver_p.h" diff --git a/src/qmlcompiler/qqmljsutils_p.h b/src/qmlcompiler/qqmljsutils_p.h index 956d946980..5a9cbd7562 100644 --- a/src/qmlcompiler/qqmljsutils_p.h +++ b/src/qmlcompiler/qqmljsutils_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSUTILS_P_H #define QQMLJSUTILS_P_H diff --git a/src/qmlcompiler/qqmlsa.cpp b/src/qmlcompiler/qqmlsa.cpp index 7a209f52c1..73ac6f06b2 100644 --- a/src/qmlcompiler/qqmlsa.cpp +++ b/src/qmlcompiler/qqmlsa.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmlsa_p.h" diff --git a/src/qmlcompiler/qqmlsa_p.h b/src/qmlcompiler/qqmlsa_p.h index 6bbbe9441b..39c6f303d8 100644 --- a/src/qmlcompiler/qqmlsa_p.h +++ b/src/qmlcompiler/qqmlsa_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLSA_P_H #define QQMLSA_P_H diff --git a/src/qmlcompiler/qresourcerelocater.cpp b/src/qmlcompiler/qresourcerelocater.cpp index 05ad059586..228088ebe7 100644 --- a/src/qmlcompiler/qresourcerelocater.cpp +++ b/src/qmlcompiler/qresourcerelocater.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qresourcerelocater_p.h" diff --git a/src/qmlcompiler/qresourcerelocater_p.h b/src/qmlcompiler/qresourcerelocater_p.h index b0c2471147..7cd320b9a4 100644 --- a/src/qmlcompiler/qresourcerelocater_p.h +++ b/src/qmlcompiler/qresourcerelocater_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QRESOURCERELOCATER_P_H #define QRESOURCERELOCATER_P_H diff --git a/src/qmldebug/qqmlenginedebugclient.cpp b/src/qmldebug/qqmlenginedebugclient.cpp index eab1621cea..4087cdd812 100644 --- a/src/qmldebug/qqmlenginedebugclient.cpp +++ b/src/qmldebug/qqmlenginedebugclient.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmlenginedebugclient_p_p.h" #include <private/qqmldebugconnection_p.h> diff --git a/src/qmldebug/qqmlenginedebugclient_p.h b/src/qmldebug/qqmlenginedebugclient_p.h index 48bc62f076..58f8466a50 100644 --- a/src/qmldebug/qqmlenginedebugclient_p.h +++ b/src/qmldebug/qqmlenginedebugclient_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLENGINEDEBUGCLIENT_H #define QQMLENGINEDEBUGCLIENT_H diff --git a/src/qmldom/qqmldomcomments.cpp b/src/qmldom/qqmldomcomments.cpp index 5215af3117..8bdd94aecb 100644 --- a/src/qmldom/qqmldomcomments.cpp +++ b/src/qmldom/qqmldomcomments.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmldomcomments_p.h" #include "qqmldomoutwriter_p.h" diff --git a/src/qmldom/qqmldomcomments_p.h b/src/qmldom/qqmldomcomments_p.h index e4f85fa3e3..ae6e3d4c04 100644 --- a/src/qmldom/qqmldomcomments_p.h +++ b/src/qmldom/qqmldomcomments_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLDOMCOMMENTS_P_H #define QQMLDOMCOMMENTS_P_H diff --git a/src/qmldom/qqmldomelements.cpp b/src/qmldom/qqmldomelements.cpp index 3ea75b7eba..a196aeabe9 100644 --- a/src/qmldom/qqmldomelements.cpp +++ b/src/qmldom/qqmldomelements.cpp @@ -314,13 +314,39 @@ bool Import::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) return cont; } -void Import::writeOut(DomItem &, OutWriter &ow) const +void Import::writeOut(DomItem &self, OutWriter &ow) const { if (implicit) return; - ow.ensureNewline(); + + QString code; + const DomItem owner = self.owner(); + if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>()) + code = qmlFilePtr->code(); + + // check for an empty line before the import, and preserve it + int preNewlines = 0; + + const FileLocations::Tree elLoc = FileLocations::findAttachedInfo(self).foundTree; + + quint32 start = elLoc->info().fullRegion.offset; + if (size_t(code.size()) >= start) { + while (start != 0) { + QChar c = code.at(--start); + if (c == u'\n') { + if (++preNewlines == 2) + break; + } else if (!c.isSpace()) + break; + } + } + if (preNewlines == 0) + ++preNewlines; + + ow.ensureNewline(preNewlines); ow.writeRegion(u"import").space(); ow.writeRegion(u"uri", uri.toString()); + if (uri.isModule()) { QString vString = version.stringValue(); if (!vString.isEmpty()) @@ -714,7 +740,7 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const ow.writeRegion(u"name", name()); if (!onTarget.isEmpty()) ow.space().writeRegion(u"on", u"on").space().writeRegion(u"onTarget", onTarget); - ow.writeRegion(u"leftBrace", u" {").newline(); + ow.writeRegion(u"leftBrace", u" {"); int baseIndent = ow.increaseIndent(); int spacerId = 0; if (!idStr().isEmpty()) { // *always* put id first @@ -730,8 +756,10 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const == LineWriterOptions::AttributesSequence::Normalize) { ow.ensureNewline(2); } - if (myId) + if (myId) { myId.writeOutPost(ow); + ow.ensureNewline(1); + } } quint32 counter = ow.counter(); DomItem component; @@ -850,9 +878,10 @@ void QmlObject::writeOut(DomItem &self, OutWriter &ow, QString onTarget) const } else { el.second.writeOut(ow); } + ow.ensureNewline(); } ow.decreaseIndent(1, baseIndent); - ow.ensureNewline().write(u"}"); + ow.write(u"}"); return; } diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp index 6c70a1d00b..182db09511 100644 --- a/src/qmldom/qqmldomreformatter.cpp +++ b/src/qmldom/qqmldomreformatter.cpp @@ -260,9 +260,16 @@ protected: { out(ast->lbracketToken); int baseIndent = lw.increaseIndent(1); - if (ast->elements) + if (ast->elements) { accept(ast->elements); - out(ast->commaToken); + out(ast->commaToken); + auto lastElement = lastListElement(ast->elements); + if (lastElement->element && cast<ObjectPattern *>(lastElement->element->initializer)) { + newLine(); + } + } else { + out(ast->commaToken); + } lw.decreaseIndent(1, baseIndent); out(ast->rbracketToken); return false; @@ -284,14 +291,22 @@ protected: bool visit(PatternElementList *ast) override { for (PatternElementList *it = ast; it; it = it->next) { + const bool isObjectInitializer = + it->element && cast<ObjectPattern *>(it->element->initializer); + if (isObjectInitializer) + newLine(); + if (it->elision) accept(it->elision); if (it->elision && it->element) out(", "); if (it->element) accept(it->element); - if (it->next) + if (it->next) { out(", "); + if (isObjectInitializer) + newLine(); + } } return false; } @@ -302,24 +317,16 @@ protected: PatternProperty *assignment = AST::cast<PatternProperty *>(it->property); if (assignment) { preVisit(assignment); - bool isStringLike = AST::cast<StringLiteralPropertyName *>(assignment->name) - || cast<IdentifierPropertyName *>(assignment->name); + accept(assignment->name); bool useInitializer = false; const bool bindingIdentifierExist = !assignment->bindingIdentifier.isEmpty(); if (assignment->colonToken.length > 0) { - if (isStringLike) - out("\""); - accept(assignment->name); - if (isStringLike) - out("\""); out(": "); useInitializer = true; if (bindingIdentifierExist) out(assignment->bindingIdentifier); if (assignment->bindingTarget) accept(assignment->bindingTarget); - } else { - accept(assignment->name); } if (assignment->initializer) { if (bindingIdentifierExist) { @@ -374,12 +381,12 @@ protected: bool visit(IdentifierPropertyName *ast) override { - out(ast->id.toString()); + out(ast->propertyNameToken); return true; } bool visit(StringLiteralPropertyName *ast) override { - out(ast->id.toString()); + out(ast->propertyNameToken); return true; } bool visit(NumericLiteralPropertyName *ast) override @@ -446,9 +453,7 @@ protected: { accept(ast->base); out(ast->lparenToken); - int baseIndent = lw.increaseIndent(1); accept(ast->arguments); - lw.decreaseIndent(1, baseIndent); out(ast->rparenToken); return false; } @@ -553,10 +558,12 @@ protected: bool visit(Block *ast) override { out(ast->lbraceToken); - ++expressionDepth; - lnAcceptIndented(ast->statements); - newLine(); - --expressionDepth; + if (ast->statements) { + ++expressionDepth; + lnAcceptIndented(ast->statements); + newLine(); + --expressionDepth; + } out(ast->rbraceToken); return false; } diff --git a/src/qmldom/qqmldomtypesreader.cpp b/src/qmldom/qqmldomtypesreader.cpp index 12cba9ae0e..b6c356e59b 100644 --- a/src/qmldom/qqmldomtypesreader.cpp +++ b/src/qmldom/qqmldomtypesreader.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmldomtypesreader_p.h" #include "qqmldomelements_p.h" diff --git a/src/qmldom/qqmldomtypesreader_p.h b/src/qmldom/qqmldomtypesreader_p.h index d1650d726d..0ac862489b 100644 --- a/src/qmldom/qqmldomtypesreader_p.h +++ b/src/qmldom/qqmldomtypesreader_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLDOMTYPESREADER_H #define QQMLDOMTYPESREADER_H diff --git a/src/qmltest/SignalSpy.qml b/src/qmltest/SignalSpy.qml index ca92f91138..88e0d431e5 100644 --- a/src/qmltest/SignalSpy.qml +++ b/src/qmltest/SignalSpy.qml @@ -50,7 +50,7 @@ Item { // and since only the sender (target) being destroyed destroys a connection // in QML, and not the receiver (us/"spy"), we need to manually disconnect. // When QTBUG-118166 is implemented, we can remove this. - let signalFunc = target ? target[signalName] : null + let signalFunc = target ? qttest_signalFunc(target, signalName) : null if (signalFunc) signalFunc.disconnect(spy.qtest_activated) } @@ -201,8 +201,7 @@ Item { qtest_reentrancy_guard = true; if (qtest_prevTarget != null) { - var prevHandlerName = qtest_signalHandlerName(qtest_prevSignalName) - var prevFunc = qtest_prevTarget[prevHandlerName] + let prevFunc = qttest_signalFunc(qtest_prevTarget, qtest_prevSignalName) if (prevFunc) prevFunc.disconnect(spy.qtest_activated) qtest_prevTarget = null @@ -210,22 +209,16 @@ Item { } if (target != null && signalName != "") { // Look for the signal name in the object - var func = target[signalName] - if (typeof func !== "function") { - // If it is not a function, try looking for signal handler - // i.e. (onSignal) this is needed for cases where there is a property - // and a signal with the same name, e.g. Mousearea.pressed - func = target[qtest_signalHandlerName(signalName)] - } - if (func === undefined) { - spy.qtest_valid = false - console.log("Signal '" + signalName + "' not found") - } else { + let func = qttest_signalFunc(target, signalName) + if (func) { qtest_prevTarget = target qtest_prevSignalName = signalName func.connect(spy.qtest_activated) spy.qtest_valid = true spy.qtest_signalArguments = [] + } else { + spy.qtest_valid = false + console.log("Signal '" + signalName + "' not found") } } else { spy.qtest_valid = false @@ -246,4 +239,16 @@ Item { return sn return "on" + sn.substr(0, 1).toUpperCase() + sn.substr(1) } + + /*! \internal */ + function qttest_signalFunc(_target, _signalName) { + let signalFunc = _target[_signalName] + if (typeof signalFunc !== "function") { + // If it is not a function, try looking for signal handler + // i.e. (onSignal) this is needed for cases where there is a property + // and a signal with the same name, e.g. Mousearea.pressed + signalFunc = _target[qtest_signalHandlerName(_signalName)] + } + return signalFunc + } } diff --git a/src/qmltest/TestCase.qml b/src/qmltest/TestCase.qml index eb0c79b4c5..2590cbc97d 100644 --- a/src/qmltest/TestCase.qml +++ b/src/qmltest/TestCase.qml @@ -165,6 +165,15 @@ import "testlogger.js" as TestLogger and mouseMove() methods can be used to simulate mouse events in a similar fashion. + If your test creates other windows, it's possible that those windows + become active, stealing the focus from the TestCase's window. To ensure + that the TestCase's window is active, use the following code: + + \code + testCase.Window.window.requestActivate() + tryCompare(testCase.Window.window, "active", true) + \endcode + \b{Note:} keyboard and mouse events can only be delivered once the main window has been shown. Attempts to deliver events before then will fail. Use the \l when and windowShown properties to track @@ -1304,7 +1313,16 @@ Item { Waits for \a ms milliseconds while processing Qt events. - \sa sleep(), waitForRendering() + \note This methods uses a precise timer to do the actual waiting. The + event you are waiting for may not. In particular, any animations as + well as the \l{Timer} QML type can use either precise or coarse + timers, depending on various factors. For a coarse timer you have + to expect a drift of around 5% in relation to the precise timer used + by TestCase::wait(). Qt cannot give hard guarantees on the drift, + though, because the operating system usually doesn't offer hard + guarantees on timers. + + \sa sleep(), waitForRendering(), Qt::TimerType */ function wait(ms) { qtest_results.wait(ms) diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc index d5534052af..b656979273 100644 --- a/src/qmltest/doc/src/qtquicktest-index.qdoc +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -168,7 +168,7 @@ setting context properties on the QML engine, amongst other things. The macro is identical to \c QUICK_TEST_MAIN, except that it takes an - additional \c QObject* argument. The test framework will call slots and + additional type argument. The test framework will call slots and invokable functions with the following names: \table diff --git a/src/qmltest/doc/src/qtquicktest.qdoc b/src/qmltest/doc/src/qtquicktest.qdoc index 357aa9b524..d349a725f8 100644 --- a/src/qmltest/doc/src/qtquicktest.qdoc +++ b/src/qmltest/doc/src/qtquicktest.qdoc @@ -41,9 +41,9 @@ The \a name argument uniquely identifies this set of tests. This macro is identical to QUICK_TEST_MAIN(), except that it takes an - additional argument \a QuickTestSetupClass, a pointer to a QObject-derived - class. With this class it is possible to define additional setup code to - execute before running the QML test. + additional argument \a QuickTestSetupClass, the type of a QObject-derived + class which will be instantiated. With this class it is possible to define + additional setup code to execute before running the QML test. \note The macro assumes that your test sources are in the current directory, unless the \c QUICK_TEST_SOURCE_DIR environment variable is set. diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp index 899e8f91bd..b594a46a57 100644 --- a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp +++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qmetatypesjsonprocessor_p.h" diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h index 544f472383..cc1c84394a 100644 --- a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h +++ b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef METATYPESJSONPROCESSOR_P_H #define METATYPESJSONPROCESSOR_P_H diff --git a/src/qmltyperegistrar/qqmljsstreamwriter.cpp b/src/qmltyperegistrar/qqmljsstreamwriter.cpp index e435b8df92..41f1f8c689 100644 --- a/src/qmltyperegistrar/qqmljsstreamwriter.cpp +++ b/src/qmltyperegistrar/qqmljsstreamwriter.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmljsstreamwriter_p.h" diff --git a/src/qmltyperegistrar/qqmljsstreamwriter_p.h b/src/qmltyperegistrar/qqmljsstreamwriter_p.h index d0984b8ca9..628dd2e069 100644 --- a/src/qmltyperegistrar/qqmljsstreamwriter_p.h +++ b/src/qmltyperegistrar/qqmljsstreamwriter_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLJSSTREAMWRITER_P_H #define QQMLJSSTREAMWRITER_P_H diff --git a/src/qmltyperegistrar/qqmltyperegistrar.cpp b/src/qmltyperegistrar/qqmltyperegistrar.cpp index 91a0f3ad1f..3cd65bc149 100644 --- a/src/qmltyperegistrar/qqmltyperegistrar.cpp +++ b/src/qmltyperegistrar/qqmltyperegistrar.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include <QFile> #include <QJsonArray> @@ -83,11 +83,16 @@ int QmlTypeRegistrar::runExtract(const QString &baseName, const MetaTypesJsonPro fprintf(stderr, "Error: Cannot open %s for writing\n", qPrintable(headerFile.fileName())); return EXIT_FAILURE; } + + QString includeGuard = baseName; + static const QRegularExpression nonAlNum(QLatin1String("[^a-zA-Z0-9_]")); + includeGuard.replace(nonAlNum, QLatin1String("_")); + auto prefix = QString::fromLatin1( "#ifndef %1_H\n" "#define %1_H\n" "#include <QtQml/qqml.h>\n" - "#include <QtQml/qqmlmoduleregistration.h>\n").arg(baseName.toUpper()); + "#include <QtQml/qqmlmoduleregistration.h>\n").arg(includeGuard); const QStringList includes = processor.includes(); for (const QString &include: includes) prefix += u"\n#include <%1>"_s.arg(include); diff --git a/src/qmltyperegistrar/qqmltyperegistrar_p.h b/src/qmltyperegistrar/qqmltyperegistrar_p.h index 659c7ea988..0ba7584d7d 100644 --- a/src/qmltyperegistrar/qqmltyperegistrar_p.h +++ b/src/qmltyperegistrar/qqmltyperegistrar_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QMLTYPEREGISTRAR_P_H #define QMLTYPEREGISTRAR_P_H diff --git a/src/qmltyperegistrar/qqmltypesclassdescription.cpp b/src/qmltyperegistrar/qqmltypesclassdescription.cpp index bd73345f4c..68aefd3771 100644 --- a/src/qmltyperegistrar/qqmltypesclassdescription.cpp +++ b/src/qmltyperegistrar/qqmltypesclassdescription.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmltypesclassdescription_p.h" #include "qqmltypescreator_p.h" diff --git a/src/qmltyperegistrar/qqmltypesclassdescription_p.h b/src/qmltyperegistrar/qqmltypesclassdescription_p.h index cb08237a20..c1c0941828 100644 --- a/src/qmltyperegistrar/qqmltypesclassdescription_p.h +++ b/src/qmltyperegistrar/qqmltypesclassdescription_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QMLTYPESCLASSDESCRIPTION_P_H #define QMLTYPESCLASSDESCRIPTION_P_H diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp index 68019de67a..cb67c9729e 100644 --- a/src/qmltyperegistrar/qqmltypescreator.cpp +++ b/src/qmltyperegistrar/qqmltypescreator.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qqmltypescreator_p.h" #include "qqmltypesclassdescription_p.h" diff --git a/src/qmltyperegistrar/qqmltypescreator_p.h b/src/qmltyperegistrar/qqmltypescreator_p.h index eeab19a71b..62c77fb30a 100644 --- a/src/qmltyperegistrar/qqmltypescreator_p.h +++ b/src/qmltyperegistrar/qqmltypescreator_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QMLTYPESCREATOR_P_H #define QMLTYPESCREATOR_P_H diff --git a/src/quick/doc/snippets/qml/externaldrag.qml b/src/quick/doc/snippets/qml/externaldrag.qml index 072612f0c8..723701a7a5 100644 --- a/src/quick/doc/snippets/qml/externaldrag.qml +++ b/src/quick/doc/snippets/qml/externaldrag.qml @@ -12,7 +12,6 @@ Item { color: "green" radius: 5 - Drag.active: dragHandler.active Drag.dragType: Drag.Automatic Drag.supportedActions: Qt.CopyAction Drag.mimeData: { @@ -30,8 +29,11 @@ Item { onActiveChanged: if (active) { parent.grabToImage(function(result) { - parent.Drag.imageSource = result.url; + parent.Drag.imageSource = result.url + parent.Drag.active = true }) + } else { + parent.Drag.active = false } } } diff --git a/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml b/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml index 97574487e9..3d7dda7700 100644 --- a/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml +++ b/src/quick/doc/snippets/qml/treeview/qml-customdelegate.qml @@ -3,24 +3,29 @@ //![0] import QtQuick +import QtQuick.Controls -Window { - width: 600 - height: 400 +ApplicationWindow { + width: 800 + height: 600 visible: true TreeView { + id: treeView anchors.fill: parent + anchors.margins: 10 + clip: true + + selectionModel: ItemSelectionModel {} + // The model needs to be a QAbstractItemModel // model: yourTreeModel delegate: Item { - id: treeDelegate - implicitWidth: padding + label.x + label.implicitWidth + padding implicitHeight: label.implicitHeight * 1.5 - readonly property real indent: 20 + readonly property real indentation: 20 readonly property real padding: 5 // Assigned to by TreeView: @@ -29,24 +34,52 @@ Window { required property bool expanded required property int hasChildren required property int depth + required property int row + required property int column + required property bool current - TapHandler { - onTapped: treeView.toggleExpanded(row) + // Rotate indicator when expanded by the user + // (requires TreeView to have a selectionModel) + property Animation indicatorAnimation: NumberAnimation { + target: indicator + property: "rotation" + from: expanded ? 0 : 90 + to: expanded ? 90 : 0 + duration: 100 + easing.type: Easing.OutQuart } + TableView.onPooled: indicatorAnimation.complete() + TableView.onReused: if (current) indicatorAnimation.start() + onExpandedChanged: indicator.rotation = expanded ? 90 : 0 - Text { + Rectangle { + id: background + anchors.fill: parent + color: row === treeView.currentRow ? palette.highlight : "black" + opacity: (treeView.alternatingRows && row % 2 !== 0) ? 0.3 : 0.1 + } + + Label { id: indicator - visible: treeDelegate.isTreeNode && treeDelegate.hasChildren - x: padding + (treeDelegate.depth * treeDelegate.indent) - anchors.verticalCenter: label.verticalCenter - text: "▸" - rotation: treeDelegate.expanded ? 90 : 0 + x: padding + (depth * indentation) + anchors.verticalCenter: parent.verticalCenter + visible: isTreeNode && hasChildren + text: "▶" + + TapHandler { + onSingleTapped: { + let index = treeView.index(row, column) + treeView.selectionModel.setCurrentIndex(index, ItemSelectionModel.NoUpdate) + treeView.toggleExpanded(row) + } + } } - Text { + Label { id: label - x: padding + (treeDelegate.isTreeNode ? (treeDelegate.depth + 1) * treeDelegate.indent : 0) - width: treeDelegate.width - treeDelegate.padding - x + x: padding + (isTreeNode ? (depth + 1) * indentation : 0) + anchors.verticalCenter: parent.verticalCenter + width: parent.width - padding - x clip: true text: model.display } diff --git a/src/quick/doc/src/concepts/input/focus.qdoc b/src/quick/doc/src/concepts/input/focus.qdoc index 225eea7788..8c6f73024b 100644 --- a/src/quick/doc/src/concepts/input/focus.qdoc +++ b/src/quick/doc/src/concepts/input/focus.qdoc @@ -18,8 +18,8 @@ scope based extension to Qt's traditional keyboard focus model. When the user presses or releases a key, the following occurs: \list 1 \li Qt receives the key action and generates a key event. -\li If a \l QQuickWindow is the active window, the key event -is delivered to it. +\li If a \l QQuickWindow is the \l{QGuiApplication::focusWindow()}{focus window} +of the application, the key event is delivered to it. \li The key event is delivered by the scene to the \l Item with \e {active focus}. If no item has active focus, the key event is ignored. \li If the \l QQuickItem with active focus accepts the key event, propagation diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp index 44db9d4f8b..d2c38179a3 100644 --- a/src/quick/handlers/qquicksinglepointhandler.cpp +++ b/src/quick/handlers/qquicksinglepointhandler.cpp @@ -117,11 +117,16 @@ void QQuickSinglePointHandler::handlePointerEventImpl(QPointerEvent *event) void QQuickSinglePointHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point) { - if (point.state() != QEventPoint::Released) - return; + if (point.state() == QEventPoint::Released) { + // If it's a mouse or tablet event, with buttons, + // do not deactivate unless all acceptable buttons are released. + if (event->isSinglePointEvent()) { + const Qt::MouseButtons releasedButtons = static_cast<QSinglePointEvent *>(event)->buttons(); + if ((releasedButtons & acceptedButtons()) != Qt::NoButton) + return; + } - const Qt::MouseButtons releasedButtons = static_cast<QSinglePointEvent *>(event)->buttons(); - if ((releasedButtons & acceptedButtons()) == Qt::NoButton) { + // Deactivate this handler on release setExclusiveGrab(event, point, false); d_func()->reset(); } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 7cc03936fc..17c77397a0 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3045,8 +3045,8 @@ void QQuickItemPrivate::derefWindow() paintNode = nullptr; for (int ii = 0; ii < childItems.size(); ++ii) { - QQuickItem *child = childItems.at(ii); - QQuickItemPrivate::get(child)->derefWindow(); + if (QQuickItem *child = childItems.at(ii)) + QQuickItemPrivate::get(child)->derefWindow(); } dirty(Window); @@ -4314,7 +4314,11 @@ void QQuickItem::dropEvent(QDropEvent *event) This method will only be called if filtersChildMouseEvents() is \c true. Return \c true if the specified \a event should not be passed on to the - specified child \a item, and \c false otherwise. + specified child \a item, and \c false otherwise. If you return \c true, you + should also \l {QEvent::accept()}{accept} or \l {QEvent::ignore()}{ignore} + the \a event, to signal if event propagation should stop or continue. + The \a event will, however, always be sent to all childMouseEventFilters + up the parent chain. \note Despite the name, this function filters all QPointerEvent instances during delivery to all children (typically mouse, touch, and tablet @@ -4653,7 +4657,7 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem * \input item.qdocinc mapping If \a item is a \c null value, this maps the point or rect from the coordinate system of - the root QML view. + the \l{Scene Coordinates}{scene}. The versions accepting point and rect are since Qt 5.15. */ @@ -4711,7 +4715,7 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const \input item.qdocinc mapping If \a item is a \c null value, this maps the point or rect to the coordinate system of the - root QML view. + \l{Scene Coordinates}{scene}. The versions accepting point and rect are since Qt 5.15. */ @@ -8956,6 +8960,16 @@ QDebug operator<<(QDebug debug, const QRectF rect(item->position(), QSizeF(item->width(), item->height())); debug << item->metaObject()->className() << '(' << static_cast<void *>(item); + + // Deferred properties will cause recursion when calling nameForObject + // before the component is completed, so guard against this situation. + if (item->isComponentComplete()) { + if (QQmlContext *context = qmlContext(item)) { + const auto objectId = context->nameForObject(item); + if (!objectId.isEmpty()) + debug << ", id=" << objectId; + } + } if (!item->objectName().isEmpty()) debug << ", name=" << item->objectName(); debug << ", parent=" << static_cast<void *>(item->parentItem()) diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 6a1f8db8c8..faaedbf990 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -473,6 +473,10 @@ private: friend class QAccessibleQuickItem; friend class QQuickAccessibleAttached; friend class QQuickAnchorChanges; +#ifndef QT_NO_DEBUG_STREAM + friend Q_QUICK_EXPORT QDebug operator<<(QDebug debug, QQuickItem *item); +#endif + Q_DISABLE_COPY(QQuickItem) Q_DECLARE_PRIVATE(QQuickItem) }; diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 7dbbea0736..ee2bf7ac39 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1846,7 +1846,8 @@ bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e) const bool filtered = stealThisEvent || grabberDisabled; if (filtered) - pe->setAccepted(false); + pe->setAccepted(stealThisEvent && grabber == this && grabber->isEnabled()); + return filtered; } else if (d->timer.isValid()) { d->timer.invalidate(); diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 3a2fbfef0d..edc8363b49 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -204,7 +204,7 @@ void QQuickTextPrivate::setBottomPadding(qreal value, bool reset) Used to decide if the Text should use antialiasing or not. Only Text with renderType of Text.NativeRendering can disable antialiasing. - The default is true. + The default is \c true. */ void QQuickText::q_updateLayout() @@ -1301,8 +1301,8 @@ void QQuickTextPrivate::updateDocumentText() \inherits Item \brief Specifies how to add formatted text to a scene. - Text items can display both plain and rich text. For example, red text with - a specific font and size can be defined like this: + Text items can display both plain and rich text. For example, you can define + red text with a specific font and size like this: \qml Text { @@ -1313,25 +1313,47 @@ void QQuickTextPrivate::updateDocumentText() } \endqml - Rich text is defined using HTML-style markup: + Use HTML-style markup or Markdown to define rich text: + \if defined(onlinedocs) + \tab {build-qt-app}{tab-html}{HTML-style}{checked} + \tab {build-qt-app}{tab-md}{Markdown}{} + \tabcontent {tab-html} + \else + \section1 Using HTML-style + \endif \qml Text { text: "<b>Hello</b> <i>World!</i>" } \endqml + \if defined(onlinedocs) + \endtabcontent + \tabcontent {tab-md} + \else + \section1 Using Markdown + \endif + \qml + Text { + text: "**Hello** *World!*" + } + \endqml + \if defined(onlinedocs) + \endtabcontent + \endif \image declarative-text.png - If height and width are not explicitly set, Text will attempt to determine how - much room is needed and set it accordingly. Unless \l wrapMode is set, it will always - prefer width to height (all text will be placed on a single line). + If height and width are not explicitly set, Text will try to determine how + much room is needed and set it accordingly. Unless \l wrapMode is set, it + will always prefer width to height (all text will be placed on a single + line). - The \l elide property can alternatively be used to fit a single line of - plain text to a set width. + To fit a single line of plain text to a set width, you can use the \l elide + property. - Note that the \l{Supported HTML Subset} is limited. Also, if the text contains - HTML img tags that load remote images, the text is reloaded. + Note that the \l{Supported HTML Subset} is limited. Also, if the text + contains HTML img tags that load remote images, the text is reloaded. Text provides read-only text. For editable text, see \l TextEdit. @@ -1359,7 +1381,7 @@ QQuickText::~QQuickText() \qmlproperty bool QtQuick::Text::clip This property holds whether the text is clipped. - Note that if the text does not fit in the bounding rectangle it will be abruptly chopped. + Note that if the text does not fit in the bounding rectangle, it will be abruptly chopped. If you want to display potentially long text in a limited space, you probably want to use \c elide instead. */ @@ -1448,7 +1470,8 @@ QQuickText::~QQuickText() Sets the family name of the font. - The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + The family name is case insensitive and may optionally include a foundry + name, for example "Helvetica [Cronyx]". If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. If the family isn't available a family will be set using the font matching algorithm. */ @@ -1572,7 +1595,7 @@ QQuickText::~QQuickText() \value Font.PreferDefaultHinting Use the default hinting level for the target platform. \value Font.PreferNoHinting If possible, render text without hinting the outlines of the glyphs. The text layout will be typographically accurate, using the same metrics - as are used e.g. when printing. + as are used, for example, when printing. \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting, but align glyphs to the pixel grid in the vertical direction. The text will appear crisper on displays where the density is too low to give an accurate rendering @@ -1609,7 +1632,7 @@ QQuickText::~QQuickText() Sometimes, a font will apply complex rules to a set of characters in order to display them correctly. In some writing systems, such as Brahmic scripts, this is - required in order for the text to be legible, but in e.g. Latin script, it is merely + required in order for the text to be legible, but in for example Latin script, it is merely a cosmetic feature. Setting the \c preferShaping property to false will disable all such features when they are not required, which will improve performance in most cases. @@ -1619,6 +1642,7 @@ QQuickText::~QQuickText() Text { text: "Some text"; font.preferShaping: false } \endqml */ + QFont QQuickText::font() const { Q_D(const QQuickText); @@ -2139,7 +2163,7 @@ void QQuickText::resetMaximumLineCount() \l {https://guides.github.com/features/mastering-markdown/}{GitHub} extensions for tables and task lists (since 5.14) - If the text format is \c Text.AutoText the Text item + If the text format is \c Text.AutoText, the Text item will automatically determine whether the text should be treated as styled text. This determination is made using Qt::mightBeRichText(), which can detect the presence of an HTML tag on the first line of text, @@ -2268,7 +2292,7 @@ void QQuickText::setElideMode(QQuickText::TextElideMode mode) /*! \qmlproperty url QtQuick::Text::baseUrl - This property specifies a base URL which is used to resolve relative URLs + This property specifies a base URL that is used to resolve relative URLs within the text. Urls are resolved to be within the same directory as the target of the base @@ -2560,7 +2584,7 @@ void QQuickText::updatePolish() \qmlproperty real QtQuick::Text::contentWidth Returns the width of the text, including width past the width - which is covered due to insufficient wrapping if WrapMode is set. + that is covered due to insufficient wrapping if WrapMode is set. */ qreal QQuickText::contentWidth() const { @@ -2572,7 +2596,7 @@ qreal QQuickText::contentWidth() const \qmlproperty real QtQuick::Text::contentHeight Returns the height of the text, including height past the height - which is covered due to there being more text than fits in the set height. + that is covered due to there being more text than fits in the set height. */ qreal QQuickText::contentHeight() const { @@ -3310,7 +3334,7 @@ void QQuickText::resetBottomPadding() */ /*! - \qmlproperty string QtQuick::Text::fontInfo.pixelSize + \qmlproperty int QtQuick::Text::fontInfo.pixelSize \since 5.9 The pixel size of the font info that has been resolved for the current font @@ -3347,7 +3371,7 @@ QJSValue QQuickText::fontInfo() const in a text flow. Note that the advance can be negative if the text flows from - the right to the left. + right to left. */ QSizeF QQuickText::advance() const { diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 69541accc7..441335ec7a 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -704,9 +704,8 @@ QQuickTextEdit::HAlignment QQuickTextEdit::hAlign() const void QQuickTextEdit::setHAlign(HAlignment align) { Q_D(QQuickTextEdit); - bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; - d->hAlignImplicit = false; - if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + + if (d->setHAlign(align, true) && isComponentComplete()) { d->updateDefaultTextOption(); updateSize(); } @@ -741,21 +740,34 @@ QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign() const return effectiveAlignment; } -bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment alignment, bool forceAlign) +bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align, bool forceAlign) { Q_Q(QQuickTextEdit); - if (hAlign != alignment || forceAlign) { - QQuickTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); - hAlign = alignment; - emit q->horizontalAlignmentChanged(alignment); - if (oldEffectiveHAlign != q->effectiveHAlign()) - emit q->effectiveHorizontalAlignmentChanged(); + if (hAlign == align && !forceAlign) + return false; + + const bool wasImplicit = hAlignImplicit; + const auto oldEffectiveHAlign = q->effectiveHAlign(); + + hAlignImplicit = !forceAlign; + if (hAlign != align) { + hAlign = align; + emit q->horizontalAlignmentChanged(align); + } + + if (q->effectiveHAlign() != oldEffectiveHAlign) { + emit q->effectiveHorizontalAlignmentChanged(); return true; } + + if (forceAlign && wasImplicit) { + // QTBUG-120052 - when horizontal text alignment is set explicitly, + // we need notify any other controls that may depend on it, like QQuickPlaceholderText + emit q->effectiveHorizontalAlignmentChanged(); + } return false; } - Qt::LayoutDirection QQuickTextEditPrivate::textDirection(const QString &text) const { const QChar *character = text.constData(); @@ -778,20 +790,22 @@ Qt::LayoutDirection QQuickTextEditPrivate::textDirection(const QString &text) co bool QQuickTextEditPrivate::determineHorizontalAlignment() { Q_Q(QQuickTextEdit); - if (hAlignImplicit && q->isComponentComplete()) { - Qt::LayoutDirection direction = contentDirection; + if (!hAlignImplicit || !q->isComponentComplete()) + return false; + + Qt::LayoutDirection direction = contentDirection; #if QT_CONFIG(im) - if (direction == Qt::LayoutDirectionAuto) { - const QString preeditText = control->textCursor().block().layout()->preeditAreaText(); - direction = textDirection(preeditText); - } - if (direction == Qt::LayoutDirectionAuto) - direction = qGuiApp->inputMethod()->inputDirection(); + if (direction == Qt::LayoutDirectionAuto) { + const QString preeditText = control->textCursor().block().layout()->preeditAreaText(); + direction = textDirection(preeditText); + } + if (direction == Qt::LayoutDirectionAuto) + direction = qGuiApp->inputMethod()->inputDirection(); #endif - return setHAlign(direction == Qt::RightToLeft ? QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft); - } - return false; + const auto implicitHAlign = direction == Qt::RightToLeft ? + QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft; + return setHAlign(implicitHAlign); } void QQuickTextEditPrivate::mirrorChange() @@ -1483,14 +1497,35 @@ void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints) void QQuickTextEdit::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QQuickTextEdit); - if (!d->inLayout && ((newGeometry.width() != oldGeometry.width() && widthValid()) - || (newGeometry.height() != oldGeometry.height() && heightValid()))) { + if (!d->inLayout && ((newGeometry.width() != oldGeometry.width()) + || (newGeometry.height() != oldGeometry.height()))) { updateSize(); updateWholeDocument(); - moveCursorDelegate(); + if (widthValid() || heightValid()) + moveCursorDelegate(); } QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry); +} + +void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickTextEdit); + Q_UNUSED(value); + switch (change) { + case ItemDevicePixelRatioHasChanged: + if (d->renderType == NativeRendering) { + // Native rendering optimizes for a given pixel grid, so its results must not be scaled. + // Text layout code respects the current device pixel ratio automatically, we only need + // to rerun layout after the ratio changed. + updateSize(); + updateWholeDocument(); + } + break; + default: + break; + } + QQuickImplicitSizeItem::itemChange(change, value); } /*! diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index c98d062c6b..a8223780fe 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -366,6 +366,7 @@ protected: #endif void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void itemChange(ItemChange change, const ItemChangeData &value) override; bool event(QEvent *) override; void keyPressEvent(QKeyEvent *) override; diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 628177daf1..7aa590feb9 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -533,9 +533,8 @@ QQuickTextInput::HAlignment QQuickTextInput::hAlign() const void QQuickTextInput::setHAlign(HAlignment align) { Q_D(QQuickTextInput); - bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; - d->hAlignImplicit = false; - if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + + if (d->setHAlign(align, true) && isComponentComplete()) { d->updateLayout(); updateCursorRectangle(); } @@ -570,17 +569,34 @@ QQuickTextInput::HAlignment QQuickTextInput::effectiveHAlign() const return effectiveAlignment; } -bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment alignment, bool forceAlign) +bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment align, bool forceAlign) { Q_Q(QQuickTextInput); - if ((hAlign != alignment || forceAlign) && alignment <= QQuickTextInput::AlignHCenter) { // justify not supported - QQuickTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); - hAlign = alignment; - emit q->horizontalAlignmentChanged(alignment); - if (oldEffectiveHAlign != q->effectiveHAlign()) - emit q->effectiveHorizontalAlignmentChanged(); + if (align > QQuickTextInput::AlignHCenter) + return false; // justify is not supported + + if (hAlign == align && !forceAlign) + return false; + + const bool wasImplicit = hAlignImplicit; + const auto oldEffectiveHAlign = q->effectiveHAlign(); + + hAlignImplicit = !forceAlign; + if (hAlign != align) { + hAlign = align; + emit q->horizontalAlignmentChanged(align); + } + + if (q->effectiveHAlign() != oldEffectiveHAlign) { + emit q->effectiveHorizontalAlignmentChanged(); return true; } + + if (forceAlign && wasImplicit) { + // QTBUG-120052 - when horizontal text alignment is set explicitly, + // we need notify any other controls that may depend on it, like QQuickPlaceholderText + emit q->effectiveHorizontalAlignmentChanged(); + } return false; } @@ -624,16 +640,19 @@ Qt::LayoutDirection QQuickTextInputPrivate::layoutDirection() const bool QQuickTextInputPrivate::determineHorizontalAlignment() { - if (hAlignImplicit) { - // if no explicit alignment has been set, follow the natural layout direction of the text - Qt::LayoutDirection direction = textDirection(); + if (!hAlignImplicit) + return false; + + // if no explicit alignment has been set, follow the natural layout direction of the text + Qt::LayoutDirection direction = textDirection(); #if QT_CONFIG(im) - if (direction == Qt::LayoutDirectionAuto) - direction = QGuiApplication::inputMethod()->inputDirection(); + if (direction == Qt::LayoutDirectionAuto) + direction = QGuiApplication::inputMethod()->inputDirection(); #endif - return setHAlign(direction == Qt::RightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft); - } - return false; + + const auto implicitHAlign = direction == Qt::RightToLeft ? + QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft; + return setHAlign(implicitHAlign); } QQuickTextInput::VAlignment QQuickTextInput::vAlign() const @@ -1753,6 +1772,26 @@ void QQuickTextInput::geometryChange(const QRectF &newGeometry, QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry); } +void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickTextInput); + Q_UNUSED(value); + switch (change) { + case ItemDevicePixelRatioHasChanged: + if (d->renderType == NativeRendering) { + // Native rendering optimizes for a given pixel grid, so its results must not be scaled. + // Text layout code respects the current device pixel ratio automatically, we only need + // to rerun layout after the ratio changed. + d->updateLayout(); + } + break; + + default: + break; + } + QQuickImplicitSizeItem::itemChange(change, value); +} + void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int preeditLength) { Q_Q(QQuickTextInput); diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 433eed4d78..850d29cdc6 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -349,6 +349,7 @@ protected: #endif void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void itemChange(ItemChange change, const ItemChangeData &value) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp index df21174648..88373a1706 100644 --- a/src/quick/items/qquicktreeview.cpp +++ b/src/quick/items/qquicktreeview.cpp @@ -42,7 +42,7 @@ a set of properties that can be used to position and render each node in the tree correctly. - An example of a custom delegate is shown below: + An example of a custom delegate with an animating indicator is shown below: \snippet qml/treeview/qml-customdelegate.qml 0 diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ff21ef004a..44c51f6fc4 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3411,6 +3411,8 @@ void QQuickWindow::endExternalCommands() Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}. + The default value is \c false, unless overridden by setting \l visibility. + \sa visibility */ diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index eb6e38a3c8..d1394ee3b1 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -1222,8 +1222,13 @@ void Renderer::nodeWasRemoved(Node *node) if (e) { e->removed = true; m_elementsToDelete.add(e); - if (m_renderNodeElements.isEmpty()) + if (m_renderNodeElements.isEmpty()) { m_forceNoDepthBuffer = false; + // Must have a full rebuild given useDepthBuffer() now returns + // a different value than before, meaning there can once again + // be an opaque pass. + m_rebuild |= FullRebuild; + } if (e->batch != nullptr) e->batch->needsPurge = true; diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp index f9133455c8..445f338bbd 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -47,11 +47,14 @@ void QSGDefaultGlyphNode::update() QRawFont font = m_glyphs.rawFont(); QMargins margins(0, 0, 0, 0); - if (m_style == QQuickText::Normal) { + const auto *fontEngine = QRawFontPrivate::get(font)->fontEngine; + const bool isColorFont = fontEngine->glyphFormat == QFontEngine::Format_ARGB; + + if (m_style == QQuickText::Normal || isColorFont) { QFontEngine::GlyphFormat glyphFormat; // Don't try to override glyph format of color fonts - if (QRawFontPrivate::get(font)->fontEngine->glyphFormat == QFontEngine::Format_ARGB) { + if (isColorFont) { glyphFormat = QFontEngine::Format_None; } else { switch (m_preferredAntialiasingMode) { diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag b/src/quick/scenegraph/shaders_ng/24bittextmask.frag index cb98a47157..4c8a100321 100644 --- a/src/quick/scenegraph/shaders_ng/24bittextmask.frag +++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag index 000adb619e..bfb632972d 100644 --- a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag +++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag b/src/quick/scenegraph/shaders_ng/8bittextmask.frag index 9507728803..a6aee4d5e4 100644 --- a/src/quick/scenegraph/shaders_ng/8bittextmask.frag +++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag index 9c0f36b81f..1452efa515 100644 --- a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag +++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag index 9e89d17219..8896557a16 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag index 4fe30b69ee..2d70cb1298 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag index 4dc7432e44..e5ba4f7889 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a_fwidth.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag index 44aea6b979..3d92da460c 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_fwidth.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag index 320c19973b..412b745903 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag index 0ddd1b40d1..36f3e0174f 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag index 8b124213a4..130e9f401f 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a_fwidth.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag index 40f519636e..f3f938ae77 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_fwidth.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag index a86f68c8cb..b61a2fd2b9 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag index 449647561f..a3968d2820 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag index 534ec9208c..5c01d91b58 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a_fwidth.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag index 04886d6b12..3702c255cb 100644 --- a/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag +++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_fwidth.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.frag b/src/quick/scenegraph/shaders_ng/flatcolor.frag index cac8f1fb17..cf412bd6fb 100644 --- a/src/quick/scenegraph/shaders_ng/flatcolor.frag +++ b/src/quick/scenegraph/shaders_ng/flatcolor.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag index 19dce21aa4..044e7a198c 100644 --- a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag +++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag index 0cfe63edbf..325e34e4cf 100644 --- a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag +++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag index 343e93f54f..df8e42ed22 100644 --- a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag +++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag index e5aef90fda..2d0c34ee99 100644 --- a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag +++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.frag b/src/quick/scenegraph/shaders_ng/opaquetexture.frag index 18dd7e0fcb..fd7f1eaf9d 100644 --- a/src/quick/scenegraph/shaders_ng/opaquetexture.frag +++ b/src/quick/scenegraph/shaders_ng/opaquetexture.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag b/src/quick/scenegraph/shaders_ng/outlinedtext.frag index 6a5e1aa28b..6ada0cefdc 100644 --- a/src/quick/scenegraph/shaders_ng/outlinedtext.frag +++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag index 853f08e8ee..f11ef430b0 100644 --- a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag +++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.frag b/src/quick/scenegraph/shaders_ng/shadereffect.frag index ef4bbe78f3..4ab6f8c3bb 100644 --- a/src/quick/scenegraph/shaders_ng/shadereffect.frag +++ b/src/quick/scenegraph/shaders_ng/shadereffect.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.frag b/src/quick/scenegraph/shaders_ng/smoothcolor.frag index 314a387922..656ef8cf51 100644 --- a/src/quick/scenegraph/shaders_ng/smoothcolor.frag +++ b/src/quick/scenegraph/shaders_ng/smoothcolor.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.frag b/src/quick/scenegraph/shaders_ng/smoothtexture.frag index a7ddc57535..10ab0c0884 100644 --- a/src/quick/scenegraph/shaders_ng/smoothtexture.frag +++ b/src/quick/scenegraph/shaders_ng/smoothtexture.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/sprite.frag b/src/quick/scenegraph/shaders_ng/sprite.frag index 846958c71c..f3c921c569 100644 --- a/src/quick/scenegraph/shaders_ng/sprite.frag +++ b/src/quick/scenegraph/shaders_ng/sprite.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.frag b/src/quick/scenegraph/shaders_ng/stencilclip.frag index ec4d3a05b1..215af7d7b9 100644 --- a/src/quick/scenegraph/shaders_ng/stencilclip.frag +++ b/src/quick/scenegraph/shaders_ng/stencilclip.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag b/src/quick/scenegraph/shaders_ng/styledtext.frag index df14c09aed..cb0aa164ae 100644 --- a/src/quick/scenegraph/shaders_ng/styledtext.frag +++ b/src/quick/scenegraph/shaders_ng/styledtext.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag b/src/quick/scenegraph/shaders_ng/styledtext_a.frag index eda1062f2d..38ff10ec90 100644 --- a/src/quick/scenegraph/shaders_ng/styledtext_a.frag +++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag b/src/quick/scenegraph/shaders_ng/textmask.frag index 49023666ce..5d03f6ac86 100644 --- a/src/quick/scenegraph/shaders_ng/textmask.frag +++ b/src/quick/scenegraph/shaders_ng/textmask.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/texture.frag b/src/quick/scenegraph/shaders_ng/texture.frag index d4be87ec29..ebc92600dc 100644 --- a/src/quick/scenegraph/shaders_ng/texture.frag +++ b/src/quick/scenegraph/shaders_ng/texture.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.frag b/src/quick/scenegraph/shaders_ng/vertexcolor.frag index 314a387922..656ef8cf51 100644 --- a/src/quick/scenegraph/shaders_ng/vertexcolor.frag +++ b/src/quick/scenegraph/shaders_ng/vertexcolor.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag b/src/quick/scenegraph/shaders_ng/visualization.frag index 378afc2088..9b1acdf4a9 100644 --- a/src/quick/scenegraph/shaders_ng/visualization.frag +++ b/src/quick/scenegraph/shaders_ng/visualization.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 1ac87b6b74..83d5181c34 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -592,7 +592,7 @@ void QQuickUniformAnimatorJob::setTarget(QQuickItem *target) void QQuickUniformAnimatorJob::updateCurrentTime(int time) { - if (!m_effect) + if (!m_effect || m_target != m_effect) return; m_value = m_from + (m_to - m_from) * progress(time); @@ -617,7 +617,7 @@ void QQuickUniformAnimatorJob::postSync() void QQuickUniformAnimatorJob::invalidate() { - m_effect = nullptr; + } #endif diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h index fef29ac385..9a1ad53e7a 100644 --- a/src/quick/util/qquickanimatorjob_p.h +++ b/src/quick/util/qquickanimatorjob_p.h @@ -280,7 +280,7 @@ public: private: QByteArray m_uniform; - QQuickShaderEffect *m_effect = nullptr; + QPointer<QQuickShaderEffect> m_effect; }; #endif diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index f333a9828d..07506e68b7 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -2029,6 +2029,16 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event) } if (!relevantPassiveGrabbers.isEmpty()) deliverToPassiveGrabbers(relevantPassiveGrabbers, event); + + // Ensure that HoverHandlers are updated, in case no items got dirty so far and there's no update request + if (event->type() == QEvent::TouchUpdate) { + for (auto hoverItem : hoverItems) { + if (auto item = hoverItem.first) { + deliverHoverEventToItem(item, point.scenePosition(), point.sceneLastPosition(), + event->modifiers(), event->timestamp(), false); + } + } + } } if (done) @@ -2478,15 +2488,18 @@ bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *eve QQuickItemPrivate::get(receiver)->localizedTouchEvent(static_cast<QTouchEvent *>(event), true, &filteringParentTouchEvent); if (filteringParentTouchEvent.type() != QEvent::None) { qCDebug(lcTouch) << "letting parent" << filteringParent << "filter for" << receiver << &filteringParentTouchEvent; - if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) { + filtered = filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent); + if (filtered) { qCDebug(lcTouch) << "touch event intercepted by childMouseEventFilter of " << filteringParent; + event->setAccepted(filteringParentTouchEvent.isAccepted()); skipDelivery.append(filteringParent); - for (auto point : filteringParentTouchEvent.points()) { - const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point)); - if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab()) - event->setExclusiveGrabber(point, filteringParent); + if (event->isAccepted()) { + for (auto point : filteringParentTouchEvent.points()) { + const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point)); + if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab()) + event->setExclusiveGrabber(point, filteringParent); + } } - return true; } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) && !filteringParent->acceptTouchEvents()) { qCDebug(lcTouch) << "touch event NOT intercepted by childMouseEventFilter of " << filteringParent @@ -2522,17 +2535,17 @@ bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *eve // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. touchMouseId = tp.id(); touchMouseDevice = event->pointingDevice(); - if (filteringParent->childMouseEventFilter(receiver, &mouseEvent)) { + filtered = filteringParent->childMouseEventFilter(receiver, &mouseEvent); + if (filtered) { qCDebug(lcTouch) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; + event->setAccepted(mouseEvent.isAccepted()); skipDelivery.append(filteringParent); - if (t != QEvent::MouseButtonRelease) { + if (event->isAccepted() && event->isBeginEvent()) { qCDebug(lcTouchTarget) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent; filteringParentTouchEvent.setExclusiveGrabber(tp, filteringParent); touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set - if (mouseEvent.isAccepted()) - filteringParent->grabMouse(); + filteringParent->grabMouse(); } - filtered = true; } if (touchMouseUnset) // Now that we're done sending a synth mouse event, and it wasn't grabbed, diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp index 21f25d8e3d..9717f682de 100644 --- a/src/quick/util/qquickstategroup.cpp +++ b/src/quick/util/qquickstategroup.cpp @@ -181,19 +181,25 @@ void QQuickStateGroupPrivate::replace_states(QQmlListProperty<QQuickState> *list if (oldState != state) { if (oldState) oldState->setStateGroup(nullptr); - state->setStateGroup(self); + + if (state) + state->setStateGroup(self); d->states.replace(index, state); if (!oldState || d->currentState == oldState->name()) - d->setCurrentStateInternal(state->name(), true); + d->setCurrentStateInternal(state ? state->name() : QString(), true); } } void QQuickStateGroupPrivate::removeLast_states(QQmlListProperty<QQuickState> *list) { auto *d = qobject_cast<QQuickStateGroup *>(list->object)->d_func(); - if (d->currentState == d->states.last()->name()) - d->setCurrentStateInternal(d->states.size() > 1 ? d->states.first()->name() : QString(), true); - d->states.last()->setStateGroup(nullptr); + if (QQuickState *last = d->states.last()) { + if (d->currentState == last->name()) { + QQuickState *first = d->states.size() > 1 ? d->states.first() : nullptr; + d->setCurrentStateInternal(first ? first->name() : QString(), true); + } + last->setStateGroup(nullptr); + } d->states.removeLast(); } @@ -304,6 +310,9 @@ void QQuickStateGroup::componentComplete() names.reserve(d->states.size()); for (int ii = 0; ii < d->states.size(); ++ii) { QQuickState *state = d->states.at(ii); + if (!state) + continue; + if (!state->isNamed()) state->setName(QLatin1String("anonymousState") + QString::number(++d->unnamedCount)); @@ -342,7 +351,7 @@ bool QQuickStateGroupPrivate::updateAutoState() bool revert = false; for (int ii = 0; ii < states.size(); ++ii) { QQuickState *state = states.at(ii); - if (state->isWhenKnown()) { + if (state && state->isWhenKnown()) { if (state->isNamed()) { bool whenValue = state->when(); const QQmlProperty whenProp(state, u"when"_s); @@ -477,9 +486,9 @@ void QQuickStateGroupPrivate::setCurrentStateInternal(const QString &state, QQuickState *oldState = nullptr; if (!currentState.isEmpty()) { - for (int ii = 0; ii < states.size(); ++ii) { - if (states.at(ii)->name() == currentState) { - oldState = states.at(ii); + for (QQuickState *state : std::as_const(states)) { + if (state && state->name() == currentState) { + oldState = state; break; } } @@ -489,9 +498,9 @@ void QQuickStateGroupPrivate::setCurrentStateInternal(const QString &state, emit q->stateChanged(currentState); QQuickState *newState = nullptr; - for (int ii = 0; ii < states.size(); ++ii) { - if (states.at(ii)->name() == currentState) { - newState = states.at(ii); + for (QQuickState *state : std::as_const(states)) { + if (state && state->name() == currentState) { + newState = state; break; } } @@ -513,9 +522,8 @@ void QQuickStateGroupPrivate::setCurrentStateInternal(const QString &state, QQuickState *QQuickStateGroup::findState(const QString &name) const { Q_D(const QQuickStateGroup); - for (int i = 0; i < d->states.size(); ++i) { - QQuickState *state = d->states.at(i); - if (state->name() == name) + for (QQuickState *state : std::as_const(d->states)) { + if (state && state->name() == name) return state; } diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml index 7158232081..a961debd44 100644 --- a/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml +++ b/src/quickcontrols/doc/snippets/qtquickcontrols-progressbar-custom.qml @@ -21,11 +21,40 @@ ProgressBar { implicitWidth: 200 implicitHeight: 4 + // Progress indicator for determinate state. Rectangle { width: control.visualPosition * parent.width height: parent.height radius: 2 color: "#17a81a" + visible: !control.indeterminate + } + + // Scrolling animation for indeterminate state. + Item { + anchors.fill: parent + visible: control.indeterminate + clip: true + + Row { + spacing: 20 + + Repeater { + model: control.width / 40 + 1 + + Rectangle { + color: "#17a81a" + width: 20 + height: control.height + } + } + XAnimator on x { + from: 0 + to: -40 + loops: Animation.Infinite + running: control.indeterminate + } + } } } } diff --git a/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml b/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml index a53941e824..472b1b79d8 100644 --- a/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml +++ b/src/quickcontrols/doc/snippets/qtquickcontrols-selectionrectangle.qml @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick - -import QtQuick import QtQuick.Window import QtQuick.Controls import QtQml.Models diff --git a/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc b/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc index 803425a88b..dc02dffe39 100644 --- a/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc +++ b/src/quickcontrols/doc/src/includes/qquickmaterialstyle.qdocinc @@ -77,3 +77,8 @@ for example \c "Grey". \endtable //! [env] + +//! [placeholder-text-multiple-lines] +As per the Material guidelines, placeholder text should be kept short and not +take up multiple lines. +//! [placeholder-text-multiple-lines] diff --git a/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc index 26fab0a2ce..580cb7bf68 100644 --- a/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc +++ b/src/quickcontrols/doc/src/qtquickcontrols-configuration.qdoc @@ -164,12 +164,39 @@ is possible to provide a different configuration file for different platforms and locales. See \l QFileSelector documentation for more details. - Finally, the \c .qrc file must be listed in the application's \c .pro file so that - the build system knows about it. For example: - - \code - RESOURCES = application.qrc + Finally, the \c .qrc file must be listed in the application's build file. + For example: + + \if defined(onlinedocs) + \tab {build-qt-app}{tab-cmake}{CMakeLists.txt (CMake)}{checked} + \tab {build-qt-app}{tab-qmake}{.pro (qmake)}{} + \tabcontent {tab-cmake} + \else + \section2 Using CMake + \endif + \badcode + set(CMAKE_AUTORCC ON) + qt_add_executable(my_app + application.qrc + main.cpp + ... + ) \endcode + \if defined(onlinedocs) + \endtabcontent + \tabcontent {tab-qmake} + \else + \section2 Using qmake + \endif + \badcode + RESOURCES = application.qrc + ... + \endcode + \if defined(onlinedocs) + \endtabcontent + \endif + + See also: \l {Build System Integration} \section1 Related Information diff --git a/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc index 19a2619164..83c22940c8 100644 --- a/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc +++ b/src/quickcontrols/doc/src/qtquickcontrols-customize.qdoc @@ -183,27 +183,58 @@ \section2 Style-specific C++ Extensions - Sometimes you may need to use C++ to extend your custom style. There are two - ways to expose such types to QML: + Sometimes you may need to use C++ to extend your custom style. \list - \li If the style that uses the type is the only style used by an application, - it's enough to register it with the QML engine via qmlRegisterType(): + \li If the style that uses the type is the only style used by an + application, register the type with the QML engine by adding the QML_ELEMENT + macro and making the file part of your QML module: + \br + \br + + \if defined(onlinedocs) + \tab {expose-cpp-to-qml}{tab-cmake}{CMake}{checked} + \tab {expose-cpp-to-qml}{tab-qmake}{qmake}{} + \tabcontent {tab-cmake} + \else + \section3 Using CMake + \endif + \badcode + qt_add_qml_module(ACoolItem + URI MyItems + VERSION 1.0 + SOURCES + acoolcppitem.cpp acoolcppitem.h + ) + \endcode + \if defined(onlinedocs) + \endtabcontent + \tabcontent {tab-qmake} + \else + \section3 Using QMake + \endif + \code + CONFIG += qmltypes + QML_IMPORT_NAME = MyItems + QML_IMPORT_MAJOR_VERSION = 1 + \endcode + + If the header the class is declared in is not accessible from your + project's include path, you may have to amend the include path so + that the generated registration code can be compiled. \code - qmlRegisterType<ACoolCppItem>("MyApp", 1, 0, "ACoolItem"); + INCLUDEPATH += MyItems \endcode + \if defined(onlinedocs) + \endtabcontent + \endif - See \l {Using C++ Data From QML} for more information about this. + See \l {Defining QML Types from C++} and \l {Building a QML application} + for more information. \li If the style that uses the type is one of many styles used by an - application, it may be better to only register it when necessary. This - is the point at which it would make sense to implement your own - \l {Creating C++ Plugins for QML}{QML plugin}. - - Using a plugin as part of your style is not that much different from - using a set of QML files. The only difference is that the plugin and - its \c qmldir file must be present in the same directory as the QML - files. + application, consider putting each style into a separate module. The + modules will then be loaded on demand. \endlist \section3 Considerations for custom styles @@ -786,6 +817,9 @@ \snippet qtquickcontrols-progressbar-custom.qml file + Above, the content item is also animated to represent an + \l {ProgressBar::}{indeterminate} progress bar state. + \section2 Customizing RadioButton diff --git a/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc index 01c9b26bd2..a03970d1d8 100644 --- a/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc +++ b/src/quickcontrols/doc/src/qtquickcontrols-fileselectors.qdoc @@ -8,7 +8,7 @@ \l {QFileSelector}{File selectors} provide a convenient way of selecting file variants. Qt offers the platform name and the locale as built-in selectors. Qt Quick Controls extends the built-in selectors with the name - (lowercase) of the style that an application is running with. + (Capitalized) of the style that an application is running with. By using file selectors, style-specific tweaks can be applied without creating a hard dependency to a style. From the available file variants, @@ -77,7 +77,7 @@ \code // +Material/CustomButton.qml import QtQuick - import QtGraphicalEffects + import QtQuick.Effects import QtQuick.Controls import QtQuick.Controls.Material @@ -93,11 +93,12 @@ radius: width / 2 layer.enabled: control.enabled - layer.effect: DropShadow { - verticalOffset: 1 - color: Material.dropShadowColor - samples: control.pressed ? 20 : 10 - spread: 0.5 + layer.effect: MultiEffect { + shadowEnabled: true + shadowHorizontalOffset: 3 + shadowVerticalOffset: 3 + shadowColor: Material.dropShadowColor + shadowBlur: control.pressed ? 0.8 : 0.4 } } } diff --git a/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc b/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc index b919b2c1b7..c361f5f1a7 100644 --- a/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc +++ b/src/quickcontrols/doc/src/qtquickcontrols-material.qdoc @@ -32,7 +32,7 @@ \section1 Detailed Description \target detailed-desc-material - The Material style is based on the \l {https://www.google.com/design/spec/material-design/introduction.html} + The Material style is based on the \l {https://m3.material.io} {Google Material Design Guidelines}. It allows for a unified experience across platforms and device sizes. @@ -48,6 +48,10 @@ may occur due to differences in available system fonts and font rendering engines. + \note As the Material Design Guidelines change over time, this style may + change certain padding or font values, for example, in order to maintain + consistency with the guidelines. + \section2 Customization The Material style supports several customizable attributes. Some of these @@ -255,6 +259,8 @@ \l {Item::}{clip} to \c true. To avoid this, \l {Control::}{topInset} is set to an appropriate value in these cases. + \include qquickmaterialstyle.qdocinc placeholder-text-multiple-lines + \section3 TextField The same \l {material-control-specific-notes-textarea}{issue with clipping} @@ -262,6 +268,8 @@ this, \l {Control::}{topInset} is set to an appropriate value when the TextField sets clip to \c true. + \include qquickmaterialstyle.qdocinc placeholder-text-multiple-lines + \section1 Attached Property Documentation \styleproperty {Material.accent} {color} {material-accent-attached-prop} @@ -319,7 +327,7 @@ In the following example, the elevation of the pane is set to \c 6 in order to achieve the look of an - \l {https://material.google.com/components/cards.html}{elevated card}: + \l {https://m3.material.io/components/cards/overview}{elevated card}: \table \row diff --git a/src/quickcontrols/fusion/TabButton.qml b/src/quickcontrols/fusion/TabButton.qml index add96a8fa3..088a6e4529 100644 --- a/src/quickcontrols/fusion/TabButton.qml +++ b/src/quickcontrols/fusion/TabButton.qml @@ -24,6 +24,8 @@ T.TabButton { z: checked + topInset: control.checked || control.TabBar.position !== T.TabBar.Header ? 0 : 2 + contentItem: IconLabel { spacing: control.spacing mirrored: control.mirrored @@ -35,10 +37,15 @@ T.TabButton { color: control.palette.buttonText } + background: Rectangle { - y: control.checked || control.TabBar.position !== T.TabBar.Header ? 0 : 2 - implicitHeight: 21 - height: control.height - (control.checked ? 0 : 2) + implicitHeight: 19 + + // TODO: Find out why the following binding fails to update the first tab button + // See QTBUG-108807 + // y: control.checked || control.TabBar.position !== T.TabBar.Header ? 0 : 2 + // implicitHeight: 21 + // height: control.height - (control.checked ? 0 : 2) border.color: Qt.lighter(Fusion.outline(control.palette), 1.1) diff --git a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp index cce69b0e4b..8e58b09ed3 100644 --- a/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp +++ b/src/quickcontrols/fusion/impl/qquickfusionbusyindicator.cpp @@ -71,8 +71,17 @@ void QQuickFusionBusyIndicator::itemChange(ItemChange change, const ItemChangeDa { QQuickPaintedItem::itemChange(change, data); - if (change == ItemOpacityHasChanged && qFuzzyIsNull(data.realValue)) - setVisible(false); + switch (change) { + case ItemOpacityHasChanged: + if (qFuzzyIsNull(data.realValue)) + setVisible(false); + break; + case ItemVisibleHasChanged: + update(); + break; + default: + break; + } } QT_END_NAMESPACE diff --git a/src/quickcontrols/imagine/qquickimaginetheme.cpp b/src/quickcontrols/imagine/qquickimaginetheme.cpp index 921034962a..7df719c84c 100644 --- a/src/quickcontrols/imagine/qquickimaginetheme.cpp +++ b/src/quickcontrols/imagine/qquickimaginetheme.cpp @@ -5,13 +5,17 @@ #include <QtQuickTemplates2/private/qquicktheme_p.h> +#include <qfontdatabase.h> + QT_BEGIN_NAMESPACE void QQuickImagineTheme::initialize(QQuickTheme *theme) { - QFont systemFont; - systemFont.setFamilies(QStringList{QLatin1String("Open Sans")}); - theme->setFont(QQuickTheme::System, systemFont); + const auto defaultFontFamily = QLatin1String("Open Sans"); + if (QFontDatabase::hasFamily(defaultFontFamily)) { + const QFont systemFont(QStringList{defaultFontFamily}); + theme->setFont(QQuickTheme::System, systemFont); + } const QColor accentColor = QColor::fromRgb(0x4fc1e9); const QColor windowTextColor = QColor::fromRgb(0x434a54); diff --git a/src/quickcontrols/material/Button.qml b/src/quickcontrols/material/Button.qml index 45003898de..c02d9f426a 100644 --- a/src/quickcontrols/material/Button.qml +++ b/src/quickcontrols/material/Button.qml @@ -17,10 +17,9 @@ T.Button { topInset: 6 bottomInset: 6 - verticalPadding: 14 - // https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382 - leftPadding: !flat ? (!hasIcon ? 24 : 16) : 12 - rightPadding: !flat ? (text === "" ? 16 : 24) : (!hasIcon ? 12 : (text === "" ? 12 : 16)) + verticalPadding: Material.buttonVerticalPadding + leftPadding: Material.buttonLeftPadding(flat, hasIcon) + rightPadding: Material.buttonRightPadding(flat, hasIcon, text !== "") spacing: 8 icon.width: 24 diff --git a/src/quickcontrols/material/Dialog.qml b/src/quickcontrols/material/Dialog.qml index 33d8ce97d1..014fcc67c5 100644 --- a/src/quickcontrols/material/Dialog.qml +++ b/src/quickcontrols/material/Dialog.qml @@ -28,8 +28,7 @@ T.Dialog { // https://m3.material.io/components/dialogs/specs#401a48c3-f50c-4fa9-b798-701f5adcf155 // Specs say level 3 (6 dp) is the default, yet the screenshots there show 0. Native Android defaults to non-zero. Material.elevation: 6 - // https://m3.material.io/components/dialogs/specs#6771d107-624e-47cc-b6d8-2b7b620ba2f1 - Material.roundedScale: Material.ExtraLargeScale + Material.roundedScale: Material.dialogRoundedScale enter: Transition { // grow_fade_in @@ -63,7 +62,7 @@ T.Dialog { bottomPadding: 0 // TODO: QPlatformTheme::TitleBarFont // https://m3.material.io/components/dialogs/specs#401a48c3-f50c-4fa9-b798-701f5adcf155 - font.pixelSize: 24 + font.pixelSize: Material.dialogTitleFontPixelSize background: PaddedRectangle { radius: control.background.radius color: control.Material.dialogColor diff --git a/src/quickcontrols/material/qquickmaterialstyle.cpp b/src/quickcontrols/material/qquickmaterialstyle.cpp index 7338dde84e..f8ced5821b 100644 --- a/src/quickcontrols/material/qquickmaterialstyle.cpp +++ b/src/quickcontrols/material/qquickmaterialstyle.cpp @@ -1269,6 +1269,32 @@ int QQuickMaterialStyle::touchTarget() const return globalVariant == Dense ? 44 : 48; } +int QQuickMaterialStyle::buttonVerticalPadding() const +{ + return globalVariant == Dense ? 10 : 14; +} + +// https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382 +int QQuickMaterialStyle::buttonLeftPadding(bool flat, bool hasIcon) const +{ + static const int noIconPadding = globalVariant == Dense ? 12 : 24; + static const int iconPadding = globalVariant == Dense ? 8 : 16; + static const int flatPadding = globalVariant == Dense ? 6 : 12; + return !flat ? (!hasIcon ? noIconPadding : iconPadding) : flatPadding; +} + +int QQuickMaterialStyle::buttonRightPadding(bool flat, bool hasIcon, bool hasText) const +{ + static const int noTextPadding = globalVariant == Dense ? 8 : 16; + static const int textPadding = globalVariant == Dense ? 12 : 24; + static const int flatNoIconPadding = globalVariant == Dense ? 6 : 12; + static const int flatNoTextPadding = globalVariant == Dense ? 6 : 12; + static const int flatTextPadding = globalVariant == Dense ? 8 : 16; + return !flat + ? (!hasText ? noTextPadding : textPadding) + : (!hasIcon ? flatNoIconPadding : (!hasText ? flatNoTextPadding : flatTextPadding)); +} + int QQuickMaterialStyle::buttonHeight() const { // https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382 @@ -1286,6 +1312,19 @@ int QQuickMaterialStyle::dialogButtonBoxHeight() const return globalVariant == Dense ? 48 : 52; } +int QQuickMaterialStyle::dialogTitleFontPixelSize() const +{ + return globalVariant == Dense ? 16 : 24; +} + +// https://m3.material.io/components/dialogs/specs#6771d107-624e-47cc-b6d8-2b7b620ba2f1 +QQuickMaterialStyle::RoundedScale QQuickMaterialStyle::dialogRoundedScale() const +{ + return globalVariant == Dense + ? QQuickMaterialStyle::RoundedScale::LargeScale + : QQuickMaterialStyle::RoundedScale::ExtraLargeScale; +} + int QQuickMaterialStyle::frameVerticalPadding() const { return globalVariant == Dense ? 8 : 12; diff --git a/src/quickcontrols/material/qquickmaterialstyle_p.h b/src/quickcontrols/material/qquickmaterialstyle_p.h index 96d07179f7..5ff27f532e 100644 --- a/src/quickcontrols/material/qquickmaterialstyle_p.h +++ b/src/quickcontrols/material/qquickmaterialstyle_p.h @@ -76,9 +76,12 @@ class QQuickMaterialStyle : public QQuickAttachedPropertyPropagator Q_PROPERTY(QColor textFieldFilledContainerColor READ textFieldFilledContainerColor NOTIFY themeChanged FINAL) Q_PROPERTY(int touchTarget READ touchTarget CONSTANT FINAL) + Q_PROPERTY(int buttonVerticalPadding READ buttonVerticalPadding CONSTANT FINAL) Q_PROPERTY(int buttonHeight READ buttonHeight CONSTANT FINAL) Q_PROPERTY(int delegateHeight READ delegateHeight CONSTANT FINAL) Q_PROPERTY(int dialogButtonBoxHeight READ dialogButtonBoxHeight CONSTANT FINAL) + Q_PROPERTY(int dialogTitleFontPixelSize READ dialogTitleFontPixelSize CONSTANT FINAL) + Q_PROPERTY(RoundedScale dialogRoundedScale READ dialogRoundedScale CONSTANT FINAL) Q_PROPERTY(int frameVerticalPadding READ frameVerticalPadding CONSTANT FINAL) Q_PROPERTY(int menuItemHeight READ menuItemHeight CONSTANT FINAL) Q_PROPERTY(int menuItemVerticalPadding READ menuItemVerticalPadding CONSTANT FINAL) @@ -269,9 +272,14 @@ public: Q_INVOKABLE QColor shade(const QColor &color, Shade shade) const; int touchTarget() const; + int buttonVerticalPadding() const; + Q_INVOKABLE int buttonLeftPadding(bool flat, bool hasIcon) const; + Q_INVOKABLE int buttonRightPadding(bool flat, bool hasIcon, bool hasText) const; int buttonHeight() const; int delegateHeight() const; int dialogButtonBoxHeight() const; + int dialogTitleFontPixelSize() const; + RoundedScale dialogRoundedScale() const; int frameVerticalPadding() const; int menuItemHeight() const; int menuItemVerticalPadding() const; diff --git a/src/quickcontrols/material/shaders/RectangularGlow.frag b/src/quickcontrols/material/shaders/RectangularGlow.frag index 50188d4516..4687dfd2f7 100644 --- a/src/quickcontrols/material/shaders/RectangularGlow.frag +++ b/src/quickcontrols/material/shaders/RectangularGlow.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quickcontrolstestutils/controlstestutils.cpp b/src/quickcontrolstestutils/controlstestutils.cpp index b800817bc9..91ed710bf9 100644 --- a/src/quickcontrolstestutils/controlstestutils.cpp +++ b/src/quickcontrolstestutils/controlstestutils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "controlstestutils_p.h" diff --git a/src/quickcontrolstestutils/controlstestutils_p.h b/src/quickcontrolstestutils/controlstestutils_p.h index 7b58afb105..460d5bd5e5 100644 --- a/src/quickcontrolstestutils/controlstestutils_p.h +++ b/src/quickcontrolstestutils/controlstestutils_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef CONTROLSTESTUTILS_P_H #define CONTROLSTESTUTILS_P_H diff --git a/src/quickcontrolstestutils/dialogstestutils.cpp b/src/quickcontrolstestutils/dialogstestutils.cpp index 4a795e42da..0e95c90a73 100644 --- a/src/quickcontrolstestutils/dialogstestutils.cpp +++ b/src/quickcontrolstestutils/dialogstestutils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "dialogstestutils_p.h" diff --git a/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp index 075ccd54db..3e71a9ac4c 100644 --- a/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp +++ b/src/quickdialogs/quickdialogs/qquickmessagedialog.cpp @@ -219,6 +219,7 @@ void QQuickMessageDialog::handleClick(QPlatformDialogHelper::StandardButton butt QPlatformDialogHelper::ButtonRole role) { emit buttonClicked(button, role); + close(); } void QQuickMessageDialog::onCreate(QPlatformDialogHelper *dialog) diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml index a928a4b0cc..965f56bdc7 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml @@ -31,6 +31,29 @@ FileDialogImpl { standardButtons: T.Dialog.Open | T.Dialog.Cancel + Dialog { + id: overwriteConfirmationDialog + objectName: "confirmationDialog" + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + dim: true + modal: true + title: qsTr("“%1” already exists. Do you want to replace it?").arg(control.fileName) + + Label { + text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName) + } + + footer: DialogButtonBox { + alignment: Qt.AlignHCenter + standardButtons: DialogButtonBox.Yes | DialogButtonBox.No + } + + Overlay.modal: Rectangle { + color: Fusion.darkShade + } + } + /* We use attached properties because we want to handle logic in C++, and: - We can't assume the footer only contains a DialogButtonBox (which would allow us @@ -45,6 +68,7 @@ FileDialogImpl { FileDialogImpl.breadcrumbBar: breadcrumbBar FileDialogImpl.fileNameLabel: fileNameLabel FileDialogImpl.fileNameTextField: fileNameTextField + FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog background: Rectangle { implicitWidth: 600 @@ -124,7 +148,7 @@ FileDialogImpl { fileDetailRowWidth: nameFiltersComboBox.width KeyNavigation.backtab: breadcrumbBar - KeyNavigation.tab: nameFiltersComboBox + KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox } } } @@ -143,7 +167,6 @@ FileDialogImpl { TextField { id: fileNameTextField objectName: "fileNameTextField" - text: control.fileName visible: false Layout.fillWidth: true diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml index 475528d2ec..46cdb06f0d 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialogDelegate.qml @@ -34,7 +34,7 @@ DialogsQuickImpl.FileDialogDelegate { required property int index required property string fileName required property url fileUrl - required property int fileSize + required property string fileSize required property date fileModified required property bool fileIsDir diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml index 664965e571..8568be710a 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml @@ -39,12 +39,34 @@ FileDialogImpl { standardButtons: T.Dialog.Open | T.Dialog.Cancel + Dialog { + id: overwriteConfirmationDialog + objectName: "confirmationDialog" + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + dim: true + modal: true + spacing: 12 + title: qsTr("“%1” already exists. Do you want to replace it?").arg(control.fileName) + + Label { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName) + horizontalAlignment: Text.AlignHCenter + } + + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Yes | DialogButtonBox.No + } + } + FileDialogImpl.buttonBox: buttonBox FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox FileDialogImpl.fileDialogListView: fileDialogListView FileDialogImpl.breadcrumbBar: breadcrumbBar FileDialogImpl.fileNameLabel: fileNameLabel FileDialogImpl.fileNameTextField: fileNameTextField + FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog background: NinePatchImage { source: Imagine.url + "dialog-background" @@ -115,6 +137,9 @@ FileDialogImpl { highlighted: ListView.isCurrentItem dialog: control fileDetailRowWidth: nameFiltersComboBox.width + + KeyNavigation.backtab: breadcrumbBar + KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox } } @@ -133,7 +158,6 @@ FileDialogImpl { TextField { id: fileNameTextField objectName: "fileNameTextField" - text: control.fileName visible: false Layout.fillWidth: true diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml index 30094a7d9d..88c6f99d1b 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialogDelegate.qml @@ -39,7 +39,7 @@ DialogsQuickImpl.FileDialogDelegate { required property int index required property string fileName required property url fileUrl - required property int fileSize + required property string fileSize required property date fileModified required property bool fileIsDir diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml index cd2c513c2e..9700aeba79 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml @@ -32,12 +32,32 @@ FileDialogImpl { Material.elevation: 24 + Dialog { + id: overwriteConfirmationDialog + objectName: "confirmationDialog" + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + dim: true + modal: true + title: qsTr("“%1” already exists. Do you want to replace it?").arg(control.fileName) + + Label { + text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName) + } + + footer: DialogButtonBox { + alignment: Qt.AlignHCenter + standardButtons: DialogButtonBox.Yes | DialogButtonBox.No + } + } + FileDialogImpl.buttonBox: buttonBox FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox FileDialogImpl.fileDialogListView: fileDialogListView FileDialogImpl.breadcrumbBar: breadcrumbBar FileDialogImpl.fileNameLabel: fileNameLabel FileDialogImpl.fileNameTextField: fileNameTextField + FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog background: Rectangle { implicitWidth: 600 @@ -97,6 +117,9 @@ FileDialogImpl { highlighted: ListView.isCurrentItem dialog: control fileDetailRowWidth: nameFiltersComboBox.width + + KeyNavigation.backtab: breadcrumbBar + KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox } } @@ -116,7 +139,6 @@ FileDialogImpl { TextField { id: fileNameTextField objectName: "fileNameTextField" - text: control.fileName visible: false Layout.topMargin: 12 diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml index c96231345d..ff06ea8d43 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialogDelegate.qml @@ -31,7 +31,7 @@ DialogsQuickImpl.FileDialogDelegate { required property int index required property string fileName required property url fileUrl - required property int fileSize + required property string fileSize required property date fileModified required property bool fileIsDir diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml index c029b06293..0d2db8b426 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml @@ -30,12 +30,35 @@ FileDialogImpl { standardButtons: T.Dialog.Open | T.Dialog.Cancel + Dialog { + id: overwriteConfirmationDialog + objectName: "confirmationDialog" + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + dim: true + modal: true + title: qsTr("“%1” already exists. Do you want to replace it?").arg(control.fileName) + + Label { + text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName) + } + + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Yes | DialogButtonBox.No + } + + Overlay.modal: Rectangle { + color: overwriteConfirmationDialog.Universal.baseMediumColor + } + } + FileDialogImpl.buttonBox: buttonBox FileDialogImpl.nameFiltersComboBox: nameFiltersComboBox FileDialogImpl.fileDialogListView: fileDialogListView FileDialogImpl.breadcrumbBar: breadcrumbBar FileDialogImpl.fileNameLabel: fileNameLabel FileDialogImpl.fileNameTextField: fileNameTextField + FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog background: Rectangle { implicitWidth: 600 @@ -99,6 +122,9 @@ FileDialogImpl { highlighted: ListView.isCurrentItem dialog: control fileDetailRowWidth: nameFiltersComboBox.width + + KeyNavigation.backtab: breadcrumbBar + KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox } } @@ -117,7 +143,6 @@ FileDialogImpl { TextField { id: fileNameTextField objectName: "fileNameTextField" - text: control.fileName visible: false Layout.fillWidth: true diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml index 7650ec7a34..92c16f406f 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialogDelegate.qml @@ -32,7 +32,7 @@ DialogsQuickImpl.FileDialogDelegate { required property int index required property string fileName required property url fileUrl - required property int fileSize + required property string fileSize required property date fileModified required property bool fileIsDir diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml index 0f25dee35b..defb7dd4e2 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml @@ -34,6 +34,25 @@ FileDialogImpl { standardButtons: T.Dialog.Open | T.Dialog.Cancel + Dialog { + id: overwriteConfirmationDialog + objectName: "confirmationDialog" + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + dim: true + modal: true + title: qsTr("“%1” already exists. Do you want to replace it?").arg(control.fileName) + + Label { + text: qsTr("A file with the same name already exists in %1.\nReplacing it will overwrite its current contents.").arg(control.currentFolderName) + } + + footer: DialogButtonBox { + alignment: Qt.AlignHCenter + standardButtons: DialogButtonBox.Yes | DialogButtonBox.No + } + } + /* We use attached properties because we want to handle logic in C++, and: - We can't assume the footer only contains a DialogButtonBox (which would allow us @@ -48,6 +67,7 @@ FileDialogImpl { FileDialogImpl.breadcrumbBar: breadcrumbBar FileDialogImpl.fileNameLabel: fileNameLabel FileDialogImpl.fileNameTextField: fileNameTextField + FileDialogImpl.overwriteConfirmationDialog: overwriteConfirmationDialog background: Rectangle { implicitWidth: 600 @@ -106,7 +126,7 @@ FileDialogImpl { fileDetailRowWidth: nameFiltersComboBox.width KeyNavigation.backtab: breadcrumbBar - KeyNavigation.tab: nameFiltersComboBox + KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox } } @@ -135,7 +155,6 @@ FileDialogImpl { TextField { id: fileNameTextField objectName: "fileNameTextField" - text: control.fileName visible: false Layout.fillWidth: true diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml index 3abe35f43e..6f62375a88 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml +++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialogDelegate.qml @@ -34,7 +34,7 @@ DialogsQuickImpl.FileDialogDelegate { required property int index required property string fileName required property url fileUrl - required property int fileSize + required property string fileSize required property date fileModified required property bool fileIsDir diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp index 71b1fccbc5..369176f844 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp +++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp @@ -11,6 +11,7 @@ #include <QtQuickTemplates2/private/qquickitemdelegate_p_p.h> #include "qquickfiledialogimpl_p.h" +#include "qquickfiledialogimpl_p_p.h" #include "qquickfolderdialogimpl_p.h" QT_BEGIN_NAMESPACE @@ -67,6 +68,16 @@ void QQuickFileDialogDelegatePrivate::chooseFile() Q_ASSERT(fileDialog); // Otherwise it's a file, so select it and close the dialog. fileDialog->setSelectedFile(file); + + // Prioritize closing the dialog with QQuickDialogPrivate::handleClick() over QQuickDialog::accept() + const QQuickFileDialogImplAttached *attached = QQuickFileDialogImplPrivate::get(fileDialog)->attachedOrWarn(); + if (Q_LIKELY(attached)) { + auto *openButton = attached->buttonBox()->standardButton(QPlatformDialogHelper::Open); + if (Q_LIKELY(openButton)) { + emit openButton->clicked(); + return; + } + } fileDialog->accept(); } } diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp index 72f2ea25cf..99606878b7 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp +++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp @@ -137,12 +137,24 @@ void QQuickFileDialogImplPrivate::updateSelectedFile(const QString &oldFolderPat qCDebug(lcUpdateSelectedFile).nospace() << "updateSelectedFile is setting selectedFile to " << newSelectedFileUrl << ", newSelectedFileIndex is " << newSelectedFileIndex; q->setSelectedFile(newSelectedFileUrl); + updateFileNameTextEdit(); // If the index is -1, there are no files in the directory, and so fileDialogListView's // currentIndex will already be -1. if (newSelectedFileIndex != -1) tryUpdateFileDialogListViewCurrentIndex(newSelectedFileIndex); } +void QQuickFileDialogImplPrivate::updateFileNameTextEdit() +{ + QQuickFileDialogImplAttached *attached = attachedOrWarn(); + if (Q_UNLIKELY(!attached)) + return; + + const QFileInfo fileInfo(selectedFile.toLocalFile()); + if (fileInfo.isFile()) + attached->fileNameTextField()->setText(fileInfo.fileName()); +} + QDir::SortFlags QQuickFileDialogImplPrivate::fileListSortFlags() { QDir::SortFlags sortFlags = QDir::IgnoreCase; @@ -238,14 +250,35 @@ void QQuickFileDialogImplPrivate::handleClick(QQuickAbstractButton *button) // Don't call accept(), because selecting a folder != accepting the dialog. } else { // Otherwise it's a file, so select it and close the dialog. - q->setSelectedFile(selectedFile); - q->accept(); - QQuickDialogPrivate::handleClick(button); - emit q->fileSelected(selectedFile); + + lastButtonClicked = button; + + // Unless it already exists... + const bool dontConfirmOverride = q->options()->testOption(QFileDialogOptions::DontConfirmOverwrite); + const bool isSaveMode = q->options()->fileMode() == QFileDialogOptions::AnyFile; + if (QQuickFileDialogImplAttached *attached = attachedOrWarn(); + attached && fileInfo.exists() && isSaveMode && !dontConfirmOverride) { + QQuickDialog *confirmationDialog = attached->overwriteConfirmationDialog(); + confirmationDialog->open(); + static_cast<QQuickDialogButtonBox *>(confirmationDialog->footer())->standardButton(QPlatformDialogHelper::Yes) + ->forceActiveFocus(Qt::PopupFocusReason); + } else { + selectFile(); + } } } } +void QQuickFileDialogImplPrivate::selectFile() +{ + Q_Q(QQuickFileDialogImpl); + Q_ASSERT(lastButtonClicked); + q->setSelectedFile(selectedFile); + q->accept(); + QQuickDialogPrivate::handleClick(lastButtonClicked); + emit q->fileSelected(selectedFile); +} + QQuickFileDialogImpl::QQuickFileDialogImpl(QObject *parent) : QQuickDialog(*(new QQuickFileDialogImplPrivate), parent) { @@ -332,6 +365,7 @@ void QQuickFileDialogImpl::setInitialCurrentFolderAndSelectedFile(const QUrl &fi qCDebug(lcSelectedFile) << "setting initial currentFolder to" << fileDirUrl << "and selectedFile to" << file; setCurrentFolder(fileDirUrl, QQuickFileDialogImpl::SetReason::Internal); setSelectedFile(file); + d->updateFileNameTextEdit(); d->setCurrentIndexToInitiallySelectedFile = true; // If the currentFolder didn't change, the FolderListModel won't change and @@ -426,8 +460,11 @@ void QQuickFileDialogImpl::setAcceptLabel(const QString &label) return; } + auto buttonType = (d->options && d->options->acceptMode() == QFileDialogOptions::AcceptSave) + ? QPlatformDialogHelper::Save + : QPlatformDialogHelper::Open; acceptButton->setText(!label.isEmpty() - ? label : QQuickDialogButtonBoxPrivate::buttonText(QPlatformDialogHelper::Open)); + ? label : QQuickDialogButtonBoxPrivate::buttonText(buttonType)); } void QQuickFileDialogImpl::setRejectLabel(const QString &label) @@ -467,7 +504,15 @@ void QQuickFileDialogImpl::setFileName(const QString &fileName) if (previous == fileName) return; - setSelectedFile(QUrl(currentFolder().path() + u'/' + fileName)); + QUrl newSelectedFile; + newSelectedFile.setScheme(currentFolder().scheme()); + newSelectedFile.setPath(currentFolder().path() + u'/' + fileName); + setSelectedFile(newSelectedFile); +} + +QString QQuickFileDialogImpl::currentFolderName() const +{ + return QDir(currentFolder().toLocalFile()).dirName(); } void QQuickFileDialogImpl::componentComplete() @@ -561,6 +606,7 @@ void QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged( auto fileDialogImplPrivate = QQuickFileDialogImplPrivate::get(fileDialogImpl); if (moveReason != QQuickItemViewPrivate::Other) { fileDialogImpl->setSelectedFile(fileDialogDelegate->file()); + fileDialogImplPrivate->updateFileNameTextEdit(); } else if (fileDialogImplPrivate->setCurrentIndexToInitiallySelectedFile) { // When setting selectedFile before opening the FileDialog, // we need to ensure that the currentIndex is correct, because the initial change @@ -753,6 +799,32 @@ void QQuickFileDialogImplAttached::setFileNameTextField(QQuickTextField *fileNam emit fileNameTextFieldChanged(); } +QQuickDialog *QQuickFileDialogImplAttached::overwriteConfirmationDialog() const +{ + Q_D(const QQuickFileDialogImplAttached); + return d->overwriteConfirmationDialog; +} + +void QQuickFileDialogImplAttached::setOverwriteConfirmationDialog(QQuickDialog *dialog) +{ + Q_D(QQuickFileDialogImplAttached); + if (dialog == d->overwriteConfirmationDialog) + return; + + QQuickFileDialogImpl *fileDialogImpl = qobject_cast<QQuickFileDialogImpl*>(parent()); + if (d->overwriteConfirmationDialog && fileDialogImpl) + QObjectPrivate::disconnect(d->overwriteConfirmationDialog, &QQuickDialog::accepted, + QQuickFileDialogImplPrivate::get(fileDialogImpl), &QQuickFileDialogImplPrivate::selectFile); + + d->overwriteConfirmationDialog = dialog; + + if (d->overwriteConfirmationDialog && fileDialogImpl) + QObjectPrivate::connect(d->overwriteConfirmationDialog, &QQuickDialog::accepted, + QQuickFileDialogImplPrivate::get(fileDialogImpl), &QQuickFileDialogImplPrivate::selectFile, Qt::QueuedConnection); + + emit overwriteConfirmationDialogChanged(); +} + QT_END_NAMESPACE #include "moc_qquickfiledialogimpl_p.cpp" diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h index 4c64c5e4dd..1861d7d7e3 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h +++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p.h @@ -41,6 +41,7 @@ class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImpl : public QQui Q_PROPERTY(QStringList nameFilters READ nameFilters NOTIFY nameFiltersChanged FINAL) Q_PROPERTY(QQuickFileNameFilter *selectedNameFilter READ selectedNameFilter CONSTANT) Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY selectedFileChanged FINAL) + Q_PROPERTY(QString currentFolderName READ currentFolderName NOTIFY selectedFileChanged FINAL) QML_NAMED_ELEMENT(FileDialogImpl) QML_ATTACHED(QQuickFileDialogImplAttached) QML_ADDED_IN_VERSION(6, 2) @@ -80,6 +81,8 @@ public: QString fileName() const; void setFileName(const QString &fileName); + QString currentFolderName() const; + public Q_SLOTS: void selectNameFilter(const QString &filter); @@ -107,6 +110,7 @@ class Q_QUICKDIALOGS2QUICKIMPL_PRIVATE_EXPORT QQuickFileDialogImplAttached : pub Q_PROPERTY(QQuickFolderBreadcrumbBar *breadcrumbBar READ breadcrumbBar WRITE setBreadcrumbBar NOTIFY breadcrumbBarChanged) Q_PROPERTY(QQuickLabel *fileNameLabel READ fileNameLabel WRITE setFileNameLabel NOTIFY fileNameLabelChanged FINAL) Q_PROPERTY(QQuickTextField *fileNameTextField READ fileNameTextField WRITE setFileNameTextField NOTIFY fileNameTextFieldChanged FINAL) + Q_PROPERTY(QQuickDialog *overwriteConfirmationDialog READ overwriteConfirmationDialog WRITE setOverwriteConfirmationDialog NOTIFY overwriteConfirmationDialogChanged FINAL) Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickdialogbuttonbox_p.h>) Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquickcombobox_p.h>) Q_MOC_INCLUDE(<QtQuickTemplates2/private/qquicktextfield_p.h>) @@ -136,6 +140,9 @@ public: QQuickTextField *fileNameTextField() const; void setFileNameTextField(QQuickTextField *fileNameTextField); + QQuickDialog *overwriteConfirmationDialog() const; + void setOverwriteConfirmationDialog(QQuickDialog *dialog); + Q_SIGNALS: void buttonBoxChanged(); void nameFiltersComboBoxChanged(); @@ -143,6 +150,7 @@ Q_SIGNALS: void breadcrumbBarChanged(); void fileNameLabelChanged(); void fileNameTextFieldChanged(); + void overwriteConfirmationDialogChanged(); private: Q_DISABLE_COPY(QQuickFileDialogImplAttached) diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h index 6f3cc55e86..6de7b27773 100644 --- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h +++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl_p_p.h @@ -45,6 +45,7 @@ public: void updateEnabled(); void updateSelectedFile(const QString &oldFolderPath); + void updateFileNameTextEdit(); static QDir::SortFlags fileListSortFlags(); static QFileInfoList fileList(const QDir &dir); void setFileDialogListViewCurrentIndex(int newCurrentIndex); @@ -53,10 +54,12 @@ public: void handleAccept() override; void handleClick(QQuickAbstractButton *button) override; + void selectFile(); QSharedPointer<QFileDialogOptions> options; QUrl currentFolder; QUrl selectedFile; + QQuickAbstractButton *lastButtonClicked = nullptr; QStringList nameFilters; mutable QQuickFileNameFilter *selectedNameFilter = nullptr; QString acceptLabel; @@ -81,6 +84,7 @@ public: QPointer<QQuickFolderBreadcrumbBar> breadcrumbBar; QPointer<QQuickLabel> fileNameLabel; QPointer<QQuickTextField> fileNameTextField; + QPointer<QQuickDialog> overwriteConfirmationDialog; }; QT_END_NAMESPACE diff --git a/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag b/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag index ad2b23faa1..a72d306d1e 100644 --- a/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag +++ b/src/quickdialogs/quickdialogsquickimpl/shaders/SaturationLightness.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only diff --git a/src/quicknativestyle/controls/DefaultTextArea.qml b/src/quicknativestyle/controls/DefaultTextArea.qml index 4ed9427bab..faab250fae 100644 --- a/src/quicknativestyle/controls/DefaultTextArea.qml +++ b/src/quicknativestyle/controls/DefaultTextArea.qml @@ -34,8 +34,8 @@ T.TextArea { id: placeholder x: control.leftPadding y: control.topPadding - width: control.availableWidth - height: control.availableHeight + width: control.width - (control.leftPadding + control.rightPadding) + height: control.height - (control.topPadding + control.bottomPadding) text: control.placeholderText font: control.font color: control.placeholderTextColor diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp index 0aa9696068..7360ffb3c6 100644 --- a/src/quickshapes/qquickshape.cpp +++ b/src/quickshapes/qquickshape.cpp @@ -662,6 +662,7 @@ QQuickShape::~QQuickShape() /*! \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType + \readonly This property determines which path rendering backend is active. @@ -746,6 +747,7 @@ void QQuickShape::setVendorExtensionsEnabled(bool enable) /*! \qmlproperty enumeration QtQuick.Shapes::Shape::status + \readonly This property determines the status of the Shape and is relevant when Shape.asynchronous is set to \c true. diff --git a/src/quickshapes/shaders_ng/conicalgradient.frag b/src/quickshapes/shaders_ng/conicalgradient.frag index 99592e14e4..ceb7c8e3da 100644 --- a/src/quickshapes/shaders_ng/conicalgradient.frag +++ b/src/quickshapes/shaders_ng/conicalgradient.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quickshapes/shaders_ng/lineargradient.frag b/src/quickshapes/shaders_ng/lineargradient.frag index 26fa540154..650e6d75c0 100644 --- a/src/quickshapes/shaders_ng/lineargradient.frag +++ b/src/quickshapes/shaders_ng/lineargradient.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quickshapes/shaders_ng/radialgradient.frag b/src/quickshapes/shaders_ng/radialgradient.frag index 743cc2a8cb..ce505fb4cc 100644 --- a/src/quickshapes/shaders_ng/radialgradient.frag +++ b/src/quickshapes/shaders_ng/radialgradient.frag @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #version 440 diff --git a/src/quicktemplates/qquickabstractbutton.cpp b/src/quicktemplates/qquickabstractbutton.cpp index 86a28e9666..e41c065b33 100644 --- a/src/quicktemplates/qquickabstractbutton.cpp +++ b/src/quicktemplates/qquickabstractbutton.cpp @@ -822,7 +822,7 @@ void QQuickAbstractButton::setIcon(const QQuickIcon &icon) \header \li Display \li Result \row \li \c AbstractButton.IconOnly \li \image qtquickcontrols-button-icononly.png \row \li \c AbstractButton.TextOnly \li \image qtquickcontrols-button-textonly.png - \row \li \c AbstractButton.TextBesideIcon \li \image qtquickcontrols-button-textbesideicon.png + \row \li \c AbstractButton.TextBesideIcon (default) \li \image qtquickcontrols-button-textbesideicon.png \row \li \c AbstractButton.TextUnderIcon \li \image qtquickcontrols-button-textundericon.png \endtable diff --git a/src/quicktemplates/qquickdialog.cpp b/src/quicktemplates/qquickdialog.cpp index 300ad4301f..30daee5f33 100644 --- a/src/quicktemplates/qquickdialog.cpp +++ b/src/quicktemplates/qquickdialog.cpp @@ -25,6 +25,10 @@ QT_BEGIN_NAMESPACE \image qtquickcontrols-page-wireframe.png + The \l {Popup::}{padding} properties only affect the contentItem. Use the + \l {Popup::}{spacing} property to affect the space between header, + contentItem and footer. + By default, Dialogs have \l {QQuickItem::}{focus}. \section1 Dialog Title and Buttons diff --git a/src/quicktemplates/qquickdrawer.cpp b/src/quicktemplates/qquickdrawer.cpp index 7fd90bd6be..517db920b8 100644 --- a/src/quicktemplates/qquickdrawer.cpp +++ b/src/quicktemplates/qquickdrawer.cpp @@ -217,17 +217,17 @@ void QQuickDrawerPositioner::reposition() QQuickPopupPositioner::reposition(); } -void QQuickDrawerPrivate::showOverlay() +void QQuickDrawerPrivate::showDimmer() { // managed in setPosition() } -void QQuickDrawerPrivate::hideOverlay() +void QQuickDrawerPrivate::hideDimmer() { // managed in setPosition() } -void QQuickDrawerPrivate::resizeOverlay() +void QQuickDrawerPrivate::resizeDimmer() { if (!dimmer || !window) return; @@ -802,7 +802,7 @@ void QQuickDrawer::geometryChange(const QRectF &newGeometry, const QRectF &oldGe { Q_D(QQuickDrawer); QQuickPopup::geometryChange(newGeometry, oldGeometry); - d->resizeOverlay(); + d->resizeDimmer(); } QT_END_NAMESPACE diff --git a/src/quicktemplates/qquickdrawer_p_p.h b/src/quicktemplates/qquickdrawer_p_p.h index 224e557839..142c3fda89 100644 --- a/src/quicktemplates/qquickdrawer_p_p.h +++ b/src/quicktemplates/qquickdrawer_p_p.h @@ -35,9 +35,9 @@ public: qreal positionAt(const QPointF &point) const; QQuickPopupPositioner *getPositioner() override; - void showOverlay() override; - void hideOverlay() override; - void resizeOverlay() override; + void showDimmer() override; + void hideDimmer() override; + void resizeDimmer() override; bool startDrag(QEvent *event); bool grabMouse(QQuickItem *item, QMouseEvent *event); diff --git a/src/quicktemplates/qquickoverlay.cpp b/src/quicktemplates/qquickoverlay.cpp index 3d9ac5481a..4119a34b84 100644 --- a/src/quicktemplates/qquickoverlay.cpp +++ b/src/quicktemplates/qquickoverlay.cpp @@ -392,7 +392,7 @@ void QQuickOverlay::geometryChange(const QRectF &newGeometry, const QRectF &oldG Q_D(QQuickOverlay); QQuickItem::geometryChange(newGeometry, oldGeometry); for (QQuickPopup *popup : std::as_const(d->allPopups)) - QQuickPopupPrivate::get(popup)->resizeOverlay(); + QQuickPopupPrivate::get(popup)->resizeDimmer(); } void QQuickOverlay::mousePressEvent(QMouseEvent *event) diff --git a/src/quicktemplates/qquickpane.cpp b/src/quicktemplates/qquickpane.cpp index 2def00b77e..3c941548aa 100644 --- a/src/quicktemplates/qquickpane.cpp +++ b/src/quicktemplates/qquickpane.cpp @@ -156,12 +156,7 @@ void QQuickPanePrivate::contentChildrenChange() { Q_Q(QQuickPane); - // The first child varies depending on how the content item is declared. - // If it's declared as a child of the Pane, it will be parented to the - // default QQuickContentItem. If it's assigned to the contentItem property - // directly, QQuickControl::contentItem will be used." - QQuickItem *newFirstChild = ((qobject_cast<QQuickContentItem *>(contentItem)) - ? contentChildItems().value(0) : *contentItem); + QQuickItem *newFirstChild = getFirstChild(); if (newFirstChild != firstChild) { if (firstChild) @@ -191,6 +186,16 @@ qreal QQuickPanePrivate::getContentWidth() const return 0; } +QQuickItem* QQuickPanePrivate::getFirstChild() const +{ + // The first child varies depending on how the content item is declared. + // If it's declared as a child of the Pane, it will be parented to the + // default QQuickContentItem. If it's assigned to the contentItem property + // directly, QQuickControl::contentItem will be used. + return (qobject_cast<QQuickContentItem *>(contentItem) + ? contentChildItems().value(0) : contentItem.data()); +} + qreal QQuickPanePrivate::getContentHeight() const { if (!contentItem) diff --git a/src/quicktemplates/qquickpane_p_p.h b/src/quicktemplates/qquickpane_p_p.h index fc77d52d00..e9964c70a0 100644 --- a/src/quicktemplates/qquickpane_p_p.h +++ b/src/quicktemplates/qquickpane_p_p.h @@ -31,6 +31,7 @@ public: virtual QQmlListProperty<QObject> contentData(); virtual QQmlListProperty<QQuickItem> contentChildren(); virtual QList<QQuickItem *> contentChildItems() const; + virtual QQuickItem *getFirstChild() const; QQuickItem *getContentItem() override; diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp index 416ae5bb95..d81da8ece8 100644 --- a/src/quicktemplates/qquickpopup.cpp +++ b/src/quicktemplates/qquickpopup.cpp @@ -556,7 +556,7 @@ bool QQuickPopupPrivate::prepareEnterTransition() } if (dim) createOverlay(); - showOverlay(); + showDimmer(); emit q->aboutToShow(); visible = true; transitionState = EnterTransition; @@ -593,7 +593,7 @@ bool QQuickPopupPrivate::prepareExitTransition() if (focus) popupItem->setFocus(false, Qt::PopupFocusReason); transitionState = ExitTransition; - hideOverlay(); + hideDimmer(); emit q->aboutToHide(); emit q->openedChanged(); } @@ -881,7 +881,7 @@ void QQuickPopupPrivate::createOverlay() if (!dimmer) dimmer = createDimmer(component, q, overlay); - resizeOverlay(); + resizeDimmer(); } void QQuickPopupPrivate::destroyDimmer() @@ -914,21 +914,21 @@ void QQuickPopupPrivate::updateContentPalettes(const QPalette& parentPalette) QQuickItemPrivate::get(popupItem)->updateChildrenPalettes(parentPalette); } -void QQuickPopupPrivate::showOverlay() +void QQuickPopupPrivate::showDimmer() { // use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors if (dim && dimmer) QQmlProperty::write(dimmer, QStringLiteral("opacity"), 1.0); } -void QQuickPopupPrivate::hideOverlay() +void QQuickPopupPrivate::hideDimmer() { // use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors if (dim && dimmer) QQmlProperty::write(dimmer, QStringLiteral("opacity"), 0.0); } -void QQuickPopupPrivate::resizeOverlay() +void QQuickPopupPrivate::resizeDimmer() { if (!dimmer) return; diff --git a/src/quicktemplates/qquickpopup_p_p.h b/src/quicktemplates/qquickpopup_p_p.h index eb48ce913e..9da91a4e26 100644 --- a/src/quicktemplates/qquickpopup_p_p.h +++ b/src/quicktemplates/qquickpopup_p_p.h @@ -96,9 +96,9 @@ public: void destroyDimmer(); void toggleOverlay(); void updateContentPalettes(const QPalette& parentPalette); - virtual void showOverlay(); - virtual void hideOverlay(); - virtual void resizeOverlay(); + virtual void showDimmer(); + virtual void hideDimmer(); + virtual void resizeDimmer(); virtual bool prepareEnterTransition(); virtual bool prepareExitTransition(); diff --git a/src/quicktemplates/qquickscrollview.cpp b/src/quicktemplates/qquickscrollview.cpp index d68728c359..3afdc08030 100644 --- a/src/quicktemplates/qquickscrollview.cpp +++ b/src/quicktemplates/qquickscrollview.cpp @@ -98,6 +98,7 @@ public: QQmlListProperty<QObject> contentData() override; QQmlListProperty<QQuickItem> contentChildren() override; QList<QQuickItem *> contentChildItems() const override; + QQuickItem* getFirstChild() const override; QQuickItem *getContentItem() override; @@ -150,6 +151,11 @@ QQuickItem *QQuickScrollViewPrivate::getContentItem() return ensureFlickable(false); } +QQuickItem* QQuickScrollViewPrivate::getFirstChild() const +{ + return contentChildItems().value(0); +} + QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(bool content) { Q_Q(QQuickScrollView); diff --git a/src/quicktestutils/qml/platforminputcontext_p.h b/src/quicktestutils/qml/platforminputcontext_p.h index 8067ad876e..5f40cb6a7c 100644 --- a/src/quicktestutils/qml/platforminputcontext_p.h +++ b/src/quicktestutils/qml/platforminputcontext_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef PLATFORMINPUTCONTEXT_P_H #define PLATFORMINPUTCONTEXT_P_H diff --git a/src/quicktestutils/qml/platformquirks_p.h b/src/quicktestutils/qml/platformquirks_p.h index 6bbdf81bee..e22e555082 100644 --- a/src/quicktestutils/qml/platformquirks_p.h +++ b/src/quicktestutils/qml/platformquirks_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef PLATFORMQUIRKS_P_H #define PLATFORMQUIRKS_P_H diff --git a/src/quicktestutils/qml/qmlutils.cpp b/src/quicktestutils/qml/qmlutils.cpp index 05d94b68ae..02ca8e5b03 100644 --- a/src/quicktestutils/qml/qmlutils.cpp +++ b/src/quicktestutils/qml/qmlutils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "qmlutils_p.h" diff --git a/src/quicktestutils/qml/qmlutils_p.h b/src/quicktestutils/qml/qmlutils_p.h index 47825a3793..d197dc7cac 100644 --- a/src/quicktestutils/qml/qmlutils_p.h +++ b/src/quicktestutils/qml/qmlutils_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQMLTESTUTILS_P_H #define QQMLTESTUTILS_P_H diff --git a/src/quicktestutils/qml/testhttpserver.cpp b/src/quicktestutils/qml/testhttpserver.cpp index 9873cd1827..d48bd10de6 100644 --- a/src/quicktestutils/qml/testhttpserver.cpp +++ b/src/quicktestutils/qml/testhttpserver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "testhttpserver_p.h" #include <QTcpSocket> diff --git a/src/quicktestutils/qml/testhttpserver_p.h b/src/quicktestutils/qml/testhttpserver_p.h index 0620aa4e72..211da4f08a 100644 --- a/src/quicktestutils/qml/testhttpserver_p.h +++ b/src/quicktestutils/qml/testhttpserver_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef TESTHTTPSERVER_P_H #define TESTHTTPSERVER_P_H diff --git a/src/quicktestutils/quick/geometrytestutils.cpp b/src/quicktestutils/quick/geometrytestutils.cpp index 2fe1138fbd..42fc66608a 100644 --- a/src/quicktestutils/quick/geometrytestutils.cpp +++ b/src/quicktestutils/quick/geometrytestutils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "geometrytestutils_p.h" diff --git a/src/quicktestutils/quick/geometrytestutils_p.h b/src/quicktestutils/quick/geometrytestutils_p.h index 605dfd9027..46cdc78cc0 100644 --- a/src/quicktestutils/quick/geometrytestutils_p.h +++ b/src/quicktestutils/quick/geometrytestutils_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQUICKGEOMETRYTESTUTIL_P_H #define QQUICKGEOMETRYTESTUTIL_P_H diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp index 79c34168e5..db30cedd3e 100644 --- a/src/quicktestutils/quick/viewtestutils.cpp +++ b/src/quicktestutils/quick/viewtestutils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "viewtestutils_p.h" diff --git a/src/quicktestutils/quick/viewtestutils_p.h b/src/quicktestutils/quick/viewtestutils_p.h index 28666786f0..4ee9744545 100644 --- a/src/quicktestutils/quick/viewtestutils_p.h +++ b/src/quicktestutils/quick/viewtestutils_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQUICKVIEWTESTUTILS_P_H #define QQUICKVIEWTESTUTILS_P_H diff --git a/src/quicktestutils/quick/visualtestutils.cpp b/src/quicktestutils/quick/visualtestutils.cpp index 3435be5353..07090f1165 100644 --- a/src/quicktestutils/quick/visualtestutils.cpp +++ b/src/quicktestutils/quick/visualtestutils.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #include "visualtestutils_p.h" diff --git a/src/quicktestutils/quick/visualtestutils_p.h b/src/quicktestutils/quick/visualtestutils_p.h index 4ef0464602..bf29ce73ec 100644 --- a/src/quicktestutils/quick/visualtestutils_p.h +++ b/src/quicktestutils/quick/visualtestutils_p.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial #ifndef QQUICKVISUALTESTUTILS_P_H #define QQUICKVISUALTESTUTILS_P_H diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index ade87f9e02..3ba29d6305 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -335,8 +335,10 @@ void QQuickWidgetPrivate::render(bool needsSync) q->createFramebufferObject(); } - if (!rhi) + if (!rhi) { + qWarning("QQuickWidget: Attempted to render scene with no rhi"); return; + } // createFramebufferObject() bails out when the size is empty. In this case // we cannot render either. @@ -396,11 +398,6 @@ void QQuickWidgetPrivate::renderSceneGraph() if (!q->isVisible() || fakeHidden) return; - if (!useSoftwareRenderer && !rhi) { - qWarning("QQuickWidget: Attempted to render scene with no rhi"); - return; - } - render(true); #if QT_CONFIG(graphicsview) @@ -658,6 +655,9 @@ QQuickWidget::~QQuickWidget() delete d->root; d->root = nullptr; + if (d->rhi) + d->rhi->removeCleanupCallback(this); + // NB! resetting graphics resources must be done from this destructor, // *not* from the private class' destructor. This is due to how destruction // works and due to the QWidget dtor (for toplevels) destroying the repaint @@ -1022,8 +1022,19 @@ void QQuickWidgetPrivate::initializeWithRhi() if (rhi) return; - if (QWidgetRepaintManager *repaintManager = tlwd->maybeRepaintManager()) + if (QWidgetRepaintManager *repaintManager = tlwd->maybeRepaintManager()) { rhi = repaintManager->rhi(); + if (rhi) { + // We don't own the RHI, so make sure we clean up if it goes away + rhi->addCleanupCallback(q, [this](QRhi *rhi) { + if (this->rhi == rhi) { + invalidateRenderControl(); + deviceLost = true; + this->rhi = nullptr; + } + }); + } + } if (!rhi) { // The widget (and its parent chain, if any) may not be shown at @@ -1652,7 +1663,7 @@ bool QQuickWidget::event(QEvent *e) QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e); auto deliveredPoints = pointerEvent->points(); for (auto &point : deliveredPoints) { - if (pointerEvent->exclusiveGrabber(point)) + if (pointerEvent->exclusiveGrabber(point) || !pointerEvent->passiveGrabbers(point).isEmpty()) point.setAccepted(true); } } @@ -1676,7 +1687,10 @@ bool QQuickWidget::event(QEvent *e) } case QEvent::WindowAboutToChangeInternal: + if (d->rhi) + d->rhi->removeCleanupCallback(this); d->invalidateRenderControl(); + d->deviceLost = true; d->rhi = nullptr; break; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 78eb95f4f0..9f113a8f18 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -310,6 +310,9 @@ private slots: void symbolToVariant(); void garbageCollectedObjectMethodBase(); + + void deleteDefineCycle(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -6302,6 +6305,25 @@ void tst_QJSEngine::garbageCollectedObjectMethodBase() } } +void tst_QJSEngine::deleteDefineCycle() +{ + QJSEngine engine; + QStringList stackTrace; + + QJSValue result = engine.evaluate(QString::fromLatin1(R"( + let global = ({}) + + for (let j = 0; j < 1000; j++) { + for (let i = 0; i < 2; i++) { + const name = "test" + i + delete global[name] + Object.defineProperty(global, name, { get() { return 0 }, configurable: true }) + } + } + )"), {}, 1, &stackTrace); + QVERIFY(stackTrace.isEmpty()); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 516abed9e8..e65c1a3259 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -1124,6 +1124,25 @@ void tst_QJSValue::toVariant() QCOMPARE(func.toVariant().metaType(), QMetaType::fromType<QJSValue>()); } + + // object with custom prototype + { + QJSValue object = eng.evaluate(R"js( + (function(){ + function Person(firstName, lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + return new Person("John", "Doe"); + })(); + )js"); + QVERIFY(object.isObject()); + auto asVariant = object.toVariant(); + QCOMPARE(asVariant.metaType(), QMetaType::fromType<QVariantMap>()); + auto variantMap = asVariant.value<QVariantMap>(); + QVERIFY(variantMap.contains("firstName")); + QCOMPARE(variantMap["firstName"].toString(), "John"); + } } void tst_QJSValue::toPrimitive_data() diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 0fd72351da..18f74f64e6 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -12,9 +12,11 @@ set(cpp_sources enumProperty.h gadgetwithenum.h invisible.h + listprovider.h multiforeign.h objectwithmethod.h person.cpp person.h + resettable.h sequenceToIterable.h sequencetypeexample.cpp sequencetypeexample.h state.h @@ -36,6 +38,7 @@ set(qml_files Cycle1.qml Cycle2.qml Cycle3.qml + CppMethodListReturnType.qml CxxTypeFromDir.qml CxxTypeFromImplicit.qml Dummy.qml @@ -84,6 +87,7 @@ set(qml_files compositeTypeMethod.qml compositesingleton.qml consoleObject.qml + consoleTrace.qml construct.qml contextParam.qml conversionDecrement.qml @@ -116,6 +120,7 @@ set(qml_files extendedTypes.qml failures.qml fallbacklookups.qml + fallbackresettable.qml fileDialog.qml fromBoolValue.qml functionLookup.qml @@ -175,10 +180,14 @@ set(qml_files popContextAfterRet.qml prefixedMetaType.qml pressAndHoldButton.qml + reduceWithNullThis.qml readEnumFromInstance.qml registerelimination.qml registerPropagation.qml + renameAdjust.qml + resettable.qml revisions.qml + scopeIdLookup.qml scopeVsObject.qml script.js script.mjs diff --git a/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml new file mode 100644 index 0000000000..9c3ce4e877 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/CppMethodListReturnType.qml @@ -0,0 +1,12 @@ +pragma Strict + +import QtQuick +import TestTypes + +Item { + ListProvider { + id: listProvider + } + + property var list: listProvider.intList() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/Test.qml b/tests/auto/qml/qmlcppcodegen/data/Test.qml index 65c25aa100..6fc5be7191 100644 --- a/tests/auto/qml/qmlcppcodegen/data/Test.qml +++ b/tests/auto/qml/qmlcppcodegen/data/Test.qml @@ -1,4 +1,4 @@ -pragma Strict + import TestTypes 1.0 CppBaseClass { diff --git a/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml new file mode 100644 index 0000000000..a80af89ddd --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/consoleTrace.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { + function a() { b() } + function b() { c() } + function c() { console.trace() } + Component.onCompleted: a() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h index 64c2850bda..100e9d825c 100644 --- a/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h +++ b/tests/auto/qml/qmlcppcodegen/data/dynamicmeta.h @@ -5,34 +5,44 @@ #define DYNAMICMETA_H #include <private/qobject_p.h> +#include <private/qmetaobjectbuilder_p.h> #include <QtQmlIntegration/qqmlintegration.h> -struct FreeDeleter { - void operator()(QMetaObject *meta) { free(meta); } -}; - template<typename T> class MetaObjectData : public QDynamicMetaObjectData { Q_DISABLE_COPY_MOVE(MetaObjectData) public: - MetaObjectData() = default; - ~MetaObjectData() = default; + MetaObjectData() + { + QMetaObjectBuilder builder; + builder.setSuperClass(&T::staticMetaObject); + builder.setFlags(builder.flags() | DynamicMetaObject); + metaObject = builder.toMetaObject(); + }; + + ~MetaObjectData() { + free(metaObject); + }; QMetaObject *toDynamicMetaObject(QObject *) override { - return const_cast<QMetaObject *>(&T::staticMetaObject); + return metaObject; } int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override { return o->qt_metacall(call, idx, argv); } + + QMetaObject *metaObject = nullptr; }; class DynamicMeta : public QObject { Q_OBJECT Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged FINAL) + Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal shadowable READ shadowable CONSTANT) QML_ELEMENT public: @@ -54,11 +64,26 @@ public: Q_INVOKABLE int bar(int baz) { return baz + 12; } + qreal value() const { return m_value; } + qreal shadowable() const { return 25; } + +public slots: + void resetValue() { setValue(0); } + void setValue(qreal value) + { + if (m_value == value) + return; + m_value = value; + emit valueChanged(); + } + Q_SIGNALS: void fooChanged(); + void valueChanged(); private: int m_foo = 0; + qreal m_value = 0; }; class DynamicMetaSingleton : public DynamicMeta diff --git a/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml new file mode 100644 index 0000000000..175c8cf00f --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/fallbackresettable.qml @@ -0,0 +1,23 @@ + +import QtQml +import TestTypes + +DynamicMeta { + id: self + value: 999 + + property double notResettable: 10 + property double notResettable2: { return undefined } + + property DynamicMeta shadowing: DynamicMeta { + property var shadowable: undefined + } + + function doReset() { self.value = undefined } + function doReset2() { self.value = shadowing.shadowable } + function doNotReset() { self.notResettable = undefined } + + signal aaa() + signal bbb() + onAaa: objectName = self.bbb() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/listprovider.h b/tests/auto/qml/qmlcppcodegen/data/listprovider.h new file mode 100644 index 0000000000..076944b586 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/listprovider.h @@ -0,0 +1,24 @@ +#ifndef QLISTPROVIDER_H +#define QLISTPROVIDER_H + +#include <QObject> +#include <QQmlEngine> + +class QListProvider : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(ListProvider) + +public: + explicit QListProvider(QObject *parent = nullptr) : QObject(parent) { } + + Q_INVOKABLE QList<int> intList() const + { + QList<int> list; + for (int i = 0; i < 3; ++i) + list.append(i); + return list; + } +}; + +#endif // QLISTPROVIDER_H diff --git a/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml new file mode 100644 index 0000000000..c6fda8c739 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/reduceWithNullThis.qml @@ -0,0 +1,18 @@ +import QtQml + +QtObject { + id: mainItem + property int topPadding: 12 + property int bottomPadding: 12 + + property int preferredHeight: mainItem.children.reduce(maximumImplicitHeightReducer, 0) + topPadding + bottomPadding + function maximumImplicitHeightReducer(accumulator: real, item: Binding): real { + return Math.max(accumulator, (item.objectName + "b").length); + } + + property int preferredHeight2: mainItem.children.reduce((accumulator, item) => { + return Math.max(accumulator, (item.objectName + "b").length); + }, 0) + topPadding + bottomPadding + + property list<Binding> children: [ Binding { objectName: "aaa" } ] +} diff --git a/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml new file mode 100644 index 0000000000..9352163ba7 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/renameAdjust.qml @@ -0,0 +1,19 @@ +pragma Strict +import QtQml + +QtObject { + id: last + property int value: 10 + + function verify(i: int) { + if (last.value !== i) + console.error("failed", last.value, i); + else + console.log("success") + } + + Component.onCompleted: { + verify(10) + verify(11) + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.h b/tests/auto/qml/qmlcppcodegen/data/resettable.h new file mode 100644 index 0000000000..129ef36ef4 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/resettable.h @@ -0,0 +1,39 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef RESETTABLE_H +#define RESETTABLE_H + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class ResettableProperty : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(Resettable) + Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue NOTIFY valueChanged FINAL) + Q_PROPERTY(qreal shadowable READ shadowable CONSTANT) + +public: + explicit ResettableProperty(QObject *parent = nullptr) : QObject(parent) {} + qreal value() const { return m_value; } + qreal shadowable() const { return 25; } + +public slots: + void resetValue() { setValue(0); } + void setValue(qreal value) + { + if (m_value == value) + return; + m_value = value; + emit valueChanged(); + } + +signals: + void valueChanged(); + +private: + qreal m_value = 0; +}; + +#endif // RESETTABLE_H diff --git a/tests/auto/qml/qmlcppcodegen/data/resettable.qml b/tests/auto/qml/qmlcppcodegen/data/resettable.qml new file mode 100644 index 0000000000..5fa11438c4 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/resettable.qml @@ -0,0 +1,23 @@ + +import QtQml +import TestTypes + +Resettable { + id: self + value: 999 + + property double notResettable: 10 + property double notResettable2: { return undefined } + + property Resettable shadowing: Resettable { + property var shadowable: undefined + } + + function doReset() { self.value = undefined } + function doReset2() { self.value = shadowing.shadowable } + function doNotReset() { self.notResettable = undefined } + + signal aaa() + signal bbb() + onAaa: objectName = self.bbb() +} diff --git a/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml new file mode 100644 index 0000000000..e23f180598 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/scopeIdLookup.qml @@ -0,0 +1,20 @@ +pragma ComponentBehavior: Bound + +import QtQml + +QtObject { + id: root + + property QtObject b: QtObject { + id: bar + objectName: "outer" + } + + property Instantiator i: Instantiator { + model: 1 + delegate: QtObject { + property QtObject bar: QtObject { objectName: "inner" } + Component.onCompleted: root.objectName = bar.objectName + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/data/urlString.qml b/tests/auto/qml/qmlcppcodegen/data/urlString.qml index 511c54532c..a83855ebdb 100644 --- a/tests/auto/qml/qmlcppcodegen/data/urlString.qml +++ b/tests/auto/qml/qmlcppcodegen/data/urlString.qml @@ -9,5 +9,12 @@ QtObject { Component.onCompleted: { c = "http://dddddd.com"; self.d = "http://aaaaaa.com"; + myUrlChanged(c) + } + + signal myUrlChanged(urlParam: url) + + onMyUrlChanged: (urlParam) => { + objectName = urlParam; } } diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index bbf0c92550..0b04a0ee2e 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -4,6 +4,7 @@ #include <data/cppbaseclass.h> #include <data/enumproblems.h> #include <data/objectwithmethod.h> +#include <data/resettable.h> #include <QtQml/private/qqmlengine_p.h> #include <QtQml/private/qqmlpropertycachecreator_p.h> @@ -28,6 +29,7 @@ private slots: void initTestCase(); void simpleBinding(); + void cppMethodListReturnType(); void cppValueTypeList(); void anchorsFill(); void signalHandler(); @@ -76,6 +78,7 @@ private slots: void shifts(); void valueTypeProperty(); void propertyOfParent(); + void reduceWithNullThis(); void readEnumFromInstance(); void accessModelMethodFromOutSide(); void functionArguments(); @@ -110,7 +113,13 @@ private slots: void unknownAttached(); void variantlist(); void popContextAfterRet(); + void renameAdjust(); + + void resettableProperty(); + void resettableProperty_data(); + void revisions(); + void scopeIdLookup(); void invisibleBase(); void notEqualsInt(); void infinities(); @@ -158,6 +167,7 @@ private slots: void callWithSpread(); void nullComparison(); void consoleObject(); + void consoleTrace(); void multiForeign(); void namespaceWithEnum(); void enumProblems(); @@ -212,6 +222,17 @@ void tst_QmlCppCodegen::simpleBinding() } } +void tst_QmlCppCodegen::cppMethodListReturnType() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/CppMethodListReturnType.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("list").toList()[2].toInt(), 2); +} + void tst_QmlCppCodegen::cppValueTypeList() { QQmlEngine engine; @@ -1152,6 +1173,18 @@ void tst_QmlCppCodegen::propertyOfParent() } } +void tst_QmlCppCodegen::reduceWithNullThis() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/reduceWithNullThis.qml"_s)); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("preferredHeight").toDouble(), 28.0); + QCOMPARE(object->property("preferredHeight2").toDouble(), 28.0); +} + void tst_QmlCppCodegen::readEnumFromInstance() { QQmlEngine engine; @@ -1697,6 +1730,7 @@ void tst_QmlCppCodegen::urlString() QCOMPARE(qvariant_cast<QUrl>(rootObject->property("c")), QUrl(u"http://dddddd.com"_s)); QCOMPARE(qvariant_cast<QUrl>(rootObject->property("d")), QUrl(u"http://aaaaaa.com"_s)); QCOMPARE(qvariant_cast<QUrl>(rootObject->property("e")), QUrl(u"http://a112233.de"_s)); + QCOMPARE(rootObject->objectName(), QLatin1String("http://dddddd.com")); } @@ -1926,6 +1960,62 @@ void tst_QmlCppCodegen::popContextAfterRet() QCOMPARE(o->objectName(), u"backgroundImage"_s); } +void tst_QmlCppCodegen::renameAdjust() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/renameAdjust.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage(QtDebugMsg, "success"); + QTest::ignoreMessage(QtCriticalMsg, "failed 10 11"); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); +} + +void tst_QmlCppCodegen::resettableProperty() +{ + QFETCH(QString, url); + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(url)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + u":10:5: Unable to assign [undefined] to double"_s)); + + QScopedPointer<QObject> o(c.create()); + QVERIFY(o); + + QCOMPARE(o->property("value").toDouble(), 999); + QMetaObject::invokeMethod(o.data(), "doReset"); + QCOMPARE(o->property("value").toDouble(), 0); + + o->setProperty("value", double(82)); + QCOMPARE(o->property("value").toDouble(), 82); + QMetaObject::invokeMethod(o.data(), "doReset2"); + QCOMPARE(o->property("value").toDouble(), 0); + + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + u":18: Error: Cannot assign [undefined] to double"_s)); + QCOMPARE(o->property("notResettable").toDouble(), 10); + QMetaObject::invokeMethod(o.data(), "doNotReset"); + QCOMPARE(o->property("notResettable").toDouble(), 10); + QCOMPARE(o->property("notResettable2").toDouble(), 0); // not NaN + + o->setObjectName(u"namename"_s); + QTest::ignoreMessage( + QtWarningMsg, qPrintable(url + u":22: Error: Cannot assign [undefined] to QString"_s)); + QMetaObject::invokeMethod(o.data(), "aaa"); + QCOMPARE(o->objectName(), u"namename"_s); +} + +void tst_QmlCppCodegen::resettableProperty_data() +{ + QTest::addColumn<QString>("url"); + QTest::addRow("object lookups") << u"qrc:/qt/qml/TestTypes/resettable.qml"_s; + QTest::addRow("fallback lookups") << u"qrc:/qt/qml/TestTypes/fallbackresettable.qml"_s; +} + void tst_QmlCppCodegen::revisions() { QQmlEngine engine; @@ -1938,6 +2028,16 @@ void tst_QmlCppCodegen::revisions() QCOMPARE(o->property("gotten").toInt(), 5); } +void tst_QmlCppCodegen::scopeIdLookup() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/scopeIdLookup.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("objectName").toString(), u"outer"_s); +} + void tst_QmlCppCodegen::invisibleBase() { QQmlEngine engine; @@ -3102,6 +3202,32 @@ void tst_QmlCppCodegen::consoleObject() QVERIFY(!p.isNull()); } +void tst_QmlCppCodegen::consoleTrace() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleTrace.qml"_s)); + QVERIFY2(!component.isError(), component.errorString().toUtf8()); + +#if !defined(QT_NO_DEBUG) || defined(QT_TEST_FORCE_INTERPRETER) + // All line numbers in debug mode or when interpreting + + QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6) +b (qrc:/qt/qml/TestTypes/consoleTrace.qml:5) +a (qrc:/qt/qml/TestTypes/consoleTrace.qml:4) +expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml:7))"); +#else + // Only top-most line number otherwise + + QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6) +b (qrc:/qt/qml/TestTypes/consoleTrace.qml) +a (qrc:/qt/qml/TestTypes/consoleTrace.qml) +expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml))"); +#endif + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); +} + void tst_QmlCppCodegen::multiForeign() { QQmlEngine engine; diff --git a/tests/auto/qml/qmlformat/data/Annotations.formatted.qml b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml index 1898585e31..019b7f0141 100644 --- a/tests/auto/qml/qmlformat/data/Annotations.formatted.qml +++ b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml @@ -10,17 +10,14 @@ import QtCharts 2.0 @Pippo { atg1: 3 } -@Annotation2 { -} +@Annotation2 {} Item { - @Annotate { - } + @Annotate {} anchors.fill: parent @AnnotateMore { property int x: 5 } - @AnnotateALot { - } + @AnnotateALot {} property variant othersSlice: 0 //![1] @@ -79,14 +76,12 @@ Item { @BindingAnn { bType: 2 } - val2: Item { - } + val2: Item {} @BindingAnn { bType: 3 } val3: [ - Item { - } + Item {} ] @BindingAnn { bType: 4 diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml index ca870e948a..b1662e8898 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.2spaces.qml @@ -135,11 +135,9 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // some_read_only_bool diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.qml index b77ebf1213..c4aef78924 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.qml @@ -135,11 +135,9 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // some_read_only_bool diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml index 966fba78f1..7cea50213a 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.tabs.qml @@ -135,11 +135,9 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // some_read_only_bool diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted2.qml b/tests/auto/qml/qmlformat/data/Example1.formatted2.qml index 916e19a46b..7049686900 100644 --- a/tests/auto/qml/qmlformat/data/Example1.formatted2.qml +++ b/tests/auto/qml/qmlformat/data/Example1.formatted2.qml @@ -142,11 +142,9 @@ Item { // This is an orphan // This is a cool text - Text { - }, + Text {}, // This is a cool rectangle - Rectangle { - } + Rectangle {} ] // This comment is related to the property animation PropertyAnimation on x { diff --git a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml index 7dfe435ac0..95b4fcf4a3 100644 --- a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml +++ b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml @@ -1,4 +1,3 @@ // This comment should be directly above Item after formatting -Item { -} +Item {} diff --git a/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml b/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml new file mode 100644 index 0000000000..8ae4dd7c88 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrayEndComma.formatted.qml @@ -0,0 +1,4 @@ +Item { + // should keep its comma + property var some_array_literal: [30, 20, 0.3,] +} diff --git a/tests/auto/qml/qmlformat/data/arrayEndComma.qml b/tests/auto/qml/qmlformat/data/arrayEndComma.qml new file mode 100644 index 0000000000..1aec09515c --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrayEndComma.qml @@ -0,0 +1,8 @@ +Item { + // should keep its comma + property var some_array_literal: [ + 30, + 20, + 0.3, + ] +} diff --git a/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml index a7e70d70ea..071e3bc69f 100644 --- a/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml +++ b/tests/auto/qml/qmlformat/data/blanklinesAfterComment.formatted.qml @@ -9,5 +9,4 @@ import QtQml -QtObject { -} +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml index 209ecd8b05..0bfcc58179 100644 --- a/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml +++ b/tests/auto/qml/qmlformat/data/destructuringFunctionParameter.formatted.qml @@ -14,11 +14,11 @@ QtObject { }, fun = 42 } = { - "destructuring": 123, - "is": { - "x": 123 + destructuring: 123, + is: { + x: 123 }, - "fun": 456 + fun: 456 }) { } } diff --git a/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml b/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml index 0c7a2829c9..8eaac71178 100644 --- a/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml +++ b/tests/auto/qml/qmlformat/data/dontRemoveComments.formatted.qml @@ -1,13 +1,15 @@ Item { - property var test: [{ + property var test: [ + { // Testing "foo": "bar" - }] + } + ] onTestChanged: { fooBar(test, { - // Testing - "foo": "bar" - }); + // Testing + "foo": "bar" + }); } } diff --git a/tests/auto/qml/qmlformat/data/dontRemoveComments.qml b/tests/auto/qml/qmlformat/data/dontRemoveComments.qml index 1797834879..2d2b4b6705 100644 --- a/tests/auto/qml/qmlformat/data/dontRemoveComments.qml +++ b/tests/auto/qml/qmlformat/data/dontRemoveComments.qml @@ -1,8 +1,10 @@ Item { - property var test: [{ -// Testing + property var test: [ + { + // Testing "foo": "bar" - }] + } + ] onTestChanged: { fooBar(test, { diff --git a/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml b/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml new file mode 100644 index 0000000000..3cb99b704a --- /dev/null +++ b/tests/auto/qml/qmlformat/data/escapeChars.formatted.qml @@ -0,0 +1,15 @@ +import QtQuick + +Item { + x: { + const s = "\""; + let a = { + "\"": "\\" + }; + let patron = { + "\\\"\n\n": "\?\?\\\"", + "": "", + "\'\"\n": 1 + }; + } +} diff --git a/tests/auto/qml/qmlformat/data/escapeChars.qml b/tests/auto/qml/qmlformat/data/escapeChars.qml new file mode 100644 index 0000000000..c82ff3119e --- /dev/null +++ b/tests/auto/qml/qmlformat/data/escapeChars.qml @@ -0,0 +1,16 @@ +import QtQuick + +Item { + x: { + const s = "\"" + let a = { + "\"": "\\" + }; + + let patron = { + "\\\"\n\n" : "\?\?\\\"","": "", "\'\"\n":1 + }; + + + } +} diff --git a/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml b/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml index d452ba2b8c..91f520b5fc 100644 --- a/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml +++ b/tests/auto/qml/qmlformat/data/functionsSpacing.formatted.qml @@ -6,8 +6,7 @@ Item { } function test2() { } - Button { - } + Button {} function test4() { } diff --git a/tests/auto/qml/qmlformat/data/importStatements.formatted.qml b/tests/auto/qml/qmlformat/data/importStatements.formatted.qml new file mode 100644 index 0000000000..6613becaca --- /dev/null +++ b/tests/auto/qml/qmlformat/data/importStatements.formatted.qml @@ -0,0 +1,8 @@ +import QtQml + +import QtQuick +import QtQuick.Controls + +import org.test.module + +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/importStatements.qml b/tests/auto/qml/qmlformat/data/importStatements.qml new file mode 100644 index 0000000000..efe1872e93 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/importStatements.qml @@ -0,0 +1,12 @@ +import QtQml + + +import QtQuick +import QtQuick.Controls + +import org.test.module + + + +QtObject { +} diff --git a/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml b/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml new file mode 100644 index 0000000000..09ab9454e1 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/javascriptBlock.formatted.qml @@ -0,0 +1,6 @@ +Item { + block1: { + console.log("Hello, world!"); + } + emptyBlock: {} +} diff --git a/tests/auto/qml/qmlformat/data/javascriptBlock.qml b/tests/auto/qml/qmlformat/data/javascriptBlock.qml new file mode 100644 index 0000000000..1665c81f42 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/javascriptBlock.qml @@ -0,0 +1,5 @@ +Item { + block1: {console.log("Hello, world!");} + emptyBlock: { + } +} diff --git a/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml b/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml index 45d04c5887..46e3e963ea 100644 --- a/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml +++ b/tests/auto/qml/qmlformat/data/multilineComment.formatted.qml @@ -1,11 +1,9 @@ Item { - Item { - } + Item {} /* This is a multiline comment. it should stay attached to Commented instead of getting orphaned. */ // This should also stick to Commented - Commented { - } + Commented {} } diff --git a/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml b/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml index d8e1978ae8..3feac73688 100644 --- a/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml +++ b/tests/auto/qml/qmlformat/data/objectDestructuring.formatted.qml @@ -15,21 +15,21 @@ QtObject { }] = array; const [a4, b4, ...[c, d]] = array; const obj = { - "_a": 1, - "_b": 2 + _a: 1, + _b: 2 }; const { a5, b5 } = obj; const { - "a6": a_, - "b6": b1_ + a6: a_, + b6: b1_ } = obj; const { - "a7": a11 = 4, + a7: a11 = 4, b11 = 34, - "c1": b111, + c1: b111, d1 } = obj; let key = a; @@ -53,79 +53,79 @@ QtObject { }] = array; [a, b, ...[c, d]] = array; const obj = { - "_a": 1, - "_b": 2 + _a: 1, + _b: 2 }; ({ a, b } = obj); // brackets are required ({ - "a": a1, - "b": b1 + a: a1, + b: b1 } = obj); const complicatedObject = { - "a": 1, - "b": { - "c": 2, - "d": { - "e": 3, - "f": [4, 5, 6] + a: 1, + b: { + c: 2, + d: { + e: 3, + f: [4, 5, 6] } }, - "g": [7, 8, 9] + g: [7, 8, 9] }; const { patron, - "b": { + b: { mafik, - "d": { + d: { e, - "f": [, secondF, ...restF] + f: [, secondF, ...restF] } }, - "g": [firstG, ...restG] + g: [firstG, ...restG] } = complicatedObject; } Component.onCompleted: { const myFunction = myLambda => { const myObject = { - "a": 1, - "b": { - "c": 2, - "d": [3, 4, 5] + a: 1, + b: { + c: 2, + d: [3, 4, 5] }, - "e": { - "f": 6, - "g": { - "h": 7, - "i": [8, 9, 10] + e: { + f: 6, + g: { + h: 7, + i: [8, 9, 10] } } }; myLambda(myObject); }; myFunction(({ - a, - "b": { - c, - "d": [firstD] - }, - "e": { - f, - "g": { - h, - "i": [, secondI] - } + a, + b: { + c, + d: [firstD] + }, + e: { + f, + g: { + h, + i: [, secondI] } - }) => { - console.log(a); // 1 - console.log(c); // 2 - console.log(firstD); // 3 - console.log(f); // 6 - console.log(h); // 7 - console.log(secondI); // 9 - }); + } + }) => { + console.log(a); // 1 + console.log(c); // 2 + console.log(firstD); // 3 + console.log(f); // 6 + console.log(h); // 7 + console.log(secondI); // 9 + }); } } diff --git a/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml b/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml index bd0406e595..df26a9b599 100644 --- a/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml +++ b/tests/auto/qml/qmlformat/data/objectsSpacing.formatted.qml @@ -1,6 +1,9 @@ Item { + Button {} + Button { + id: foo } height: 360 diff --git a/tests/auto/qml/qmlformat/data/objectsSpacing.qml b/tests/auto/qml/qmlformat/data/objectsSpacing.qml index 6adc89778c..0239b05145 100644 --- a/tests/auto/qml/qmlformat/data/objectsSpacing.qml +++ b/tests/auto/qml/qmlformat/data/objectsSpacing.qml @@ -3,6 +3,10 @@ Item { Button { } + Button { + id: foo + } + height: 360 width: 360 diff --git a/tests/auto/qml/qmlformat/data/pragma.formatted.qml b/tests/auto/qml/qmlformat/data/pragma.formatted.qml index 092d9ee80f..0a4ad91b56 100644 --- a/tests/auto/qml/qmlformat/data/pragma.formatted.qml +++ b/tests/auto/qml/qmlformat/data/pragma.formatted.qml @@ -2,7 +2,7 @@ pragma Singleton pragma ComponentBehavior: Bound pragma FunctionSignatureBehavior: Enforced pragma ValueTypeBehavior: Copy + import QtQml -QtObject { -} +QtObject {} diff --git a/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml b/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml index 606e522848..9214014889 100644 --- a/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml +++ b/tests/auto/qml/qmlformat/data/propertyNames.formatted.qml @@ -3,9 +3,9 @@ Item { var copiedItem = "copied value"; var computedItem = "computedName"; var obj = { - "identifierName": "identifier value", + identifierName: "identifier value", "string name": "string value", - "Infinity": "numeric value", + Infinity: "numeric value", [computedItem]: "computed value", copiedItem }; diff --git a/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml b/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml index 40cf5068da..923f0642d7 100644 --- a/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml +++ b/tests/auto/qml/qmlformat/data/statesAndTransitions.formatted.qml @@ -2,15 +2,12 @@ QtObject { id: foo states: [ - State { - } + State {} ] transitions: [ - Transition { - } + Transition {} ] // This needs to be *before* states and transitions after formatting - Item { - } + Item {} } diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp index 5f8f39ad84..0c3a2a276c 100644 --- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp +++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp @@ -321,6 +321,18 @@ void TestQmlformat::testFormat_data() QTest::newRow("ellipsisFunctionArgument") << "ellipsisFunctionArgument.qml" << "ellipsisFunctionArgument.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("escapeChars") + << "escapeChars.qml" + << "escapeChars.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("arrayEndComma") + << "arrayEndComma.qml" + << "arrayEndComma.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("importStatements") + << "importStatements.qml" + << "importStatements.formatted.qml" << QStringList{} << RunOption::OnCopy; + QTest::newRow("javascriptBlock") + << "javascriptBlock.qml" + << "javascriptBlock.formatted.qml" << QStringList{} << RunOption::OnCopy; } void TestQmlformat::testFormat() diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 8b8c9c2d2f..e6354a39cd 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -2025,6 +2025,11 @@ void TestQmllint::quickPlugin() 12, 30 }, Message { + u"You should remove any bindings on the \"target\" property and avoid " + "custom-parsed bindings in PropertyChanges."_s, + 11, 29 + }, + Message { u"Unknown property \"notThere\" in PropertyChanges."_s, 13, 31 } diff --git a/tests/auto/qml/qmlsplitlib/CMakeLists.txt b/tests/auto/qml/qmlsplitlib/CMakeLists.txt index f9783941c2..a0ae9dfde9 100644 --- a/tests/auto/qml/qmlsplitlib/CMakeLists.txt +++ b/tests/auto/qml/qmlsplitlib/CMakeLists.txt @@ -13,6 +13,17 @@ generate_export_header(tst_qmlsplitlib_library) target_link_libraries(tst_qmlsplitlib_library Qt::Core Qt::QmlIntegration) +qt_add_library(tst-qmlsplitlib-library-2 + lib2.h + lib2.cpp +) +qt_autogen_tools_initial_setup(tst-qmlsplitlib-library-2) +target_include_directories(tst-qmlsplitlib-library-2 PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # find autogenerated header +generate_export_header(tst-qmlsplitlib-library-2) + +target_link_libraries(tst-qmlsplitlib-library-2 Qt::Core Qt::QmlIntegration) + + qt_internal_add_test(tst_qmlsplitlib SOURCES tst_qmlsplitlib.cpp @@ -23,6 +34,7 @@ qt_internal_add_test(tst_qmlsplitlib qt_autogen_tools_initial_setup(tst_qmlsplitlib) qt6_generate_foreign_qml_types(tst_qmlsplitlib_library tst_qmlsplitlib) +qt6_generate_foreign_qml_types(tst-qmlsplitlib-library-2 tst_qmlsplitlib) qt_policy(SET QTP0001 NEW) @@ -30,6 +42,7 @@ qt6_add_qml_module(tst_qmlsplitlib URI "SplitLib" QML_FILES main.qml + main2.qml ) -target_link_libraries(tst_qmlsplitlib PRIVATE tst_qmlsplitlib_library) +target_link_libraries(tst_qmlsplitlib PRIVATE tst_qmlsplitlib_library tst-qmlsplitlib-library-2) diff --git a/tests/auto/qml/qmlsplitlib/lib2.cpp b/tests/auto/qml/qmlsplitlib/lib2.cpp new file mode 100644 index 0000000000..d4489f5c77 --- /dev/null +++ b/tests/auto/qml/qmlsplitlib/lib2.cpp @@ -0,0 +1,5 @@ +#include "lib2.h" + +bool SplitLib2::transmogrify() { return true; } + +#include "moc_lib2.cpp" diff --git a/tests/auto/qml/qmlsplitlib/lib2.h b/tests/auto/qml/qmlsplitlib/lib2.h new file mode 100644 index 0000000000..480ec7709e --- /dev/null +++ b/tests/auto/qml/qmlsplitlib/lib2.h @@ -0,0 +1,30 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef SPLITLIB_LIB2_H +#define SPLITLIB_LIB2_H +#include "tst-qmlsplitlib-library-2_export.h" + +#include <QtQmlIntegration/qqmlintegration.h> +#include <QtCore/qglobal.h> +#include <QObject> + +class TST_QMLSPLITLIB_LIBRARY_2_EXPORT SplitLib2 : public QObject +{ +public: + Q_OBJECT + QML_ELEMENT + + Q_INVOKABLE bool transmogrify(); +}; + + +class TST_QMLSPLITLIB_LIBRARY_2_EXPORT Foo2 : public QObject +{ +public: + Q_OBJECT + QML_NAMED_ELEMENT(Bar2) + QML_SINGLETON +}; + +#endif diff --git a/tests/auto/qml/qmlsplitlib/main2.qml b/tests/auto/qml/qmlsplitlib/main2.qml new file mode 100644 index 0000000000..9b42b70fde --- /dev/null +++ b/tests/auto/qml/qmlsplitlib/main2.qml @@ -0,0 +1,8 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import SplitLib + +SplitLib2 { + property string s: Bar2.objectName +} diff --git a/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp b/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp index 893ed77ff7..92a9e07c4a 100644 --- a/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp +++ b/tests/auto/qml/qmlsplitlib/tst_qmlsplitlib.cpp @@ -6,12 +6,14 @@ #include <QObject> #include <qtest.h> #include "lib.h" +#include "lib2.h" class tst_splitlib : public QObject { Q_OBJECT private slots: void verifyComponent(); + void verifyComponent2(); }; void tst_splitlib::verifyComponent() @@ -25,5 +27,16 @@ void tst_splitlib::verifyComponent() QVERIFY(lib); } +void tst_splitlib::verifyComponent2() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QStringLiteral("qrc:/qt/qml/SplitLib/main2.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + auto lib = qobject_cast<SplitLib2 *>(o.get()); + QVERIFY(lib); +} + QTEST_MAIN(tst_splitlib) #include "tst_qmlsplitlib.moc" diff --git a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt index 6545010e85..a18f17c733 100644 --- a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt +++ b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt @@ -109,6 +109,7 @@ set(qml_sources singletons.qml mySignals.qml myCheckBox.qml + signalConnections.qml # support types: DefaultPropertySingleChild.qml diff --git a/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml b/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml new file mode 100644 index 0000000000..abcf6fdd60 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/signalConnections.qml @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQml + +QtObject { + id: root + + property bool cycleEnabled: false + property bool cycleFirst: false + property bool cycleSecond: false + + property Timer enableTimer: Timer { + running: root.cycleEnabled + interval: 1 + onTriggered: { + conn.enabled = !conn.enabled; + root.cycleEnabled = false; + } + } + + property Timer firstTimer: Timer { + id: firstTimer + objectName: "first" + running: root.cycleFirst + interval: 1 + onTriggered: root.cycleFirst = false + } + + property Timer secondTimer: Timer { + objectName: "second" + running: root.cycleSecond + interval: 1 + onTriggered: conn.target = this; + repeat: true + } + + property Connections conn: Connections { + id: conn + target: firstTimer + function onTriggered(m) { + root.objectName = target.objectName + root.cycleSecond = false; + } + } +} diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp index 048daed795..f477956060 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.cpp +++ b/tests/auto/qml/qmltc/tst_qmltc.cpp @@ -82,6 +82,7 @@ #include "testprivateproperty.h" #include "singletons.h" #include "mysignals.h" +#include "signalconnections.h" // Qt: #include <QtCore/qstring.h> @@ -3190,4 +3191,30 @@ void tst_qmltc::constSignalParameters() QCOMPARE(fromQmltc.object(), &myItem); } +void tst_qmltc::signalConnections() +{ + QQmlEngine e; + PREPEND_NAMESPACE(signalConnections) createdByQmltc(&e); + + QVERIFY(createdByQmltc.objectName().isEmpty()); + createdByQmltc.setCycleFirst(true); + QTRY_VERIFY(!createdByQmltc.cycleFirst()); + QCOMPARE(createdByQmltc.objectName(), QLatin1String("first")); + + createdByQmltc.setObjectName(QLatin1String("none")); + createdByQmltc.setCycleEnabled(true); + QTRY_VERIFY(!createdByQmltc.cycleEnabled()); + + createdByQmltc.setCycleFirst(true); + QTRY_VERIFY(!createdByQmltc.cycleFirst()); + QCOMPARE(createdByQmltc.objectName(), QLatin1String("none")); + + createdByQmltc.setCycleEnabled(true); + QTRY_VERIFY(!createdByQmltc.cycleEnabled()); + + createdByQmltc.setCycleSecond(true); + QTRY_VERIFY(!createdByQmltc.cycleSecond()); + QCOMPARE(createdByQmltc.objectName(), QLatin1String("second")); +} + QTEST_MAIN(tst_qmltc) diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h index 59a6bd777a..63061dd8fa 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.h +++ b/tests/auto/qml/qmltc/tst_qmltc.h @@ -92,4 +92,5 @@ private slots: void inlineComponentsFromDifferentFiles(); void singletons(); void constSignalParameters(); + void signalConnections(); }; diff --git a/tests/auto/qml/qqmlcontext/data/A.qml b/tests/auto/qml/qqmlcontext/data/A.qml new file mode 100644 index 0000000000..1a44f005af --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/A.qml @@ -0,0 +1,6 @@ +import QtQml + +B { + id: b + property int y: 2 +} diff --git a/tests/auto/qml/qqmlcontext/data/B.qml b/tests/auto/qml/qqmlcontext/data/B.qml new file mode 100644 index 0000000000..7754728304 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/B.qml @@ -0,0 +1,6 @@ +import QtQml + +C { + id: z + property int z: 3 +} diff --git a/tests/auto/qml/qqmlcontext/data/C.qml b/tests/auto/qml/qqmlcontext/data/C.qml new file mode 100644 index 0000000000..6afd23aa6c --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/C.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + id: outer + objectName: "the" + "C" +} diff --git a/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml new file mode 100644 index 0000000000..7b1f46d7d1 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/destroyContextObject.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + property A a: A {} +} diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index 055c6225f3..a14f980766 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -49,6 +49,7 @@ private slots: void outerContextObject(); void contextObjectHierarchy(); void destroyContextProperty(); + void destroyContextObject(); void numericContextProperty(); void gcDeletesContextObject(); @@ -982,6 +983,30 @@ void tst_qqmlcontext::destroyContextProperty() // TODO: Or are we? } +void tst_qqmlcontext::destroyContextObject() +{ + QQmlEngine engine; + QList<QQmlRefPointer<QQmlContextData>> contexts; + QQmlComponent component(&engine, testFileUrl("destroyContextObject.qml")); + QScopedPointer<QObject> root(component.create()); + + QPointer<QObject> a = root->property("a").value<QObject *>(); + QVERIFY(a); + + for (QQmlRefPointer<QQmlContextData> context = QQmlData::get(a)->ownContext; + context; context = context->parent()) { + contexts.append(context); + } + + QObject *deleted = a.data(); + root.reset(); + + QVERIFY(a.isNull()); + + for (const auto &context : contexts) + QVERIFY(context->contextObject() != deleted); +} + void tst_qqmlcontext::numericContextProperty() { QQmlEngine engine; diff --git a/tests/auto/qml/qqmllanguage/data/AliasHolder.qml b/tests/auto/qml/qqmllanguage/data/AliasHolder.qml new file mode 100644 index 0000000000..42aed6ed26 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/AliasHolder.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + property alias strokeStyle: path.restoreMode + property Binding p: Binding { id: path } +} diff --git a/tests/auto/qml/qqmllanguage/data/aliasWriter.qml b/tests/auto/qml/qqmllanguage/data/aliasWriter.qml new file mode 100644 index 0000000000..4001c2af34 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/aliasWriter.qml @@ -0,0 +1,5 @@ +import QtQml + +AliasHolder { + strokeStyle: 1 +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 7278d87c39..5b0a4346a5 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -424,6 +424,8 @@ private slots: void objectInQmlListAndGc(); void deepAliasOnICOrReadonly(); + void writeNumberToEnumAlias(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -6785,6 +6787,7 @@ void tst_qqmllanguage::bareInlineComponent() QVERIFY(tab1Found); } +#if QT_CONFIG(qml_debug) struct DummyDebugger : public QV4::Debugging::Debugger { bool pauseAtNextOpportunity() const final { return false; } @@ -6793,6 +6796,9 @@ struct DummyDebugger : public QV4::Debugging::Debugger void leavingFunction(const QV4::ReturnedValue &) final { } void aboutToThrow() final { } }; +#else +using DummyDebugger = QV4::Debugging::Debugger; // it's already dummy +#endif void tst_qqmllanguage::hangOnWarning() { @@ -8139,6 +8145,17 @@ void tst_qqmllanguage::deepAliasOnICOrReadonly() "Invalid property assignment: \"readonlyRectX\" is a read-only property"))); } +void tst_qqmllanguage::writeNumberToEnumAlias() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("aliasWriter.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("strokeStyle").toInt(), 1); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp index 1f005ae5ca..67d15e5043 100644 --- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp +++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp @@ -457,9 +457,10 @@ void tst_qqmllocale::toString_data() const QDateTime midnight2000(QDate(2000, 1, 1), QTime(0, 0)); // 12 AM might not exist in this timezone (some timezones have transitions at midnight). if (midnight2000.isValid()) { - functionCallScript = "locale.toString(new Date(2000, 1, 1))"; - QTest::newRow(qPrintable(functionCallScript)) << "en_AU" << functionCallScript - << QLatin1String("Tuesday, 1 February 2000 12:00:00 AM ") + midnight2000.timeZoneAbbreviation() << QString(); + functionCallScript = "locale.toString(new Date(2000, 0, 1))"; + const QLocale locale("en_AU"); + QTest::newRow(qPrintable(functionCallScript)) << locale.name() << functionCallScript + << locale.toString(midnight2000, QLocale::LongFormat) << QString(); } functionCallScript = "locale.toString(new Date(2022, 7, 16), [])"; diff --git a/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml b/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml new file mode 100644 index 0000000000..0ced54a9ca --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/propertyStartsWithOn.qml @@ -0,0 +1,9 @@ +import QtQml + +QtObject { + id: root + property int onlineStatus + property Binding b: Binding { + root.onlineStatus: 12 + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 3b5d48df1b..235de5f406 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -220,6 +220,8 @@ private slots: void invalidateQPropertyChangeTriggers(); + void propertyStartsWithOn(); + private: QQmlEngine engine; }; @@ -2574,6 +2576,18 @@ void tst_qqmlproperty::invalidateQPropertyChangeTriggers() })); } +void tst_qqmlproperty::propertyStartsWithOn() +{ + QTest::failOnWarning("\"onlineStatus\" is not a properly capitalized signal handler name. " + "\"onLineStatus\" would be correct."); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("propertyStartsWithOn.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); + QCOMPARE(root->property("onlineStatus").toInt(), 12); +} + QTEST_MAIN(tst_qqmlproperty) #include "tst_qqmlproperty.moc" diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml index f6bb84bc6b..6a24f907d1 100644 --- a/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml +++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml @@ -41,7 +41,7 @@ Window { function f(v = 4) { let c = 0; return { - "a": function () { + a: function () { if (b == 0) c += 78 * 5 * v; }() diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml index 7ad4ca51c6..2e7483f453 100644 --- a/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml +++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml @@ -14,8 +14,7 @@ Window { Rectangle { anchors.fill: parent - Behavior on opacity { - } + Behavior on opacity {} ListView { width: parent.width @@ -35,7 +34,7 @@ Window { function f(v = 4) { let c = 0; return { - "a": function () { + a: function () { if (b == 0) c += 78 * 5 * v; }() diff --git a/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml index 48a961930d..3ed3aa208c 100644 --- a/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml +++ b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml @@ -52,7 +52,7 @@ Window { function f(v) { let c = 0; return { - "a": function () { + a: function () { if (b == 0) c += 78 * 5 * v; }() diff --git a/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml index cb84168307..7473283605 100644 --- a/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml +++ b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml @@ -8,7 +8,7 @@ Item { function foo() { theItem.foo("The issue is exacerbated if the object literal is wrapped onto the next line like so:", { - "foo": theFoo - }); + "foo": theFoo + }); } } diff --git a/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml index b01eca88d3..fde8ffd686 100644 --- a/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml +++ b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml @@ -4,7 +4,7 @@ Item { function foo() { iterableObj = [1, 2]; obj = { - "a": 42 + a: 42 }; let x = (console.log("bla\n"), 3); myFunction(...iterableObj); // pass all elements of iterableObj as arguments to function myFunction diff --git a/tests/auto/qmltest/selftests/tst_signalspy.qml b/tests/auto/qmltest/selftests/tst_signalspy.qml new file mode 100644 index 0000000000..dbc607e02a --- /dev/null +++ b/tests/auto/qmltest/selftests/tst_signalspy.qml @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest + +TestCase { + id: testCase + name: "SignalSpy" + + Component { + id: itemWithAmbigiousSignalNameOnChild + Item { + property Item child: Item { + property bool opened: true + signal opened() + } + } + } + + Component { + id: signalSpy + SignalSpy {} + } + + function init() { + failOnWarning(/.?/) + } + + function test_ambigiousSignalName() { + let control = createTemporaryObject(itemWithAmbigiousSignalNameOnChild, testCase) + verify(control) + + let openedSpy = signalSpy.createObject(control, {target: control.child, signalName: "opened"}) + verify(openedSpy) + compare(openedSpy.target, control.child) + compare(openedSpy.signalName, "opened") + verify(openedSpy.valid) + + // reset the target, i.e. disconnect the spy + openedSpy.target = null + compare(openedSpy.target, null) + compare(openedSpy.signalName, "opened") + verify(!openedSpy.valid) + + // connect again to check that it will be disconnected + // on SignalSpy destruction with no issues/warnings/etc. + openedSpy.target = control.child + compare(openedSpy.target, control.child) + compare(openedSpy.signalName, "opened") + verify(openedSpy.valid) + } +} diff --git a/tests/auto/quick/CMakeLists.txt b/tests/auto/quick/CMakeLists.txt index 15ae6c6a9f..070613fcd1 100644 --- a/tests/auto/quick/CMakeLists.txt +++ b/tests/auto/quick/CMakeLists.txt @@ -79,7 +79,9 @@ if(QT_FEATURE_private_tests) add_subdirectory(qquickview) add_subdirectory(qquickview_extra) add_subdirectory(qquickcanvasitem) - add_subdirectory(qquickdesignersupport) + if(QT_FEATURE_quick_designer) + add_subdirectory(qquickdesignersupport) + endif() add_subdirectory(qquickscreen) add_subdirectory(touchmouse) add_subdirectory(scenegraph) diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml new file mode 100644 index 0000000000..60dfc53c40 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml @@ -0,0 +1,17 @@ +import QtQuick + +Item { + width: 320 + height: 240 + + Rectangle { + width: 100 + height: 100 + anchors.centerIn: parent + color: hh.hovered ? "lightsteelblue" : "beige" + + HoverHandler { + id: hh + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp index 85f1cbda57..ace99daa6a 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp @@ -49,9 +49,12 @@ private slots: void addHandlerFromCpp(); void ensureHoverHandlerWorksWhenItemHasHoverDisabled(); void changeCursor(); + void touchDrag(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); + + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); }; void tst_HoverHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) @@ -700,6 +703,46 @@ void tst_HoverHandler::changeCursor() #endif } +void tst_HoverHandler::touchDrag() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("hoverHandler.qml"))); + const QQuickItem *root = window.rootObject(); + QQuickHoverHandler *handler = root->findChild<QQuickHoverHandler *>(); + QVERIFY(handler); + + // polishAndSync() calls flushFrameSynchronousEvents() before emitting afterAnimating() + QSignalSpy frameSyncSpy(&window, &QQuickWindow::afterAnimating); + + const QPoint out(root->width() - 1, root->height() / 2); + QPoint in(root->width() / 2, root->height() / 2); + + QTest::touchEvent(&window, touchscreen.get()).press(0, out, &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(handler->isHovered(), false); + + frameSyncSpy.clear(); + QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(handler->isHovered(), true); + QCOMPARE(handler->point().scenePosition(), in); + + in += {10, 10}; + QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window); + QQuickTouchUtils::flush(&window); + // ensure that the color change is visible + QTRY_COMPARE_GE(frameSyncSpy.size(), 1); + QCOMPARE(handler->isHovered(), true); + QCOMPARE(handler->point().scenePosition(), in); + + QTest::touchEvent(&window, touchscreen.get()).move(0, out, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE_GE(frameSyncSpy.size(), 2); + QCOMPARE(handler->isHovered(), false); + + QTest::touchEvent(&window, touchscreen.get()).release(0, out, &window); +} + QTEST_MAIN(tst_HoverHandler) #include "tst_qquickhoverhandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml new file mode 100644 index 0000000000..7732c42082 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml @@ -0,0 +1,39 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 + +Item { + width: 360 + height: 280 + + Rectangle { + width: 200; height: 200; x: 100; y: 10 + color: th1.pressed ? "blue" : "lightblue" + + TapHandler { + id: th1 + objectName: "th1" + } + + Rectangle { + width: 200; height: 200; x: 50; y: 50 + color: th2.pressed ? "steelblue" : "lightsteelblue" + + TapHandler { + id: th2 + objectName: "th2" + } + } + } + + Rectangle { + width: 200; height: 200; x: 10; y: 50 + color: th3.pressed ? "goldenrod" : "beige" + + TapHandler { + id: th3 + objectName: "th3" + } + } +} + diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp index 52198ba16d..df24af7f4f 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -57,6 +57,8 @@ private slots: void nonTopLevelParentWindow(); void nestedDoubleTap_data(); void nestedDoubleTap(); + void nestedAndSiblingPropagation_data(); + void nestedAndSiblingPropagation(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName, @@ -1232,6 +1234,61 @@ void tst_TapHandler::nestedDoubleTap() // QTBUG-102625 childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 4 : 2); } +void tst_TapHandler::nestedAndSiblingPropagation_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + QTest::addColumn<QQuickTapHandler::GesturePolicy>("gesturePolicy"); + QTest::addColumn<bool>("expectPropagation"); + + const QPointingDevice *constTouchDevice = touchDevice; + + QTest::newRow("primary, DragThreshold") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::DragThreshold << true; + QTest::newRow("primary, WithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::WithinBounds << false; + QTest::newRow("primary, ReleaseWithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false; + QTest::newRow("primary, DragWithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::DragWithinBounds << false; + + QTest::newRow("touch, DragThreshold") << constTouchDevice + << QQuickTapHandler::GesturePolicy::DragThreshold << true; + QTest::newRow("touch, WithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::WithinBounds << false; + QTest::newRow("touch, ReleaseWithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false; + QTest::newRow("touch, DragWithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::DragWithinBounds << false; +} + +void tst_TapHandler::nestedAndSiblingPropagation() // QTBUG-117387 +{ + QFETCH(const QPointingDevice *, device); + QFETCH(QQuickTapHandler::GesturePolicy, gesturePolicy); + QFETCH(bool, expectPropagation); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("nestedAndSibling.qml"))); + QQuickItem *root = window.rootObject(); + QQuickTapHandler *th1 = root->findChild<QQuickTapHandler*>("th1"); + QVERIFY(th1); + th1->setGesturePolicy(gesturePolicy); + QQuickTapHandler *th2 = root->findChild<QQuickTapHandler*>("th2"); + QVERIFY(th2); + th2->setGesturePolicy(gesturePolicy); + QQuickTapHandler *th3 = root->findChild<QQuickTapHandler*>("th3"); + QVERIFY(th3); + th3->setGesturePolicy(gesturePolicy); + + QPoint middle(180, 140); + QQuickTest::pointerPress(device, &window, 0, middle); + QVERIFY(th3->isPressed()); // it's on top + QCOMPARE(th2->isPressed(), expectPropagation); + QCOMPARE(th1->isPressed(), expectPropagation); + + QQuickTest::pointerRelease(device, &window, 0, middle); +} + QTEST_MAIN(tst_TapHandler) #include "tst_qquicktaphandler.moc" diff --git a/tests/auto/quick/qquickanimatedimage/BLACKLIST b/tests/auto/quick/qquickanimatedimage/BLACKLIST index c4778ba8e5..6805f9dd5e 100644 --- a/tests/auto/quick/qquickanimatedimage/BLACKLIST +++ b/tests/auto/quick/qquickanimatedimage/BLACKLIST @@ -3,3 +3,5 @@ android [mirror_notRunning] android +[currentFrame] +macos ci # QTBUG-122111 diff --git a/tests/auto/quick/qquickanimatedimage/data/currentframe.qml b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml index b679da2a99..3c58bdaa4b 100644 --- a/tests/auto/quick/qquickanimatedimage/data/currentframe.qml +++ b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml @@ -4,6 +4,7 @@ AnimatedImage { property int currentFrameChangeCount: 0 property int frameChangeCount: 0 source: "stickman.gif" + paused: true onCurrentFrameChanged: if (currentFrame > 0) ++currentFrameChangeCount; onFrameChanged: if (currentFrame > 0) ++frameChangeCount; function scriptedSetCurrentFrame(frame) { diff --git a/tests/auto/quick/qquickpathview/data/mousePressAfterFlick.qml b/tests/auto/quick/qquickpathview/data/mousePressAfterFlick.qml new file mode 100644 index 0000000000..a30234127b --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/mousePressAfterFlick.qml @@ -0,0 +1,61 @@ +import QtQuick + +Item { + id: root + width: 360 + height: 900 + + MouseArea { + id: rootMouseArea + objectName: "rootMouseArea" + anchors.fill: parent + } + + PathView { + id: pathView + objectName: "pathView" + anchors.centerIn: parent + width: 150 + height: parent.height + dragMargin: Number.POSITIVE_INFINITY + + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + pathItemCount: 9 + + Item { + anchors.fill: parent + } + + model: 10 + + delegate: Rectangle { + id: delegate + width: parent.width + height: 120 + color: "red" + Text { + anchors.fill: parent + horizontalAlignment: Qt.AlignHCenter + text: "Path element " + modelData + } + } + + path: Path { + id: path + property int width: pathView.width + startX: path.width / 2 + startY: 0 + + PathCurve { + x: path.width / 2 + relativeY: 32 + } + PathCurve { + x: path.width / 2 + relativeY: pathView.height - 32 + } + } + } +} + diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 98085002ea..90c3060235 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -13,6 +13,7 @@ #include <QtQuick/private/qquickpath_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQuick/private/qquickrectangle_p.h> +#include <QtQuick/private/qquickmousearea_p.h> #include <QtQuickTest/QtQuickTest> #include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQml/private/qqmlvaluetype_p.h> @@ -20,6 +21,7 @@ #include <QStringListModel> #include <QFile> #include <QEvent> +#include <QSignalSpy> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/viewtestutils_p.h> @@ -130,6 +132,7 @@ private slots: void requiredPropertiesInDelegate(); void requiredPropertiesInDelegatePreventUnrelated(); void touchMove(); + void mousePressAfterFlick(); private: QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); @@ -2851,6 +2854,47 @@ void tst_QQuickPathView::touchMove() } +void tst_QQuickPathView::mousePressAfterFlick() // QTBUG-115121 +{ + QScopedPointer<QQuickView> window(createView()); + QQuickVisualTestUtils::moveMouseAway(window.data()); + window->setSource(testFileUrl("mousePressAfterFlick.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickPathView *pathview = window->rootObject()->findChild<QQuickPathView *>("pathView"); + QVERIFY(pathview != nullptr); + + QQuickMouseArea *mouseArea = window->rootObject()->findChild<QQuickMouseArea *>("rootMouseArea"); + QVERIFY(mouseArea != nullptr); + + QSignalSpy flickingSpy(pathview, SIGNAL(flickingChanged())); + QSignalSpy flickStartedSpy(pathview, SIGNAL(flickStarted())); + QSignalSpy flickEndedSpy(pathview, SIGNAL(flickEnded())); + + QSignalSpy pressedSpy(mouseArea, qOverload<QQuickMouseEvent *>(&QQuickMouseArea::pressed)); + + flickingSpy.clear(); + flickStartedSpy.clear(); + flickEndedSpy.clear(); + + // Dragging the child mouse area should animate the PathView (MA has no drag target) + QPoint from = QPoint((window->width() / 2), (window->height() * 3 / 4)); + QPoint to = QPoint((window->width() / 2), (window->height() / 4)); + flick(window.data(), from, to, 100); + QVERIFY(pathview->isMoving()); + QCOMPARE(flickingSpy.size(), 1); + QCOMPARE(flickStartedSpy.size(), 1); + QCOMPARE(flickEndedSpy.size(), 0); + + // Now while it's still moving, click it. + // The PathView should stop at a position + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(window->width() / 2, window->height() / 4)); + QTRY_VERIFY(!pathview->isMoving()); + QCOMPARE(flickEndedSpy.size(), 1); + QCOMPARE(pressedSpy.size(), 0); +} + QTEST_MAIN(tst_QQuickPathView) #include "tst_qquickpathview.moc" diff --git a/tests/auto/quick/qquickstates/data/broken.qml b/tests/auto/quick/qquickstates/data/broken.qml new file mode 100644 index 0000000000..8813c8bb39 --- /dev/null +++ b/tests/auto/quick/qquickstates/data/broken.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + states: ["test"] +} diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp index 8d010595b5..be1361e4ab 100644 --- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp @@ -191,6 +191,7 @@ private slots: void rewindAnchorChange(); void rewindAnchorChangeSize(); void bindingProperlyRemovedWithTransition(); + void doNotCrashOnBroken(); }; void tst_qquickstates::initTestCase() @@ -2036,6 +2037,21 @@ void tst_qquickstates::bindingProperlyRemovedWithTransition() QTRY_COMPARE(item->width(), 100); } +void tst_qquickstates::doNotCrashOnBroken() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("broken.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> root(c.create()); + QVERIFY(root); + QQuickItem *item = qobject_cast<QQuickItem *>(root.get()); + QVERIFY(item); + + QQmlListReference states(item, "states"); + QCOMPARE(states.size(), 1); + QCOMPARE(states.at(0), nullptr); +} + QTEST_MAIN(tst_qquickstates) #include "tst_qquickstates.moc" diff --git a/tests/auto/quick/qquicktextedit/BLACKLIST b/tests/auto/quick/qquicktextedit/BLACKLIST index 270a4f7869..d1097331a8 100644 --- a/tests/auto/quick/qquicktextedit/BLACKLIST +++ b/tests/auto/quick/qquicktextedit/BLACKLIST @@ -1,10 +1,3 @@ -# QTBUG-109488 temporary fix -[largeTextObservesViewport] -linux -windows -android -darwin - [mouseSelection] opensuse-leap diff --git a/tests/auto/quick/qquicktextedit/data/resizeTextEditPolish.qml b/tests/auto/quick/qquicktextedit/data/resizeTextEditPolish.qml new file mode 100644 index 0000000000..44819a0de9 --- /dev/null +++ b/tests/auto/quick/qquicktextedit/data/resizeTextEditPolish.qml @@ -0,0 +1,42 @@ +import QtQuick +import QtQuick.Controls + +Item { + width: 400 + height: 150 + + TextEdit { + id: textEdit + anchors { + top: parent.top + left: parent.left + right: parent.right + } + readOnly: true + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + text: "Lorem ipsum dolor sit amet, consectetur adipisicing" + states: [ + State { + name: "multi-line" + when: textEdit.lineCount > 1 + AnchorChanges { + target: textEdit + anchors.bottom: undefined + } + PropertyChanges { + target: textEdit + height: undefined + } + }, + State { + name: "single-line" + when: true + AnchorChanges { + target: textEdit + anchors.bottom: { return textEdit.parent.bottom } + } + } + ] + } +} diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 9819494d34..99f95cc5a6 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -6,6 +6,7 @@ #include <math.h> #include <QFile> #include <QtQuick/QQuickTextDocument> +#include <QtQuickTest/QtQuickTest> #include <QTextDocument> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcontext.h> @@ -211,6 +212,7 @@ private slots: void rtlAlignmentInColumnLayout_QTBUG_112858(); + void resizeTextEditPolish(); private: void simulateKeys(QWindow *window, const QList<Key> &keys); #if QT_CONFIG(shortcut) @@ -3766,16 +3768,16 @@ void tst_qquicktextedit::largeTextObservesViewport_data() // by default, the root item acts as the viewport: // QQuickTextEdit doesn't populate lines of text beyond the bottom of the window // cursor position 1000 is on line 121 - QTest::newRow("default plain text") << text << QQuickTextEdit::PlainText << false << 1000 << 0 - << 1 << 118 << 142 << 2400 << 3000; + QTest::newRow("default plain text") << text << QQuickTextEdit::PlainText << false << 1000 << 2 + << 3 << 118 << 144 << 2150 << 3000; // make the rectangle into a viewport item, and move the text upwards: // QQuickTextEdit doesn't populate lines of text beyond the bottom of the viewport rectangle QTest::newRow("clipped plain text") << text << QQuickTextEdit::PlainText << true << 1000 << 0 - << 1 << 123 << 137 << 2550 << 3000; + << 3 << 123 << 137 << 2200 << 3000; // scroll backwards QTest::newRow("scroll backwards in plain text") << text << QQuickTextEdit::PlainText << true << 1000 << 600 - << 1 << 93 << 108 << 1475 << 2300; + << 3 << 91 << 108 << 1475 << 2300; { QStringList lines; @@ -3799,20 +3801,20 @@ void tst_qquicktextedit::largeTextObservesViewport_data() // by default, the root item acts as the viewport: // QQuickTextEdit doesn't populate blocks beyond the bottom of the window QTest::newRow("default styled text") << text << QQuickTextEdit::RichText << false << 1000 << 0 - << 1 << 124 << 139 << 3900 << 4500; + << 3 << 122 << 139 << 3600 << 4500; // make the rectangle into a viewport item, and move the text upwards: // QQuickTextEdit doesn't populate blocks that don't intersect the viewport rectangle QTest::newRow("clipped styled text") << text << QQuickTextEdit::RichText << true << 1000 << 0 - << 1 << 127 << 136 << 4000 << 4360; + << 3 << 127 << 136 << 3700 << 4360; // get the "chapter 2" heading into the viewport QTest::newRow("heading visible") << text << QQuickTextEdit::RichText << true << 800 << 0 - << 1 << 105 << 113 << 3300 << 3600; + << 3 << 105 << 113 << 3050 << 3600; // get the "chapter 2" heading into the viewport, and then scroll backwards QTest::newRow("scroll backwards") << text << QQuickTextEdit::RichText << true << 800 << 20 - << 1 << 104 << 113 << 3200 << 3600; + << 3 << 104 << 113 << 3000 << 3600; // get the "chapter 2" heading into the viewport, and then scroll forwards QTest::newRow("scroll forwards") << text << QQuickTextEdit::RichText << true << 800 << -50 - << 1 << 106 << 115 << 3300 << 3670; + << 3 << 106 << 115 << 3000 << 3670; } void tst_qquicktextedit::largeTextObservesViewport() @@ -6591,6 +6593,35 @@ void tst_qquicktextedit::rtlAlignmentInColumnLayout_QTBUG_112858() } } +void tst_qquicktextedit::resizeTextEditPolish() +{ + QQuickView window(testFileUrl("resizeTextEditPolish.qml")); + QVERIFY(window.rootObject() != nullptr); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + auto *edit = window.rootObject()->findChild<QQuickTextEdit *>(); + QVERIFY(edit != nullptr); + QCOMPARE(edit->lineCount(), 1); + + QSignalSpy spy(edit, SIGNAL(lineCountChanged())); + + // Resize item and check for item polished + auto *item = edit->parentItem(); + item->setWidth(item->width() - (item->width() / 2)); + + QVERIFY(QQuickTest::qIsPolishScheduled(edit)); + QVERIFY(QQuickTest::qWaitForPolish(edit)); + + QTRY_COMPARE(spy.size(), 1); + QVERIFY(edit->lineCount() > 1); + QCOMPARE(edit->state(), QString("multi-line")); + auto *editPriv = QQuickTextEditPrivate::get(edit); + QCOMPARE(editPriv->xoff, 0); + QCOMPARE(editPriv->yoff, 0); +} + QTEST_MAIN(tst_qquicktextedit) #include "tst_qquicktextedit.moc" diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 1aa3a46dd1..9e5ba116ad 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -3298,57 +3298,48 @@ public: */ bool testFilterPreConditions() const { return !m_filterNotPreAccepted; } static QVector<DeliveryRecord> &deliveryList() { return m_deliveryList; } - static QSet<QEvent::Type> &includedEventTypes() + static void setExpectedDeliveryList(const QVector<DeliveryRecord> &v) { m_expectedDeliveryList = v; } + + bool isRelevant(QQuickItem *receiver, QEvent *e) { - if (m_includedEventTypes.isEmpty()) - m_includedEventTypes << QEvent::MouseButtonPress; - return m_includedEventTypes; + if (receiver->acceptTouchEvents()) + return e->type() == QEvent::TouchBegin; + return e->type() == QEvent::MouseButtonPress; } - static void setExpectedDeliveryList(const QVector<DeliveryRecord> &v) { m_expectedDeliveryList = v; } protected: bool childMouseEventFilter(QQuickItem *i, QEvent *e) override { + if (!isRelevant(i, e)) + return QQuickRectangle::childMouseEventFilter(i, e); + appendEvent(this, i, e); - switch (e->type()) { - case QEvent::MouseButtonPress: - if (!e->isAccepted()) - m_filterNotPreAccepted = true; - e->setAccepted(m_filterAccepts); - // qCDebug(lcTests) << objectName() << i->objectName(); - return m_filterReturns; - default: - break; - } - return QQuickRectangle::childMouseEventFilter(i, e); + if (!e->isAccepted()) + m_filterNotPreAccepted = true; + e->setAccepted(m_filterAccepts); + return m_filterReturns; } bool event(QEvent *e) override { + if (!isRelevant(this, e)) + QQuickRectangle::event(e); + appendEvent(nullptr, this, e); - switch (e->type()) { - case QEvent::MouseButtonPress: - // qCDebug(lcTests) << objectName(); - e->setAccepted(m_eventAccepts); - return true; - default: - break; - } - return QQuickRectangle::event(e); + e->setAccepted(m_eventAccepts); + return true; } private: static void appendEvent(QQuickItem *filter, QQuickItem *receiver, QEvent *event) { - if (includedEventTypes().contains(event->type())) { - auto record = DeliveryRecord(filter ? filter->objectName() : QString(), receiver ? receiver->objectName() : QString()); - int i = m_deliveryList.size(); - if (m_expectedDeliveryList.size() > i && m_expectedDeliveryList[i] == record) - qCDebug(lcTests).noquote().nospace() << i << ": " << record; - else - qCDebug(lcTests).noquote().nospace() << i << ": " << record - << ", expected " << (m_expectedDeliveryList.size() > i ? m_expectedDeliveryList[i].toString() : QLatin1String("nothing")) << " <---"; - m_deliveryList << record; - } + auto record = DeliveryRecord(filter ? filter->objectName() : QString(), receiver ? receiver->objectName() : QString()); + int i = m_deliveryList.size(); + if (m_expectedDeliveryList.size() > i && m_expectedDeliveryList[i] == record) + qCDebug(lcTests).noquote().nospace() << i << ": " << record; + else + qCDebug(lcTests).noquote().nospace() << i << ": " << record + << ", expected " << (m_expectedDeliveryList.size() > i ? m_expectedDeliveryList[i].toString() : QLatin1String("nothing")) << " <---"; + m_deliveryList << record; } bool m_eventAccepts; bool m_filterReturns; @@ -3358,12 +3349,10 @@ private: // list of (filtering-parent . receiver) pairs static DeliveryRecordVector m_expectedDeliveryList; static DeliveryRecordVector m_deliveryList; - static QSet<QEvent::Type> m_includedEventTypes; }; DeliveryRecordVector EventItem::m_expectedDeliveryList; DeliveryRecordVector EventItem::m_deliveryList; -QSet<QEvent::Type> EventItem::m_includedEventTypes; typedef QVector<const char*> CharStarVector; @@ -3388,11 +3377,17 @@ void tst_qquickwindow::testChildMouseEventFilter_data() // r0->r1->r2->r3 // QTest::addColumn<QPoint>("mousePos"); + QTest::addColumn<QString>("eventMode"); QTest::addColumn<InputState>("inputState"); QTest::addColumn<DeliveryRecordVector>("expectedDeliveryOrder"); - QTest::newRow("if filtered and rejected, do not deliver it to the item that filtered it") + for (const QString &eventMode : {"mouse", "touch", "touchToMouse"}) { + + #define desc(txt) qPrintable(QString("%1 events, ").arg(eventMode) + txt) + + QTest::newRow(desc("if filtered and rejected, do not deliver it to the item that filtered it")) << QPoint(100, 100) + << eventMode << InputState({ // | event() | child mouse filter // +---------+---------+---------+--------- @@ -3410,8 +3405,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data() << DeliveryRecord("r1") ); - QTest::newRow("no filtering, no accepting") + QTest::newRow(desc("no filtering, no accepting")) << QPoint(100, 100) + << eventMode << InputState({ // | event() | child mouse filter // +---------+---------+---------+--------- @@ -3430,8 +3426,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data() << DeliveryRecord("root") ); - QTest::newRow("all filtering, no accepting") + QTest::newRow(desc("all filtering, no accepting")) << QPoint(100, 100) + << eventMode << InputState({ // | event() | child mouse filter // +---------+---------+---------+--------- @@ -3457,8 +3454,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data() ); - QTest::newRow("some filtering, no accepting") + QTest::newRow(desc("some filtering, no accepting")) << QPoint(100, 100) + << eventMode << InputState({ // | event() | child mouse filter // +---------+---------+---------+--------- @@ -3482,8 +3480,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data() << DeliveryRecord("root") ); - QTest::newRow("r1 accepts") + QTest::newRow(desc("r1 accepts")) << QPoint(100, 100) + << eventMode << InputState({ // | event() | child mouse filter // +---------+---------+---------+--------- @@ -3505,8 +3504,9 @@ void tst_qquickwindow::testChildMouseEventFilter_data() << DeliveryRecord("r1") ); - QTest::newRow("r1 rejects and filters") + QTest::newRow(desc("r1 rejects and filters")) << QPoint(100, 100) + << eventMode << InputState({ // | event() | child mouse filter // +---------+---------+---------+--------- @@ -3529,12 +3529,13 @@ void tst_qquickwindow::testChildMouseEventFilter_data() << DeliveryRecord("r0") << DeliveryRecord("root") ); - + } } void tst_qquickwindow::testChildMouseEventFilter() { QFETCH(QPoint, mousePos); + QFETCH(QString, eventMode); QFETCH(InputState, inputState); QFETCH(DeliveryRecordVector, expectedDeliveryOrder); @@ -3549,21 +3550,28 @@ void tst_qquickwindow::testChildMouseEventFilter() QScopedPointer<EventFilter> rootFilter(new EventFilter); root->installEventFilter(rootFilter.data()); + const bool useMouseEvents = eventMode == "mouse"; + const bool acceptTouchEvents = eventMode == "touch"; + // Create 4 items; each item a child of the previous item. EventItem *r[4]; r[0] = new EventItem(root); r[0]->setColor(QColor(0x404040)); r[0]->setWidth(200); r[0]->setHeight(200); + r[0]->setAcceptTouchEvents(acceptTouchEvents); r[1] = new EventItem(r[0]); r[1]->setColor(QColor(0x606060)); + r[1]->setAcceptTouchEvents(acceptTouchEvents); r[2] = new EventItem(r[1]); r[2]->setColor(Qt::red); + r[2]->setAcceptTouchEvents(acceptTouchEvents); r[3] = new EventItem(r[2]); r[3]->setColor(Qt::green); + r[3]->setAcceptTouchEvents(acceptTouchEvents); for (uint i = 0; i < sizeof(r)/sizeof(EventItem*); ++i) { r[i]->setEventAccepts(inputState.r[i].eventAccepts); @@ -3580,7 +3588,11 @@ void tst_qquickwindow::testChildMouseEventFilter() DeliveryRecordVector &actualDeliveryOrder = EventItem::deliveryList(); actualDeliveryOrder.clear(); - QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, mousePos); + + if (useMouseEvents) + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, mousePos); + else + QTest::touchEvent(&window, touchDevice).press(0, mousePos, &window); // Check if event got delivered to the root item. If so, append it to the list of items the event got delivered to if (rootFilter->events.contains(QEvent::MouseButtonPress)) @@ -3596,8 +3608,11 @@ void tst_qquickwindow::testChildMouseEventFilter() QVERIFY(item->testFilterPreConditions()); } - // "restore" mouse state - QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, mousePos); + // "restore" mouse/touch state + if (useMouseEvents) + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, mousePos); + else + QTest::touchEvent(&window, touchDevice).release(0, mousePos, &window); } void tst_qquickwindow::cleanupGrabsOnRelease() diff --git a/tests/auto/quickcontrols/CMakeLists.txt b/tests/auto/quickcontrols/CMakeLists.txt index c45d6f38e3..f4f2e6b2c6 100644 --- a/tests/auto/quickcontrols/CMakeLists.txt +++ b/tests/auto/quickcontrols/CMakeLists.txt @@ -10,7 +10,9 @@ endif() add_subdirectory(cursor) add_subdirectory(customization) add_subdirectory(deferred) -add_subdirectory(designer) +if(QT_FEATURE_quick_designer) + add_subdirectory(designer) +endif() if(NOT ANDROID) # QTBUG-100258 add_subdirectory(focus) endif() diff --git a/tests/auto/quickcontrols/controls/data/tst_action.qml b/tests/auto/quickcontrols/controls/data/tst_action.qml index 40aa4810f2..6ed17a0d24 100644 --- a/tests/auto/quickcontrols/controls/data/tst_action.qml +++ b/tests/auto/quickcontrols/controls/data/tst_action.qml @@ -24,18 +24,20 @@ TestCase { SignalSpy { } } - function test_empty() { + function init() { failOnWarning(/.?/) + } + function test_empty() { let control = createTemporaryObject(component, testCase) verify(control) } function test_enabled() { - var action = createTemporaryObject(component, testCase) + let action = createTemporaryObject(component, testCase) verify(action) - var spy = createTemporaryObject(signalSpy, testCase, {target: action, signalName: "triggered"}) + let spy = createTemporaryObject(signalSpy, testCase, {target: action, signalName: "triggered"}) verify(spy.valid) action.trigger() @@ -89,7 +91,7 @@ TestCase { } function test_shared() { - var container = createTemporaryObject(buttonAndMenu, testCase) + let container = createTemporaryObject(buttonAndMenu, testCase) verify(container) keyClick(Qt.Key_B, Qt.ControlModifier) @@ -126,10 +128,10 @@ TestCase { } function test_repeater() { - var container = createTemporaryObject(actionAndRepeater, testCase) + let container = createTemporaryObject(actionAndRepeater, testCase) verify(container) - var spy = signalSpy.createObject(container, {target: container.action, signalName: "triggered"}) + let spy = signalSpy.createObject(container, {target: container.action, signalName: "triggered"}) verify(spy.valid) keyClick(Qt.Key_A, Qt.ControlModifier) @@ -160,7 +162,7 @@ TestCase { } function test_shortcutBinding() { - var container = createTemporaryObject(shortcutBinding, testCase); + let container = createTemporaryObject(shortcutBinding, testCase); verify(container) compare(container.indirect.nativeText, container.direct.nativeText); } @@ -188,7 +190,7 @@ TestCase { function test_shortcutCleanup() { { - var container = createTemporaryObject(shortcutCleanup, testCase); + let container = createTemporaryObject(shortcutCleanup, testCase); verify(container) container.action.shortcut = "Delete" container.menu.open() diff --git a/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml b/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml index 7385677f93..69b1cc1d11 100644 --- a/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml +++ b/tests/auto/quickcontrols/controls/data/tst_busyindicator.qml @@ -22,7 +22,14 @@ TestCase { id: mouseArea MouseArea { } } - + + Component { + id: busyIndicatorInItem + Item { + BusyIndicator { } + } + } + function init() { failOnWarning(/.?/) } @@ -63,4 +70,16 @@ TestCase { touch.release(0, control).commit() verify(!ma.pressed) } + + // QTBUG-108808 + function test_visibility() { + let control = createTemporaryObject(busyIndicatorInItem, testCase, {visible: false}) + verify(control) + + let invisibleImage = grabImage(control) + control.visible = true + let visibleImage = grabImage(control) + + verify(!invisibleImage.equals(visibleImage)) + } } diff --git a/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml b/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml index a2f843678f..5ccc10af08 100644 --- a/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml +++ b/tests/auto/quickcontrols/controls/data/tst_calendarmodel.qml @@ -29,6 +29,10 @@ TestCase { } } + function init() { + failOnWarning(/.?/) + } + function test_indices_data() { return [ // "from" and "to" must currently be in the same year. @@ -63,7 +67,7 @@ TestCase { } function test_invalid() { - var model = calendarModel.createObject(testCase) + let model = calendarModel.createObject(testCase) verify(model) compare(model.indexOf(-1, -1), -1) @@ -73,11 +77,11 @@ TestCase { } function test_instantiator() { - var inst = instantiator.createObject(testCase) + let inst = instantiator.createObject(testCase) verify(inst) compare(inst.count, 12) - for (var m = 0; m < inst.count; ++m) { + for (let m = 0; m < inst.count; ++m) { compare(inst.objectAt(m).month, m) compare(inst.objectAt(m).year, 2016) } diff --git a/tests/auto/quickcontrols/controls/data/tst_checkbox.qml b/tests/auto/quickcontrols/controls/data/tst_checkbox.qml index 5991ae9008..7d80484123 100644 --- a/tests/auto/quickcontrols/controls/data/tst_checkbox.qml +++ b/tests/auto/quickcontrols/controls/data/tst_checkbox.qml @@ -25,9 +25,11 @@ TestCase { } } - function test_defaults() { + function init() { failOnWarning(/.?/) + } + function test_defaults() { let control = createTemporaryObject(checkBox, testCase) verify(control) compare(control.tristate, false) @@ -35,7 +37,7 @@ TestCase { } function test_text() { - var control = createTemporaryObject(checkBox, testCase) + let control = createTemporaryObject(checkBox, testCase) verify(control) compare(control.text, "") @@ -46,10 +48,10 @@ TestCase { } function test_checked() { - var control = createTemporaryObject(checkBox, testCase) + let control = createTemporaryObject(checkBox, testCase) verify(control) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) sequenceSpy.expectedSequence = [] compare(control.checked, false) @@ -72,10 +74,10 @@ TestCase { } function test_checkState() { - var control = createTemporaryObject(checkBox, testCase) + let control = createTemporaryObject(checkBox, testCase) verify(control) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) sequenceSpy.expectedSequence = [] compare(control.checked, false) @@ -98,10 +100,10 @@ TestCase { } function test_mouse() { - var control = createTemporaryObject(checkBox, testCase) + let control = createTemporaryObject(checkBox, testCase) verify(control) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) // check sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }], @@ -169,12 +171,12 @@ TestCase { } function test_touch() { - var control = createTemporaryObject(checkBox, testCase) + let control = createTemporaryObject(checkBox, testCase) verify(control) - var touch = touchEvent(control) + let touch = touchEvent(control) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) // check sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }], @@ -235,10 +237,10 @@ TestCase { } function test_keys() { - var control = createTemporaryObject(checkBox, testCase) + let control = createTemporaryObject(checkBox, testCase) verify(control) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) sequenceSpy.expectedSequence = [] control.forceActiveFocus() @@ -277,8 +279,8 @@ TestCase { sequenceSpy.expectedSequence = [] // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for // pressing buttons and the CI uses the QGnomeTheme platform theme. - var keys = [Qt.Key_Escape, Qt.Key_Tab] - for (var i = 0; i < keys.length; ++i) { + let keys = [Qt.Key_Escape, Qt.Key_Tab] + for (let i = 0; i < keys.length; ++i) { sequenceSpy.reset() keyClick(keys[i]) compare(control.checked, false) @@ -295,7 +297,7 @@ TestCase { } function test_checked_binding() { - var container = createTemporaryObject(checkedBoundBoxes, testCase) + let container = createTemporaryObject(checkedBoundBoxes, testCase) verify(container) compare(container.cb1.checked, false) @@ -325,7 +327,7 @@ TestCase { } function test_checkState_binding() { - var container = createTemporaryObject(checkStateBoundBoxes, testCase) + let container = createTemporaryObject(checkStateBoundBoxes, testCase) verify(container) compare(container.cb1.checked, false) @@ -361,9 +363,9 @@ TestCase { } function test_tristate() { - var control = createTemporaryObject(checkBox, testCase, {tristate: true}) + let control = createTemporaryObject(checkBox, testCase, {tristate: true}) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) sequenceSpy.expectedSequence = [] control.forceActiveFocus() @@ -457,7 +459,7 @@ TestCase { } function test_baseline() { - var control = createTemporaryObject(checkBox, testCase) + let control = createTemporaryObject(checkBox, testCase) verify(control) compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset) } @@ -484,7 +486,7 @@ TestCase { } function test_nextCheckState(data) { - var control = createTemporaryObject(nextCheckStateBox, testCase) + let control = createTemporaryObject(nextCheckStateBox, testCase) verify(control) // mouse @@ -496,7 +498,7 @@ TestCase { // touch control.checkState = data.checkState compare(control.checkState, data.checkState) - var touch = touchEvent(control) + let touch = touchEvent(control) touch.press(0, control).commit().release(0, control).commit() compare(control.checkState, data.expectedState) diff --git a/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml b/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml index d3e0910e83..69bdb83482 100644 --- a/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml +++ b/tests/auto/quickcontrols/controls/data/tst_checkdelegate.qml @@ -20,16 +20,18 @@ TestCase { // TODO: data-fy tst_checkbox (rename to tst_check?) so we don't duplicate its tests here? - function test_defaults() { + function init() { failOnWarning(/.?/) + } - var control = createTemporaryObject(checkDelegate, testCase); + function test_defaults() { + let control = createTemporaryObject(checkDelegate, testCase); verify(control); verify(!control.checked); } function test_checked() { - var control = createTemporaryObject(checkDelegate, testCase); + let control = createTemporaryObject(checkDelegate, testCase); verify(control); mouseClick(control); @@ -40,17 +42,17 @@ TestCase { } function test_baseline() { - var control = createTemporaryObject(checkDelegate, testCase); + let control = createTemporaryObject(checkDelegate, testCase); verify(control); compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset); } function test_spacing() { - var control = createTemporaryObject(checkDelegate, testCase, { text: "Some long, long, long text" }) + let control = createTemporaryObject(checkDelegate, testCase, { text: "Some long, long, long text" }) verify(control) verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth) - var textLabel = findChild(control.contentItem, "label") + let textLabel = findChild(control.contentItem, "label") verify(textLabel) // The implicitWidth of the IconLabel that all buttons use as their contentItem should be @@ -77,7 +79,7 @@ TestCase { } function test_display(data) { - var control = createTemporaryObject(checkDelegate, testCase, { + let control = createTemporaryObject(checkDelegate, testCase, { text: "CheckDelegate", display: data.display, width: 400, @@ -87,11 +89,11 @@ TestCase { verify(control) compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png") - var iconImage = findChild(control.contentItem, "image") - var textLabel = findChild(control.contentItem, "label") + let iconImage = findChild(control.contentItem, "image") + let textLabel = findChild(control.contentItem, "label") - var availableWidth = control.availableWidth - control.indicator.width - control.spacing - var indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0 + let availableWidth = control.availableWidth - control.indicator.width - control.spacing + let indicatorOffset = control.mirrored ? control.indicator.width + control.spacing : 0 switch (control.display) { case CheckDelegate.IconOnly: @@ -148,7 +150,7 @@ TestCase { } function test_nextCheckState(data) { - var control = createTemporaryObject(nextCheckStateDelegate, testCase) + let control = createTemporaryObject(nextCheckStateDelegate, testCase) verify(control) // mouse @@ -160,7 +162,7 @@ TestCase { // touch control.checkState = data.checkState compare(control.checkState, data.checkState) - var touch = touchEvent(control) + let touch = touchEvent(control) touch.press(0, control).commit().release(0, control).commit() compare(control.checkState, data.expectedState) diff --git a/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml b/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml index 67403e240a..b5a9e1bec2 100644 --- a/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml +++ b/tests/auto/quickcontrols/controls/data/tst_dayofweekrow.qml @@ -18,15 +18,17 @@ TestCase { DayOfWeekRow { } } - function test_defaults() { + function init () { failOnWarning(/.?/) + } + function test_defaults() { let control = createTemporaryObject(component, testCase) verify(control) } function test_locale() { - var control = component.createObject(testCase) + let control = component.createObject(testCase) verify(control.contentItem.children[0]) @@ -43,7 +45,7 @@ TestCase { } function test_font() { - var control = component.createObject(testCase) + let control = component.createObject(testCase) verify(control.contentItem.children[0]) diff --git a/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml b/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml index e2abe389ca..27145cd2ab 100644 --- a/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml +++ b/tests/auto/quickcontrols/controls/data/tst_delaybutton.qml @@ -38,18 +38,20 @@ TestCase { SignalSpy { } } - function test_defaults() { + function init() { failOnWarning(/.?/) + } + function test_defaults() { let control = createTemporaryObject(defaultComponent, testCase) verify(control) } function test_mouse() { - var control = createTemporaryObject(delayButton, testCase) + let control = createTemporaryObject(delayButton, testCase) verify(control) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) // click sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], @@ -145,12 +147,12 @@ TestCase { } function test_touch() { - var control = createTemporaryObject(delayButton, testCase) + let control = createTemporaryObject(delayButton, testCase) verify(control) - var touch = touchEvent(control) + let touch = touchEvent(control) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) // click sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], @@ -224,13 +226,13 @@ TestCase { } function test_keys() { - var control = createTemporaryObject(delayButton, testCase) + let control = createTemporaryObject(delayButton, testCase) verify(control) control.forceActiveFocus() verify(control.activeFocus) - var sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) + let sequenceSpy = signalSequenceSpy.createObject(control, {target: control}) // click sequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], @@ -275,8 +277,8 @@ TestCase { sequenceSpy.expectedSequence = [] // Not testing Key_Enter and Key_Return because QGnomeTheme uses them for // pressing buttons and the CI uses the QGnomeTheme platform theme. - var keys = [Qt.Key_Escape, Qt.Key_Tab] - for (var i = 0; i < keys.length; ++i) { + let keys = [Qt.Key_Escape, Qt.Key_Tab] + for (let i = 0; i < keys.length; ++i) { sequenceSpy.reset() keyClick(keys[i]) verify(sequenceSpy.success) @@ -284,10 +286,10 @@ TestCase { } function test_progress() { - var control = createTemporaryObject(delayButton, testCase) + let control = createTemporaryObject(delayButton, testCase) verify(control) - var progressSpy = signalSpy.createObject(control, {target: control, signalName: "progressChanged"}) + let progressSpy = signalSpy.createObject(control, {target: control, signalName: "progressChanged"}) verify(progressSpy.valid) compare(control.progress, 0.0) @@ -297,7 +299,7 @@ TestCase { } function test_baseline() { - var control = createTemporaryObject(delayButton, testCase) + let control = createTemporaryObject(delayButton, testCase) verify(control) compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset) } diff --git a/tests/auto/quickcontrols/controls/data/tst_dial.qml b/tests/auto/quickcontrols/controls/data/tst_dial.qml index 3fcf8c7e88..4982e5fe85 100644 --- a/tests/auto/quickcontrols/controls/data/tst_dial.qml +++ b/tests/auto/quickcontrols/controls/data/tst_dial.qml @@ -502,13 +502,13 @@ TestCase { function test_wheel_data() { return [ - { tag: "horizontal", orientation: Qt.Horizontal, dx: 120, dy: 0 }, - { tag: "vertical", orientation: Qt.Vertical, dx: 0, dy: 120 } + { tag: "horizontal", dx: 120, dy: 0 }, + { tag: "vertical", dx: 0, dy: 120 } ] } function test_wheel(data) { - var control = createTemporaryObject(dialComponent, testCase, {wheelEnabled: true, orientation: data.orientation}) + var control = createTemporaryObject(dialComponent, testCase, {wheelEnabled: true}) verify(control) compare(control.value, 0.0) diff --git a/tests/auto/quickcontrols/controls/data/tst_dialog.qml b/tests/auto/quickcontrols/controls/data/tst_dialog.qml index 180dbaf739..2c29fb366d 100644 --- a/tests/auto/quickcontrols/controls/data/tst_dialog.qml +++ b/tests/auto/quickcontrols/controls/data/tst_dialog.qml @@ -39,13 +39,12 @@ TestCase { } function init() { + failOnWarning(/.?/) tryCompare(testCase.Window.window, "active", true) } function test_defaults() { - failOnWarning(/.?/) - - var control = createTemporaryObject(dialog, testCase) + let control = createTemporaryObject(dialog, testCase) verify(control) verify(control.header) verify(control.footer) @@ -54,9 +53,9 @@ TestCase { } function test_accept() { - var control = createTemporaryObject(dialog, testCase) + let control = createTemporaryObject(dialog, testCase) - var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) verify(openedSpy.valid) control.open() @@ -64,10 +63,10 @@ TestCase { compare(openedSpy.count, 1) verify(control.visible) - var acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"}) + let acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"}) verify(acceptedSpy.valid) - var closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"}) + let closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"}) verify(closedSpy.valid) control.accept() @@ -80,9 +79,9 @@ TestCase { } function test_reject() { - var control = createTemporaryObject(dialog, testCase) + let control = createTemporaryObject(dialog, testCase) - var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) verify(openedSpy.valid) control.open() @@ -90,10 +89,10 @@ TestCase { compare(openedSpy.count, 1) verify(control.visible) - var rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"}) + let rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"}) verify(rejectedSpy.valid) - var closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"}) + let closedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "closed"}) verify(closedSpy.valid) control.reject() @@ -168,46 +167,46 @@ TestCase { } function test_buttonBox(data) { - var control = createTemporaryObject(dialog, testCase) + let control = createTemporaryObject(dialog, testCase) if (data.custom) control.footer = buttonBox.createObject(testCase) control.standardButtons = Dialog.Ok | Dialog.Cancel - var box = control.footer + let box = control.footer verify(box) compare(box.standardButtons, Dialog.Ok | Dialog.Cancel) - var acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"}) + let acceptedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "accepted"}) verify(acceptedSpy.valid) box.accepted() compare(acceptedSpy.count, 1) - var rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"}) + let rejectedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "rejected"}) verify(rejectedSpy.valid) box.rejected() compare(rejectedSpy.count, 1) } function test_qtbug71444() { - var control = createTemporaryObject(qtbug71444, testCase) + let control = createTemporaryObject(qtbug71444, testCase) verify(control) } function test_standardButtons() { - var control = createTemporaryObject(dialog, testCase) + let control = createTemporaryObject(dialog, testCase) control.standardButtons = Dialog.Ok - var box = control.footer ? control.footer : control.header + let box = control.footer ? control.footer : control.header verify(box) compare(box.count, 1) - var okButton = box.itemAt(0) + let okButton = box.itemAt(0) verify(okButton) compare(okButton.text.toUpperCase(), "OK") control.standardButtons = Dialog.Cancel compare(box.count, 1) - var cancelButton = control.footer.itemAt(0) + let cancelButton = control.footer.itemAt(0) verify(cancelButton) compare(cancelButton.text.toUpperCase(), "CANCEL") @@ -230,10 +229,10 @@ TestCase { } function test_layout() { - var control = createTemporaryObject(dialog, testCase, {width: 100, height: 100}) + let control = createTemporaryObject(dialog, testCase, {width: 100, height: 100}) verify(control) - var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) verify(openedSpy.valid) control.open() @@ -324,10 +323,10 @@ TestCase { } function test_spacing(data) { - var control = createTemporaryObject(dialog, testCase, {spacing: 20, width: 100, height: 100}) + let control = createTemporaryObject(dialog, testCase, {spacing: 20, width: 100, height: 100}) verify(control) - var openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) + let openedSpy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: "opened"}) verify(openedSpy.valid) control.open() @@ -381,14 +380,14 @@ TestCase { } function test_signals(data) { - var control = createTemporaryObject(dialog, testCase) + let control = createTemporaryObject(dialog, testCase) verify(control) control.standardButtons = data.standardButton - var button = control.standardButton(data.standardButton) + let button = control.standardButton(data.standardButton) verify(button) - var buttonSpy = signalSpy.createObject(control.contentItem, {target: control, signalName: data.signalName}) + let buttonSpy = signalSpy.createObject(control.contentItem, {target: control, signalName: data.signalName}) verify(buttonSpy.valid) button.clicked() @@ -432,7 +431,7 @@ TestCase { if (Qt.platform.pluginName === "offscreen") skip("QTBUG-89909") - var window = createTemporaryObject(qtbug85884, testCase) + let window = createTemporaryObject(qtbug85884, testCase) verify(window) tryCompare(window, "focusItemActiveFocus", true) diff --git a/tests/auto/quickcontrols/controls/data/tst_frame.qml b/tests/auto/quickcontrols/controls/data/tst_frame.qml index bef46f7650..5a6ce64bc1 100644 --- a/tests/auto/quickcontrols/controls/data/tst_frame.qml +++ b/tests/auto/quickcontrols/controls/data/tst_frame.qml @@ -52,10 +52,12 @@ TestCase { } } - function test_empty() { + function init() { failOnWarning(/.?/) + } - var control = createTemporaryObject(frame, testCase) + function test_empty() { + let control = createTemporaryObject(frame, testCase) verify(control) verify(control.contentItem) @@ -66,7 +68,7 @@ TestCase { } function test_oneChild() { - var control = createTemporaryObject(oneChildFrame, testCase) + let control = createTemporaryObject(oneChildFrame, testCase) verify(control) compare(control.contentWidth, 100) @@ -78,7 +80,7 @@ TestCase { } function test_twoChildren() { - var control = createTemporaryObject(twoChildrenFrame, testCase) + let control = createTemporaryObject(twoChildrenFrame, testCase) verify(control) compare(control.contentWidth, 0) @@ -90,7 +92,7 @@ TestCase { } function test_contentItem() { - var control = createTemporaryObject(contentFrame, testCase) + let control = createTemporaryObject(contentFrame, testCase) verify(control) compare(control.contentWidth, 100) diff --git a/tests/auto/quickcontrols/controls/data/tst_groupbox.qml b/tests/auto/quickcontrols/controls/data/tst_groupbox.qml index b5867dd956..c5da5634a2 100644 --- a/tests/auto/quickcontrols/controls/data/tst_groupbox.qml +++ b/tests/auto/quickcontrols/controls/data/tst_groupbox.qml @@ -52,10 +52,12 @@ TestCase { } } - function test_empty() { + function init() { failOnWarning(/.?/) + } - var control = createTemporaryObject(groupBox, testCase) + function test_empty() { + let control = createTemporaryObject(groupBox, testCase) verify(control) verify(control.contentItem) @@ -66,7 +68,7 @@ TestCase { } function test_oneChild() { - var control = createTemporaryObject(oneChildBox, testCase) + let control = createTemporaryObject(oneChildBox, testCase) verify(control) compare(control.contentWidth, 100) @@ -78,7 +80,7 @@ TestCase { } function test_twoChildren() { - var control = createTemporaryObject(twoChildrenBox, testCase) + let control = createTemporaryObject(twoChildrenBox, testCase) verify(control) compare(control.contentWidth, 0) @@ -90,7 +92,7 @@ TestCase { } function test_contentItem() { - var control = createTemporaryObject(contentBox, testCase) + let control = createTemporaryObject(contentBox, testCase) verify(control) compare(control.contentWidth, 100) diff --git a/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml b/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml index 9d4847b4b1..25ad8e26fc 100644 --- a/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml +++ b/tests/auto/quickcontrols/controls/data/tst_itemdelegate.qml @@ -18,21 +18,23 @@ TestCase { ItemDelegate { } } - function test_defaults() { + function init() { failOnWarning(/.?/) + } + function test_defaults() { let control = createTemporaryObject(itemDelegate, testCase) verify(control) } function test_baseline() { - var control = createTemporaryObject(itemDelegate, testCase) + let control = createTemporaryObject(itemDelegate, testCase) verify(control) compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset) } function test_highlighted() { - var control = createTemporaryObject(itemDelegate, testCase) + let control = createTemporaryObject(itemDelegate, testCase) verify(control) verify(!control.highlighted) @@ -41,11 +43,11 @@ TestCase { } function test_spacing() { - var control = createTemporaryObject(itemDelegate, testCase, { text: "Some long, long, long text" }) + let control = createTemporaryObject(itemDelegate, testCase, { text: "Some long, long, long text" }) verify(control) verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth) - var textLabel = findChild(control.contentItem, "label") + let textLabel = findChild(control.contentItem, "label") verify(textLabel) // The implicitWidth of the IconLabel that all buttons use as their contentItem @@ -74,7 +76,7 @@ TestCase { } function test_display(data) { - var control = createTemporaryObject(itemDelegate, testCase, { + let control = createTemporaryObject(itemDelegate, testCase, { text: "ItemDelegate", display: data.display, width: 400, @@ -84,8 +86,8 @@ TestCase { verify(control) compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png") - var iconImage = findChild(control.contentItem, "image") - var textLabel = findChild(control.contentItem, "label") + let iconImage = findChild(control.contentItem, "image") + let textLabel = findChild(control.contentItem, "label") switch (control.display) { case ItemDelegate.IconOnly: diff --git a/tests/auto/quickcontrols/controls/data/tst_menuitem.qml b/tests/auto/quickcontrols/controls/data/tst_menuitem.qml index d1ed076705..4b801d774a 100644 --- a/tests/auto/quickcontrols/controls/data/tst_menuitem.qml +++ b/tests/auto/quickcontrols/controls/data/tst_menuitem.qml @@ -23,21 +23,23 @@ TestCase { Menu { } } - function test_defaults() { + function init() { failOnWarning(/.?/) + } + function test_defaults() { let control = createTemporaryObject(menuItem, testCase) verify(control) } function test_baseline() { - var control = createTemporaryObject(menuItem, testCase) + let control = createTemporaryObject(menuItem, testCase) verify(control) compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset) } function test_checkable() { - var control = createTemporaryObject(menuItem, testCase) + let control = createTemporaryObject(menuItem, testCase) verify(control) verify(control.hasOwnProperty("checkable")) verify(!control.checkable) @@ -54,7 +56,7 @@ TestCase { } function test_highlighted() { - var control = createTemporaryObject(menuItem, testCase) + let control = createTemporaryObject(menuItem, testCase) verify(control) verify(!control.highlighted) @@ -76,7 +78,7 @@ TestCase { } function test_display(data) { - var control = createTemporaryObject(menuItem, testCase, { + let control = createTemporaryObject(menuItem, testCase, { text: "MenuItem", display: data.display, "icon.source": "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png", @@ -85,9 +87,9 @@ TestCase { verify(control) compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png") - var padding = data.mirrored ? control.contentItem.rightPadding : control.contentItem.leftPadding - var iconImage = findChild(control.contentItem, "image") - var textLabel = findChild(control.contentItem, "label") + let padding = data.mirrored ? control.contentItem.rightPadding : control.contentItem.leftPadding + let iconImage = findChild(control.contentItem, "image") + let textLabel = findChild(control.contentItem, "label") switch (control.display) { case MenuItem.IconOnly: @@ -123,14 +125,14 @@ TestCase { } function test_menu() { - var control = createTemporaryObject(menu, testCase) + let control = createTemporaryObject(menu, testCase) verify(control) - var item1 = createTemporaryObject(menuItem, testCase) + let item1 = createTemporaryObject(menuItem, testCase) verify(item1) compare(item1.menu, null) - var item2 = createTemporaryObject(menuItem, testCase) + let item2 = createTemporaryObject(menuItem, testCase) verify(item2) compare(item2.menu, null) diff --git a/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml b/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml index 5b1081675e..7db8855b8a 100644 --- a/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml +++ b/tests/auto/quickcontrols/controls/data/tst_monthgrid.qml @@ -37,15 +37,17 @@ TestCase { SignalSpy { } } - function test_defaults() { + function init() { failOnWarning(/.?/) + } + function test_defaults() { let control = createTemporaryObject(defaultGrid, testCase) verify(control) } function test_locale() { - var control = delegateGrid.createObject(testCase, {month: 0, year: 2013}) + let control = delegateGrid.createObject(testCase, {month: 0, year: 2013}) compare(control.contentItem.children.length, 6 * 7 + 1) @@ -58,15 +60,15 @@ TestCase { compare(control.locale.name, "en_GB") // M T W T F S S - var en_GB = ["2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06", + let en_GB = ["2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06", "2013-01-07", "2013-01-08", "2013-01-09", "2013-01-10", "2013-01-11", "2013-01-12", "2013-01-13", "2013-01-14", "2013-01-15", "2013-01-16", "2013-01-17", "2013-01-18", "2013-01-19", "2013-01-20", "2013-01-21", "2013-01-22", "2013-01-23", "2013-01-24", "2013-01-25", "2013-01-26", "2013-01-27", "2013-01-28", "2013-01-29", "2013-01-30", "2013-01-31", "2013-02-01", "2013-02-02", "2013-02-03", "2013-02-04", "2013-02-05", "2013-02-06", "2013-02-07", "2013-02-08", "2013-02-09", "2013-02-10"] - for (var i = 0; i < 42; ++i) { - var cellDate = new Date(en_GB[i]) + for (let i = 0; i < 42; ++i) { + let cellDate = new Date(en_GB[i]) compare(control.contentItem.children[i].date.getFullYear(), cellDate.getUTCFullYear()) compare(control.contentItem.children[i].date.getMonth(), cellDate.getUTCMonth()) compare(control.contentItem.children[i].date.getDate(), cellDate.getUTCDate()) @@ -81,15 +83,15 @@ TestCase { compare(control.locale.name, "en_US") // S M T W T F S - var en_US = ["2012-12-30", "2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", + let en_US = ["2012-12-30", "2012-12-31", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06", "2013-01-07", "2013-01-08", "2013-01-09", "2013-01-10", "2013-01-11", "2013-01-12", "2013-01-13", "2013-01-14", "2013-01-15", "2013-01-16", "2013-01-17", "2013-01-18", "2013-01-19", "2013-01-20", "2013-01-21", "2013-01-22", "2013-01-23", "2013-01-24", "2013-01-25", "2013-01-26", "2013-01-27", "2013-01-28", "2013-01-29", "2013-01-30", "2013-01-31", "2013-02-01", "2013-02-02", "2013-02-03", "2013-02-04", "2013-02-05", "2013-02-06", "2013-02-07", "2013-02-08", "2013-02-09"] - for (var j = 0; j < 42; ++j) { - cellDate = new Date(en_US[j]) + for (let j = 0; j < 42; ++j) { + let cellDate = new Date(en_US[j]) compare(control.contentItem.children[j].date.getFullYear(), cellDate.getUTCFullYear()) compare(control.contentItem.children[j].date.getMonth(), cellDate.getUTCMonth()) compare(control.contentItem.children[j].date.getDate(), cellDate.getUTCDate()) @@ -103,7 +105,7 @@ TestCase { } function test_range() { - var control = defaultGrid.createObject(testCase) + let control = defaultGrid.createObject(testCase) control.month = 0 compare(control.month, 0) @@ -138,7 +140,7 @@ TestCase { } function test_bce() { - var control = defaultGrid.createObject(testCase) + let control = defaultGrid.createObject(testCase) compare(control.contentItem.children.length, 6 * 7 + 1) @@ -153,14 +155,14 @@ TestCase { compare(control.year, -1) // M T W T F S S - var jan1bce = [27, 28, 29, 30, 31, 1, 2, + let jan1bce = [27, 28, 29, 30, 31, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 1, 2, 3, 4, 5, 6] - for (var i = 0; i < 42; ++i) + for (let i = 0; i < 42; ++i) compare(control.contentItem.children[i].text, jan1bce[i].toString()) // February 1 BCE @@ -170,21 +172,21 @@ TestCase { compare(control.year, -1) // M T W T F S S - var feb1bce = [31, 1, 2, 3, 4, 5, 6, + let feb1bce = [31, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] - for (var j = 0; j < 42; ++j) + for (let j = 0; j < 42; ++j) compare(control.contentItem.children[j].text, feb1bce[j].toString()) control.destroy() } function test_font() { - var control = defaultGrid.createObject(testCase) + let control = defaultGrid.createObject(testCase) verify(control.contentItem.children[0]) @@ -202,24 +204,24 @@ TestCase { } function test_clicked(data) { - var control = createTemporaryObject(defaultGrid, testCase) + let control = createTemporaryObject(defaultGrid, testCase) verify(control) compare(control.contentItem.children.length, 6 * 7 + 1) - var pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"}) + let pressedSpy = signalSpy.createObject(control, {target: control, signalName: "pressed"}) verify(pressedSpy.valid) - var releasedSpy = signalSpy.createObject(control, {target: control, signalName: "released"}) + let releasedSpy = signalSpy.createObject(control, {target: control, signalName: "released"}) verify(releasedSpy.valid) - var clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"}) + let clickedSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"}) verify(clickedSpy.valid) - var touch = touchEvent(control) + let touch = touchEvent(control) - for (var i = 0; i < 42; ++i) { - var cell = control.contentItem.children[i] + for (let i = 0; i < 42; ++i) { + let cell = control.contentItem.children[i] verify(cell) if (data.touch) diff --git a/tests/auto/quickcontrols/controls/data/tst_scrollview.qml b/tests/auto/quickcontrols/controls/data/tst_scrollview.qml index bef3fe2eec..e52a8dc114 100644 --- a/tests/auto/quickcontrols/controls/data/tst_scrollview.qml +++ b/tests/auto/quickcontrols/controls/data/tst_scrollview.qml @@ -146,6 +146,7 @@ TestCase { } } } + Component { id: scrollableTextAreaWithSibling ScrollView { @@ -716,4 +717,35 @@ TestCase { // Shouldn't crash. } + + Component { + id: scrollViewContentItemComp + + ScrollView { + id: scrollView + anchors.fill: parent + Column { + width: parent.width + Repeater { + model: 20 + Rectangle { + width: scrollView.width + height: 60 + color: (index % 2 == 0) ? "red" : "green" + } + } + } + } + } + + function test_scrollViewContentItemSize() { + let scrollview = createTemporaryObject(scrollViewContentItemComp, testCase) + verify(scrollview) + let contentItem = scrollview.contentItem + waitForRendering(contentItem) + compare(contentItem.contentWidth, 400) + compare(contentItem.contentHeight, 1200) + compare(scrollview.contentWidth, 400) + compare(scrollview.contentHeight, 1200) + } } diff --git a/tests/auto/quickcontrols/controls/data/tst_stackview.qml b/tests/auto/quickcontrols/controls/data/tst_stackview.qml index 6973165212..e4a85f5b77 100644 --- a/tests/auto/quickcontrols/controls/data/tst_stackview.qml +++ b/tests/auto/quickcontrols/controls/data/tst_stackview.qml @@ -1604,7 +1604,7 @@ TestCase { StackView { id: stackView anchors.fill: parent - initialItem: cppComponent + initialItem: stackView.cppComponent property Component cppComponent: ComponentCreator.createComponent("import QtQuick; Rectangle { color: \"navajowhite\" }") } diff --git a/tests/auto/quickcontrols/controls/data/tst_textarea.qml b/tests/auto/quickcontrols/controls/data/tst_textarea.qml index 1e03947e05..1831700345 100644 --- a/tests/auto/quickcontrols/controls/data/tst_textarea.qml +++ b/tests/auto/quickcontrols/controls/data/tst_textarea.qml @@ -207,10 +207,12 @@ TestCase { } function test_alignment(data) { - var control = createTemporaryObject(textArea, testCase, {text: data.text, placeholderText: data.placeholderText, horizontalAlignment: data.textAlignment}) + var control = createTemporaryObject(textArea, testCase, {text: data.text, placeholderText: data.placeholderText}) - if (data.textAlignment !== undefined) + if (data.textAlignment !== undefined) { + control.horizontalAlignment = data.textAlignment compare(control.horizontalAlignment, data.textAlignment) + } // The placeholder text of the Material style doesn't currently respect the alignment of the control. if (StyleInfo.styleName !== "Material") { @@ -234,7 +236,7 @@ TestCase { return [ {tag: "bold", value: true}, {tag: "capitalization", value: Font.Capitalize}, - {tag: "family", value: "Courier"}, + {tag: "family", value: "Tahoma"}, {tag: "italic", value: true}, {tag: "strikeout", value: true}, {tag: "underline", value: true}, @@ -272,7 +274,7 @@ TestCase { } function test_flickable() { - var control = createTemporaryObject(flickable, testCase, {text:"line0", selectByMouse: true}) + var control = createTemporaryObject(flickable, testCase) verify(control) var textArea = control.TextArea.flickable diff --git a/tests/auto/quickcontrols/controls/data/tst_textfield.qml b/tests/auto/quickcontrols/controls/data/tst_textfield.qml index 66dcdc397a..795e6a79b2 100644 --- a/tests/auto/quickcontrols/controls/data/tst_textfield.qml +++ b/tests/auto/quickcontrols/controls/data/tst_textfield.qml @@ -154,10 +154,12 @@ TestCase { } function test_alignment(data) { - var control = createTemporaryObject(textField, testCase, {text: data.text, placeholderText: data.placeholderText, horizontalAlignment: data.textAlignment}) + var control = createTemporaryObject(textField, testCase, {text: data.text, placeholderText: data.placeholderText}) - if (data.textAlignment !== undefined) + if (data.textAlignment !== undefined) { + control.horizontalAlignment = data.textAlignment compare(control.horizontalAlignment, data.textAlignment) + } // The placeholder text of the Material style doesn't currently respect the alignment of the control. if (StyleInfo.styleName !== "Material") { for (var i = 0; i < control.children.length; ++i) { @@ -180,7 +182,7 @@ TestCase { return [ {tag: "bold", value: true}, {tag: "capitalization", value: Font.Capitalize}, - {tag: "family", value: "Courier"}, + {tag: "family", value: "Tahoma"}, {tag: "italic", value: true}, {tag: "strikeout", value: true}, {tag: "underline", value: true}, diff --git a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml index d4c8b78355..f89e22afca 100644 --- a/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml +++ b/tests/auto/quickcontrols/qquickpopup/data/applicationwindow-hover.qml @@ -15,22 +15,24 @@ ApplicationWindow { Button { id: parentButton text: "Parent" + palette.buttonText: hovered ? "tomato" : "black" anchors.fill: parent + anchors.margins: 10 Popup { id: popup - x: 1 - y: 1 - leftPadding: 1 - rightPadding: 1 - topPadding: 1 - bottomPadding: 1 - + x: 10 + y: 10 + leftPadding: 10 + rightPadding: 10 + topPadding: 10 + bottomPadding: 10 Button { anchors.centerIn: parent id: childButton text: "Child" + palette.buttonText: hovered ? "tomato" : "black" } } } diff --git a/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml b/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml index fa61e8b706..054bcf4c07 100644 --- a/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml +++ b/tests/auto/quickcontrols/qquickpopup/data/window-hover.qml @@ -16,21 +16,24 @@ Window { Button { id: parentButton text: "Parent" + palette.buttonText: hovered ? "tomato" : "black" anchors.fill: parent + anchors.margins: 10 Popup { id: popup - x: 1 - y: 1 - topPadding: 1 - bottomPadding: 1 - leftPadding: 1 - rightPadding: 1 + x: 10 + y: 10 + leftPadding: 10 + rightPadding: 10 + topPadding: 10 + bottomPadding: 10 Button { anchors.centerIn: parent id: childButton text: "Child" + palette.buttonText: hovered ? "tomato" : "black" } } } diff --git a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp index 19add80c54..09c0b29d2f 100644 --- a/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp +++ b/tests/auto/quickcontrols/qquickpopup/tst_qquickpopup.cpp @@ -991,18 +991,19 @@ void tst_QQuickPopup::hover() QVERIFY(openedSpy.size() == 1 || openedSpy.wait()); QTRY_VERIFY(popup->width() > 10); // somehow this can take a short time with macOS style - // hover the parent button outside the popup - QTest::mouseMove(window, QPoint(window->width() - 1, window->height() - 1)); + // Hover the parent button outside the popup. It has 10 pixel anchor margins around the window. + PointLerper pointLerper(window); + pointLerper.move(15, 15); QCOMPARE(parentButton->isHovered(), !modal); QVERIFY(!childButton->isHovered()); - // hover the popup background - QTest::mouseMove(window, QPoint(1, 1)); + // Hover the popup background. Its top-left is 10 pixels in from its parent. + pointLerper.move(25, 25); QVERIFY(!parentButton->isHovered()); QVERIFY(!childButton->isHovered()); - // hover the child button in a popup - QTest::mouseMove(window, QPoint(popup->x() + popup->width() / 2, popup->y() + popup->height() / 2)); + // Hover the child button in a popup. + pointLerper.move(mapCenterToWindow(childButton)); QVERIFY(!parentButton->isHovered()); QVERIFY(childButton->isHovered()); diff --git a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp index aec6087e02..27334f06bb 100644 --- a/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp +++ b/tests/auto/quickdialogs/qquickfiledialogimpl/tst_qquickfiledialogimpl.cpp @@ -89,6 +89,9 @@ private slots: void setSelectedFile(); void selectNewFileViaTextField_data(); void selectNewFileViaTextField(); + void selectExistingFileShouldWarnUserWhenFileModeEqualsSaveFile(); + void fileNameTextFieldOnlyChangesWhenSelectingFiles(); + void setSchemeForSelectedFile(); private: enum DelegateOrderPolicy @@ -1198,15 +1201,17 @@ void tst_QQuickFileDialogImpl::itemsDisabledWhenNecessary() void tst_QQuickFileDialogImpl::fileMode_data() { QTest::addColumn<QQuickFileDialog::FileMode>("fileMode"); + QTest::addColumn<QString>("acceptButtonText"); - QTest::newRow("OpenFile") << QQuickFileDialog::OpenFile; - QTest::newRow("OpenFiles") << QQuickFileDialog::OpenFiles; - QTest::newRow("SaveFile") << QQuickFileDialog::SaveFile; + QTest::newRow("OpenFile") << QQuickFileDialog::OpenFile << "Open"; + QTest::newRow("OpenFiles") << QQuickFileDialog::OpenFiles << "Open"; + QTest::newRow("SaveFile") << QQuickFileDialog::SaveFile << "Save"; } void tst_QQuickFileDialogImpl::fileMode() { QFETCH(QQuickFileDialog::FileMode, fileMode); + QFETCH(QString, acceptButtonText); // Open the dialog. FileDialogTestHelper dialogHelper(this, "fileDialog.qml"); @@ -1221,13 +1226,13 @@ void tst_QQuickFileDialogImpl::fileMode() COMPARE_URL(dialogHelper.dialog->currentFile(), QUrl::fromLocalFile(tempFile1->fileName())); COMPARE_URLS(dialogHelper.dialog->currentFiles(), { QUrl::fromLocalFile(tempFile1->fileName()) }); - // All modes should support opening an existing file, so the Open button should be enabled. + // All modes should support opening an existing file, so the accept button should be enabled. QVERIFY(dialogHelper.quickDialog->footer()); auto dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox*>(); QVERIFY(dialogButtonBox); - QQuickAbstractButton* openButton = findDialogButton(dialogButtonBox, "Open"); - QVERIFY(openButton); - QCOMPARE(openButton->isEnabled(), true); + QQuickAbstractButton *acceptButton = findDialogButton(dialogButtonBox, acceptButtonText); + QVERIFY(acceptButton); + QCOMPARE(acceptButton->isEnabled(), true); // Only the OpenFiles mode should allow multiple files to be selected, however. QQuickFileDialogDelegate *tempFile2Delegate = nullptr; @@ -1372,6 +1377,9 @@ void tst_QQuickFileDialogImpl::setSelectedFile() }; FileDialogTestHelper dialogHelper( this, "setSelectedFile.qml", {}, initialProperties); + + dialogHelper.dialog->setOptions(QFileDialogOptions::DontConfirmOverwrite); + OPEN_QUICK_DIALOG(); // The selected file should be what we set. @@ -1461,6 +1469,176 @@ void tst_QQuickFileDialogImpl::selectNewFileViaTextField() } } +void tst_QQuickFileDialogImpl::selectExistingFileShouldWarnUserWhenFileModeEqualsSaveFile() +{ + FileDialogTestHelper dialogHelper(this, "fileDialog.qml"); + dialogHelper.dialog->setFileMode(QQuickFileDialog::SaveFile); + dialogHelper.dialog->setSelectedFile(QUrl::fromLocalFile(tempFile1->fileName())); + + OPEN_QUICK_DIALOG(); + QQuickTest::qWaitForPolish(dialogHelper.window()); + + QSignalSpy acceptedSpy(dialogHelper.dialog, SIGNAL(accepted())); + + auto *dialogButtonBox = dialogHelper.quickDialog->footer()->findChild<QQuickDialogButtonBox *>(); + QVERIFY(dialogButtonBox); + + auto *confirmationDialog = dialogHelper.quickDialog->findChild<QQuickDialog *>("confirmationDialog"); + QVERIFY(confirmationDialog); + + auto *openButton = dialogButtonBox->standardButton(QPlatformDialogHelper::Open); + QVERIFY(openButton); + + auto *confirmationButtonBox = qobject_cast<QQuickDialogButtonBox *>(confirmationDialog->footer()); + QVERIFY(confirmationButtonBox); + + const QPoint openButtonCenterPos = + openButton->mapToScene({ openButton->width() / 2, openButton->height() / 2 }).toPoint(); + + QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, openButtonCenterPos); + + QTRY_VERIFY(confirmationDialog->isOpened()); + QVERIFY(dialogHelper.dialog->isVisible()); + + // Yes button should have focus by default + QTest::keyClick(dialogHelper.window(), Qt::Key_Space, Qt::NoModifier); + + QTRY_VERIFY(!confirmationDialog->isOpened()); + QVERIFY(!dialogHelper.dialog->isVisible()); + QCOMPARE(acceptedSpy.count(), 1); + + // Try again, but click "No" this time. + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier); + + QTRY_VERIFY(confirmationDialog->isOpened()); + QVERIFY(dialogHelper.dialog->isVisible()); + + // Make No button trigger a clicked() event. + auto *confirmationNoButton = confirmationButtonBox->standardButton(QPlatformDialogHelper::No); + QVERIFY(confirmationNoButton); + QVERIFY(clickButton(confirmationNoButton)); + + // FileDialog is still opened + QTRY_VERIFY(!confirmationDialog->isOpened()); + QVERIFY(dialogHelper.dialog->isVisible()); + QCOMPARE(acceptedSpy.count(), 1); + + // Try again + QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier); + + QTRY_VERIFY(confirmationDialog->isOpened()); + QVERIFY(dialogHelper.dialog->isVisible()); + + QTest::keyClick(dialogHelper.window(), Qt::Key_Space, Qt::NoModifier); + + QTRY_VERIFY(!confirmationDialog->isOpened()); + QVERIFY(!dialogHelper.dialog->isVisible()); + QCOMPARE(acceptedSpy.count(), 2); + + // Make sure that DontConfirmOverwrite works + dialogHelper.dialog->setOptions(QFileDialogOptions::DontConfirmOverwrite); + + QVERIFY(dialogHelper.openDialog()); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier); + QTRY_VERIFY(!confirmationDialog->isOpened()); + QVERIFY(!dialogHelper.dialog->isVisible()); + QCOMPARE(acceptedSpy.count(), 3); +} + +void tst_QQuickFileDialogImpl::fileNameTextFieldOnlyChangesWhenSelectingFiles() +{ + const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName()); + const auto tempSubDirUrl = QUrl::fromLocalFile(tempSubDir.path()); + const auto tempFile11Url = QUrl::fromLocalFile(tempFile1->fileName()); + + const QVariantMap initialProperties = { + { "tempFile1Url", QVariant::fromValue(tempSubFile1Url) }, + { "fileMode", QVariant::fromValue(QQuickFileDialog::SaveFile) } + }; + FileDialogTestHelper dialogHelper(this, "setSelectedFile.qml", {}, initialProperties); + + OPEN_QUICK_DIALOG(); + QQuickTest::qWaitForPolish(dialogHelper.window()); + + QQuickTextField *fileNameTextField = + dialogHelper.quickDialog->findChild<QQuickTextField *>("fileNameTextField"); + QVERIFY(fileNameTextField); + + auto getSelectedFileInfo = [&dialogHelper]() { + return QFileInfo(dialogHelper.dialog->selectedFile().toLocalFile()); + }; + + QVERIFY(getSelectedFileInfo().isFile()); + QCOMPARE(fileNameTextField->text(), tempSubFile1Url.fileName()); + QCOMPARE(dialogHelper.dialog->selectedFile(), tempSubFile1Url); + + auto *breadcrumbBar = dialogHelper.quickDialog->findChild<QQuickFolderBreadcrumbBar *>(); + QVERIFY(breadcrumbBar); + + // Pressing the up button causes tempSubDir to be selected + QVERIFY(clickButton(breadcrumbBar->upButton())); + + QVERIFY(getSelectedFileInfo().isDir()); + QCOMPARE(fileNameTextField->text(), tempSubFile1Url.fileName()); + QCOMPARE(dialogHelper.dialog->selectedFile(), tempSubDirUrl); + + // Change the selected file from the outside + dialogHelper.dialog->close(); + dialogHelper.dialog->setSelectedFile(tempFile11Url); + dialogHelper.openDialog(); + QTRY_VERIFY(dialogHelper.isQuickDialogOpen()); + + QVERIFY(getSelectedFileInfo().isFile()); + QCOMPARE(fileNameTextField->text(), tempFile11Url.fileName()); + QCOMPARE(dialogHelper.dialog->selectedFile(), tempFile11Url); +} + +void tst_QQuickFileDialogImpl::setSchemeForSelectedFile() +{ + const auto tempSubFile1Url = QUrl::fromLocalFile(tempSubFile1->fileName()); + + const QVariantMap initialProperties = { + { "tempFile1Url", QVariant::fromValue(tempSubFile1Url) }, + { "fileMode", QVariant::fromValue(QQuickFileDialog::SaveFile) } + }; + FileDialogTestHelper dialogHelper(this, "setSelectedFile.qml", {}, initialProperties); + + OPEN_QUICK_DIALOG(); + QQuickTest::qWaitForPolish(dialogHelper.window()); + + QQuickTextField *fileNameTextField = + dialogHelper.quickDialog->findChild<QQuickTextField *>("fileNameTextField"); + QVERIFY(fileNameTextField); + + QVERIFY(!tempSubFile1Url.scheme().isEmpty()); + QVERIFY(!dialogHelper.dialog->selectedFile().scheme().isEmpty()); + QCOMPARE(tempSubFile1Url, dialogHelper.dialog->selectedFile()); + + fileNameTextField->clear(); + + const QPoint textFieldCenterPos = + fileNameTextField->mapToScene({ fileNameTextField->width() / 2, fileNameTextField->height() / 2 }).toPoint(); + QTest::mouseClick(dialogHelper.window(), Qt::LeftButton, Qt::NoModifier, textFieldCenterPos); + + const QByteArray newFileName("helloworld.txt"); + for (const auto &c : newFileName) + QTest::keyClick(dialogHelper.window(), c); + QTest::keyClick(dialogHelper.window(), Qt::Key_Enter, Qt::NoModifier); + + QTRY_COMPARE(fileNameTextField->text(), QString::fromLatin1(newFileName)); + + const auto newFilePath = + QUrl::fromLocalFile(QFileInfo(tempSubFile1Url.toLocalFile()).dir().absolutePath() + u'/' + newFileName); + QVERIFY(!newFilePath.scheme().isEmpty()); + QVERIFY(!dialogHelper.dialog->selectedFile().scheme().isEmpty()); + QCOMPARE(dialogHelper.dialog->selectedFile(), newFilePath); +} + QTEST_MAIN(tst_QQuickFileDialogImpl) #include "tst_qquickfiledialogimpl.moc" diff --git a/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml b/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml new file mode 100644 index 0000000000..fe3d1925e5 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/tapHandler.qml @@ -0,0 +1,11 @@ +import QtQuick + +Rectangle { + width: 100 + height: 100 + color: th.pressed ? "steelblue" : "beige" + + TapHandler { + id: th + } +} diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 7d2c15e7fa..522ef3667d 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -11,6 +11,7 @@ #include <QtQuick/qquickitem.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquickmousearea_p.h> +#include <QtQuick/private/qquicktaphandler_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtGui/QWindow> @@ -128,6 +129,8 @@ private slots: void synthMouseFromTouch(); void touchTapMouseArea(); void touchTapButton(); + void touchTapHandler_data(); + void touchTapHandler(); void touchMultipleWidgets(); void tabKey(); void resizeOverlay(); @@ -138,6 +141,7 @@ private slots: #endif void focusPreserved(); void accessibilityHandlesViewChange(); + void cleanupRhi(); private: QPointingDevice *device = QTest::createTouchDevice(); @@ -681,6 +685,44 @@ void tst_qquickwidget::touchTapButton() QTRY_VERIFY(rootItem->property("wasClicked").toBool()); } +void tst_qquickwidget::touchTapHandler_data() +{ + QTest::addColumn<bool>("guiSynthMouse"); // AA_SynthesizeMouseForUnhandledTouchEvents + QTest::addColumn<QQuickTapHandler::GesturePolicy>("gesturePolicy"); + + // QTest::newRow("nosynth: passive grab") << false << QQuickTapHandler::DragThreshold; // still failing + QTest::newRow("nosynth: exclusive grab") << false << QQuickTapHandler::ReleaseWithinBounds; + QTest::newRow("allowsynth: passive grab") << true << QQuickTapHandler::DragThreshold; // QTBUG-113558 + QTest::newRow("allowsynth: exclusive grab") << true << QQuickTapHandler::ReleaseWithinBounds; +} + +void tst_qquickwidget::touchTapHandler() +{ + QFETCH(bool, guiSynthMouse); + QFETCH(QQuickTapHandler::GesturePolicy, gesturePolicy); + + QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, guiSynthMouse); + QQuickWidget quick; + QVERIFY(quick.testAttribute(Qt::WA_AcceptTouchEvents)); + quick.setSource(testFileUrl("tapHandler.qml")); + quick.show(); + QVERIFY(QTest::qWaitForWindowExposed(&quick)); + + QQuickItem *rootItem = quick.rootObject(); + QVERIFY(rootItem); + QQuickTapHandler *th = rootItem->findChild<QQuickTapHandler *>(); + QVERIFY(th); + th->setGesturePolicy(gesturePolicy); + QSignalSpy tappedSpy(th, &QQuickTapHandler::tapped); + + const QPoint p(50, 50); + QTest::touchEvent(&quick, device).press(0, p, &quick); + QTRY_COMPARE(th->isPressed(), true); + QTest::touchEvent(&quick, device).release(0, p, &quick); + QTRY_COMPARE(tappedSpy.size(), 1); + QCOMPARE(th->isPressed(), false); +} + void tst_qquickwidget::touchMultipleWidgets() { QWidget window; @@ -1027,6 +1069,27 @@ void tst_qquickwidget::accessibilityHandlesViewChange() (void)iface->child(0); } +class CreateDestroyWidget : public QWidget +{ +public: + using QWidget::create; + using QWidget::destroy; +}; + +void tst_qquickwidget::cleanupRhi() +{ +#ifdef Q_OS_ANDROID + QSKIP("This test crashes on Android (QTBUG-121133)"); +#endif + CreateDestroyWidget topLevel; + QQuickWidget quickWidget(&topLevel); + quickWidget.setSource(testFileUrl("rectangle.qml")); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + topLevel.destroy(); + topLevel.create(); +} QTEST_MAIN(tst_qquickwidget) diff --git a/tests/manual/quickcontrols/material/pages/ButtonPage.qml b/tests/manual/quickcontrols/material/pages/ButtonPage.qml index 3f861e7a70..22ee0894a7 100644 --- a/tests/manual/quickcontrols/material/pages/ButtonPage.qml +++ b/tests/manual/quickcontrols/material/pages/ButtonPage.qml @@ -13,6 +13,12 @@ Page { header: RowLayout { CheckBox { + id: textCheckBox + text: "Text" + checked: true + } + + CheckBox { id: iconCheckBox text: "Icon" } @@ -88,7 +94,7 @@ Page { model: 13 Button { - text: modelData + text: textCheckBox.checked ? modelData : "" flat: flatCheckBox.checked icon.source: iconCheckBox.checked ? "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png" : "" diff --git a/tools/qml/conf/content/resizeItemToWindow.qml b/tools/qml/conf/content/resizeItemToWindow.qml index 4a6d06591c..4810b6f0e4 100644 --- a/tools/qml/conf/content/resizeItemToWindow.qml +++ b/tools/qml/conf/content/resizeItemToWindow.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial import QtQuick.Window 2.0 import QtQuick 2.0 diff --git a/tools/qml/conf/content/resizeWindowToItem.qml b/tools/qml/conf/content/resizeWindowToItem.qml index 942e739d13..586047459c 100644 --- a/tools/qml/conf/content/resizeWindowToItem.qml +++ b/tools/qml/conf/content/resizeWindowToItem.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial import QtQuick.Window 2.0 import QtQuick 2.0 diff --git a/tools/qml/conf/default.qml b/tools/qml/conf/default.qml index c9bae6dcd0..b4fa025e66 100644 --- a/tools/qml/conf/default.qml +++ b/tools/qml/conf/default.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial import QmlRuntime.Config 1.0 Configuration { diff --git a/tools/qml/conf/resizeToItem.qml b/tools/qml/conf/resizeToItem.qml index 7c1389a7a7..fe40e9f2d3 100644 --- a/tools/qml/conf/resizeToItem.qml +++ b/tools/qml/conf/resizeToItem.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial import QmlRuntime.Config 1.0 Configuration { diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index e1c0e78fe3..1fcd0ad974 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -29,6 +29,35 @@ using namespace Qt::StringLiterals; constexpr int JSON_LOGGING_FORMAT_REVISION = 3; +bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments) +{ + allArguments.reserve(arguments.size()); + for (const QString &argument : arguments) { + // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it + if (argument.startsWith(u'@')) { + QString optionsFile = argument; + optionsFile.remove(0, 1); + if (optionsFile.isEmpty()) { + qWarning().nospace() << "The @ option requires an input file"; + return false; + } + QFile f(optionsFile); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning().nospace() << "Cannot open options file specified with @"; + return false; + } + while (!f.atEnd()) { + QString line = QString::fromLocal8Bit(f.readLine().trimmed()); + if (!line.isEmpty()) + allArguments << line; + } + } else { + allArguments << argument; + } + } + return true; +} + int main(int argv, char *argc[]) { QHashSeed::setDeterministicGlobalSeed(); @@ -172,11 +201,16 @@ All warnings can be set to three levels: parser.addPositionalArgument(QLatin1String("files"), QLatin1String("list of qml or js files to verify")); - if (!parser.parse(app.arguments())) { - if (parser.unknownOptionNames().isEmpty()) { - qWarning().noquote() << parser.errorText(); - return 1; - } + + QStringList arguments; + if (!argumentsFromCommandLineAndFile(arguments, app.arguments())) { + // argumentsFromCommandLine already printed any necessary warnings. + return 1; + } + + if (!parser.parse(arguments)) { + qWarning().noquote() << parser.errorText(); + return 1; } // Since we can't use QCommandLineParser::process(), we need to handle version and help manually diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index 8f75226fd8..c9e917cc00 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -3,14 +3,12 @@ #include "qmlprofilerdata.h" -#include <QStringList> -#include <QUrl> -#include <QHash> -#include <QFile> -#include <QXmlStreamReader> -#include <QRegularExpression> -#include <QQueue> -#include <QStack> +#include <QtCore/qfile.h> +#include <QtCore/qqueue.h> +#include <QtCore/qregularexpression.h> +#include <QtCore/qurl.h> +#include <QtCore/qxmlstream.h> +#include <QtCore/qxpfunctional.h> #include <limits> @@ -375,6 +373,132 @@ private: QXmlStreamWriter stream; }; +struct DataIterator +{ + DataIterator( + const QmlProfilerDataPrivate *d, + qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> &&sendEvent) + : d(d) + , sendEvent(std::move(sendEvent)) + {} + + void run(); + +private: + void handleRangeEvent(const QQmlProfilerEvent &event, const QQmlProfilerEventType &type); + void sendPending(); + void endLevel0(); + + const QmlProfilerDataPrivate *d = nullptr; + const qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> sendEvent; + + QQueue<QQmlProfilerEvent> pointEvents; + QList<QQmlProfilerEvent> rangeStarts[MaximumRangeType]; + QList<qint64> rangeEnds[MaximumRangeType]; + + int level = 0; +}; + +void DataIterator::handleRangeEvent( + const QQmlProfilerEvent &event, const QQmlProfilerEventType &type) +{ + QList<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()]; + switch (event.rangeStage()) { + case RangeStart: { + ++level; + starts.append(event); + break; + } + case RangeEnd: { + const qint64 invalidTimestamp = -1; + QList<qint64> &ends = rangeEnds[type.rangeType()]; + + // -1 because all valid timestamps are >= 0. + ends.resize(starts.size(), invalidTimestamp); + + qsizetype i = starts.size(); + while (ends[--i] != invalidTimestamp) {} + + Q_ASSERT(i >= 0); + Q_ASSERT(starts[i].timestamp() <= event.timestamp()); + + ends[i] = event.timestamp(); + if (--level == 0) + endLevel0(); + break; + } + default: + break; + } +} + +void DataIterator::sendPending() +{ + // Send all pending events in the order of their start times. + + qsizetype index[MaximumRangeType] = { 0, 0, 0, 0, 0, 0 }; + while (true) { + + // Find the range type with the minimum start time. + qsizetype minimum = MaximumRangeType; + qint64 minimumTime = std::numeric_limits<qint64>::max(); + for (qsizetype i = 0; i < MaximumRangeType; ++i) { + const QList<QQmlProfilerEvent> &starts = rangeStarts[i]; + if (starts.size() == index[i]) + continue; + const qint64 timestamp = starts[index[i]].timestamp(); + if (timestamp < minimumTime) { + minimumTime = timestamp; + minimum = i; + } + } + if (minimum == MaximumRangeType) + break; + + // Send all point events that happened before the range we've found. + while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime) + sendEvent(pointEvents.dequeue(), 0); + + // Send the range itself + sendEvent(rangeStarts[minimum][index[minimum]], + rangeEnds[minimum][index[minimum]] - minimumTime); + + // Bump the index so that we don't send the same range again + ++index[minimum]; + } +} + +void DataIterator::endLevel0() +{ + sendPending(); + for (qsizetype i = 0; i < MaximumRangeType; ++i) { + rangeStarts[i].clear(); + rangeEnds[i].clear(); + } +} + +void DataIterator::run() +{ + for (const QQmlProfilerEvent &event : std::as_const(d->events)) { + const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); + if (type.rangeType() != MaximumRangeType) + handleRangeEvent(event, type); + else if (level == 0) + sendEvent(event, 0); + else + pointEvents.enqueue(event); + } + + for (qsizetype i = 0; i < MaximumRangeType; ++i) { + while (rangeEnds[i].size() < rangeStarts[i].size()) { + rangeEnds[i].append(d->traceEndTime); + --level; + } + } + + sendPending(); +} + bool QmlProfilerData::save(const QString &filename) { if (isEmpty()) { @@ -442,6 +566,7 @@ bool QmlProfilerData::save(const QString &filename) stream.writeStartElement("profilerDataModel"); auto sendEvent = [&](const QQmlProfilerEvent &event, qint64 duration = 0) { + Q_ASSERT(duration >= 0); const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); stream.writeStartElement("range"); stream.writeAttribute("startTime", event.timestamp()); @@ -481,74 +606,7 @@ bool QmlProfilerData::save(const QString &filename) stream.writeEndElement(); }; - QQueue<QQmlProfilerEvent> pointEvents; - QQueue<QQmlProfilerEvent> rangeStarts[MaximumRangeType]; - QStack<qint64> rangeEnds[MaximumRangeType]; - int level = 0; - - auto sendPending = [&]() { - forever { - int minimum = MaximumRangeType; - qint64 minimumTime = std::numeric_limits<qint64>::max(); - for (int i = 0; i < MaximumRangeType; ++i) { - const QQueue<QQmlProfilerEvent> &starts = rangeStarts[i]; - if (starts.isEmpty()) - continue; - if (starts.head().timestamp() < minimumTime) { - minimumTime = starts.head().timestamp(); - minimum = i; - } - } - if (minimum == MaximumRangeType) - break; - - while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime) - sendEvent(pointEvents.dequeue()); - - sendEvent(rangeStarts[minimum].dequeue(), - rangeEnds[minimum].pop() - minimumTime); - } - }; - - for (const QQmlProfilerEvent &event : std::as_const(d->events)) { - const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); - - if (type.rangeType() != MaximumRangeType) { - QQueue<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()]; - switch (event.rangeStage()) { - case RangeStart: { - ++level; - starts.enqueue(event); - break; - } - case RangeEnd: { - QStack<qint64> &ends = rangeEnds[type.rangeType()]; - if (starts.size() > ends.size()) { - ends.push(event.timestamp()); - if (--level == 0) - sendPending(); - } - break; - } - default: - break; - } - } else { - if (level == 0) - sendEvent(event); - else - pointEvents.enqueue(event); - } - } - - for (int i = 0; i < MaximumRangeType; ++i) { - while (rangeEnds[i].size() < rangeStarts[i].size()) { - rangeEnds[i].push(d->traceEndTime); - --level; - } - } - - sendPending(); + DataIterator(d, std::move(sendEvent)).run(); stream.writeEndElement(); // profilerDataModel diff --git a/tools/shared/qqmltoolingsettings.cpp b/tools/shared/qqmltoolingsettings.cpp index be2f6ed8f8..d153f7daec 100644 --- a/tools/shared/qqmltoolingsettings.cpp +++ b/tools/shared/qqmltoolingsettings.cpp @@ -18,7 +18,9 @@ using namespace Qt::StringLiterals; void QQmlToolingSettings::addOption(const QString &name, QVariant defaultValue) { - m_values[name] = defaultValue; + if (defaultValue.isValid()) { + m_values[name] = defaultValue; + } } bool QQmlToolingSettings::read(const QString &settingsFilePath) |
