From a563d8e4445536ab352aff71d340c1bb02ae9867 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Thu, 1 Jun 2023 03:46:47 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Id6793fbdebd48e03c8aa999b532c9f5d497a8106 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 8c4c10388e..2235b58e95 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 16a7e8d3d55069d70ea414802821d259a733ab06 + ref: c89278f5dfc03c6d1f1a01d18d1281d0f36fdfe3 required: true ../tqtc-qtimageformats: - ref: d39028b6187769ab77a8deb8fd0e84502b8de46a + ref: d20f98bb06266455964f48219a507be76005a45d required: false ../tqtc-qtshadertools: - ref: 5bfc7b75b76787f7400617af842e17d4049b6f7e + ref: 486ea70c83d30710e103dc62552e8b785c512422 required: false ../tqtc-qtsvg: - ref: 47c817c8cfb542710f3d58d767337ac0aee50520 + ref: bb687edc02c3dcd9f46ff6e5069f7cd91a739864 required: false -- cgit v1.2.3 From 8a2d921b9f4bd74acb816ccf6e48631c53e13db1 Mon Sep 17 00:00:00 2001 From: Andreas Eliasson Date: Wed, 24 May 2023 11:01:14 +0200 Subject: Doc: Capitalize header file in code snippet Fixes: QTBUG-113584 Change-Id: I2910eba4710e527baf258176921774dfa16b1485 Reviewed-by: Amir Masoud Abdol Reviewed-by: Ulf Hermann (cherry picked from commit d0d3fcdab9e8cab6100498e3aec14741809c4444) Reviewed-by: Qt Cherry-pick Bot --- src/qml/doc/src/qtqml-writing-a-module.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/doc/src/qtqml-writing-a-module.qdoc b/src/qml/doc/src/qtqml-writing-a-module.qdoc index 6ad820552e..a3464926e1 100644 --- a/src/qml/doc/src/qtqml-writing-a-module.qdoc +++ b/src/qml/doc/src/qtqml-writing-a-module.qdoc @@ -116,7 +116,7 @@ You can use the \l Q_IMPORT_QML_PLUGIN macro to create a reference to this symbo Add the following code to the main.cpp: \badcode -#include +#include Q_IMPORT_QML_PLUGIN(ExtraModulePlugin) \endcode -- cgit v1.2.3 From afc1a9a5f5d0cf3af38cc6cae819464a789790fa Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Tue, 6 Jun 2023 05:18:20 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: If96251912fb176d675c1fd0d4f1ce37706c32800 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 2235b58e95..5b5c3ec982 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: c89278f5dfc03c6d1f1a01d18d1281d0f36fdfe3 + ref: 6db61454eab4815d73e6a85ca1347c9d8a6efd69 required: true ../tqtc-qtimageformats: - ref: d20f98bb06266455964f48219a507be76005a45d + ref: ab2d58abd81a374176f10bc3322443bb0213d398 required: false ../tqtc-qtshadertools: - ref: 486ea70c83d30710e103dc62552e8b785c512422 + ref: 3c37d17ec6fd203f142dac8c45827bd89a264fed required: false ../tqtc-qtsvg: - ref: bb687edc02c3dcd9f46ff6e5069f7cd91a739864 + ref: 303fe013a0aabe971504354ccedf2e4d61234bef required: false -- cgit v1.2.3 From 9811a9f82699b0271e19907e533ceccb8f87452c Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Tue, 13 Sep 2022 11:53:42 +0800 Subject: Doc: Fix mislabelled diagram for Context2D.arc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the text showed: Pi/2 -> Pi -> 3Pi/2 -> 2Pi But the curves showed: 3Pi/2 -> Pi -> Pi/2 -> 2Pi The curves are now drawn in a clockwise sequence to match the text. Change-Id: Ib8fe0c17c652a0f357d27372403440f4dd031516 Reviewed-by: Topi Reiniö (cherry picked from commit 0c615d61ba4705894c2d25c5fc7a2ed406bd5942) Reviewed-by: Qt Cherry-pick Bot --- src/quick/doc/images/qml-item-canvas-startAngle.png | Bin 5099 -> 5826 bytes src/quick/items/context2d/qquickcontext2d.cpp | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/doc/images/qml-item-canvas-startAngle.png b/src/quick/doc/images/qml-item-canvas-startAngle.png index bf82c3aa4b..7930284896 100644 Binary files a/src/quick/doc/images/qml-item-canvas-startAngle.png and b/src/quick/doc/images/qml-item-canvas-startAngle.png differ diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 0c65ac610a..bad2eff565 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -2404,8 +2404,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::Func \image qml-item-canvas-startAngle.png - The \a anticlockwise parameter is \c true for each arc in the figure above - because they are all drawn in the anticlockwise direction. + The \a anticlockwise parameter is \c false for each arc in the figure above + because they are all drawn in the clockwise direction. \sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D Context Standard for arc()} -- cgit v1.2.3 From 217584b1c13b64e99e2f33474e22015f574d4a3e Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Fri, 2 Jun 2023 11:37:09 +0800 Subject: Doc: Expand FolderListModel documentation * Document the data types exposed by FolderListModel * Fully-qualify the enum values of FolderListModel::sortField and mark default value * Mark read-only properties Change-Id: Ib829a69cf762f4aaf1e50f2e526f64da429e128b Reviewed-by: Ulf Hermann (cherry picked from commit 69a25c5d40bca46ad54d4e9b39778bd5257c6daa) Reviewed-by: Qt Cherry-pick Bot --- src/labs/folderlistmodel/qquickfolderlistmodel.cpp | 53 ++++++++++++---------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp index 28e97533e8..bf44bd6acd 100644 --- a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp @@ -252,16 +252,16 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path) Components access names and paths via the following roles: \list - \li \c fileName - \li \c filePath - \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15) - \li \c fileUrl (since Qt 5.15) - \li \c fileBaseName - \li \c fileSuffix - \li \c fileSize - \li \c fileModified - \li \c fileAccessed - \li \c fileIsDir + \li \c fileName (\c string) + \li \c filePath (\c string) + \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15) + \li \c fileUrl (\c url) (since Qt 5.15) + \li \c fileBaseName (\c string) + \li \c fileSuffix (\c string) + \li \c fileSize (\c qlonglong) + \li \c fileModified (\c date) + \li \c fileAccessed (\c date) + \li \c fileIsDir (\c bool) \endlist Additionally a file entry can be differentiated from a folder entry via the @@ -395,6 +395,7 @@ QHash QQuickFolderListModel::roleNames() const /*! \qmlproperty int FolderListModel::count + \readonly Returns the number of items in the current folder that match the filter criteria. @@ -493,6 +494,7 @@ void QQuickFolderListModel::setRootFolder(const QUrl &path) /*! \qmlproperty url FolderListModel::parentFolder + \readonly Returns the URL of the parent of the current \l folder. */ @@ -566,11 +568,11 @@ void QQuickFolderListModel::componentComplete() The \a sortField property contains the field to use for sorting. \c sortField may be one of: - \value Unsorted no sorting is applied - \value Name sort by filename - \value Time sort by time modified - \value Size sort by file size - \value Type sort by file type (extension) + \value FolderListModel.Unsorted no sorting is applied + \value FolderListModel.Name sort by filename (default) + \value FolderListModel.Time sort by time modified + \value FolderListModel.Size sort by file size + \value FolderListModel.Type sort by file type/extension \sa sortReversed */ @@ -818,6 +820,7 @@ void QQuickFolderListModel::setCaseSensitive(bool on) /*! \qmlproperty enumeration FolderListModel::status \since 5.11 + \readonly This property holds the status of folder reading. It can be one of: @@ -884,16 +887,16 @@ void QQuickFolderListModel::setSortCaseSensitive(bool on) are available: \list - \li \c fileName - \li \c filePath - \li \c fileURL (since Qt 5.2; deprecated since Qt 5.15) - \li \c fileUrl (since Qt 5.15) - \li \c fileBaseName - \li \c fileSuffix - \li \c fileSize - \li \c fileModified - \li \c fileAccessed - \li \c fileIsDir + \li \c fileName (\c string) + \li \c filePath (\c string) + \li \c fileURL (\c url) (since Qt 5.2; deprecated since Qt 5.15) + \li \c fileUrl (\c url) (since Qt 5.15) + \li \c fileBaseName (\c string) + \li \c fileSuffix (\c string) + \li \c fileSize (\c qlonglong) + \li \c fileModified (\c date) + \li \c fileAccessed (\c date) + \li \c fileIsDir (\c bool) \endlist */ QVariant QQuickFolderListModel::get(int idx, const QString &property) const -- cgit v1.2.3 From d43b0db1219ffed50eebfef9076c20b68e66328c Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 5 Jun 2023 14:18:36 +0800 Subject: Doc: explain how to enable mirroring for right-to-left applications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Default Layout Direction" explains how to set the default layout direction, but leaves out that the LayoutMirroring attached QML API must be used to ensure that the UI is actually mirrored. Similar code was added elsewhere in 416e3f080fa71249fd5be417d6abef5817f2197d. Fixes: QTBUG-113497 Change-Id: I6a2f817131499f111386109d0e4b49c232cccfbf Reviewed-by: Topi Reiniö Reviewed-by: Fabian Kosmale (cherry picked from commit 5ed4c2320a546e12cfc3d77cd7ec2a05c8c855c4) Reviewed-by: Qt Cherry-pick Bot --- src/quick/doc/src/concepts/positioning/righttoleft.qdoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc index 574dfcb58f..da96d31258 100644 --- a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc +++ b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc @@ -171,6 +171,16 @@ This will append the following declaration to the translation file, where you ca \endcode +Next, add the following bindings to the root QML component of your application: +\code +LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft +LayoutMirroring.childrenInherit: true +\endcode + +The first binding ensures that the UI will be mirrored appropriately when a +right-to-left locale is set. The second binding ensures that child items of the +root component will also respect mirroring. + You can test that the layout direction works as expected by running your Qt Quick application with the compiled translation file: -- cgit v1.2.3 From 3d547b7b7173ab5e669710a48d0de3f7dfe916aa Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 16 May 2023 06:54:55 +0200 Subject: doc: Clarify usage of palette; testable snippets - color groups are better shown with copyable monospace syntax rather than capitalized - clarify that using palette grouped-property syntax does not imply that you must define every color - Window has a palette property, inited from SystemPalette: it's not only ApplicationWindow - testable snippets - reuse part of the windowPalette snippet for the Window.active property (switching colors might be the main use case) - make more links Change-Id: I5974351ba66fc9aeec34cb6a4aba1eb85289f536 Reviewed-by: Oliver Eftevaag (cherry picked from commit f1e7e1c3987b905157a1b9b418ea2649100eb390) --- src/quick/doc/snippets/qml/windowPalette.qml | 85 ++++++++++++++++++++++ src/quick/items/qquickpalette.cpp | 27 +++---- src/quick/items/qquickwindow.cpp | 8 +- src/quick/util/qquicksystempalette.cpp | 4 +- .../qtquickcontrols-custom-palette-buttons.qml | 67 +++++++++++++++++ 5 files changed, 174 insertions(+), 17 deletions(-) create mode 100644 src/quick/doc/snippets/qml/windowPalette.qml create mode 100644 src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml diff --git a/src/quick/doc/snippets/qml/windowPalette.qml b/src/quick/doc/snippets/qml/windowPalette.qml new file mode 100644 index 0000000000..c566d61dbb --- /dev/null +++ b/src/quick/doc/snippets/qml/windowPalette.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//![entire] +import QtQuick +import QtQuick.Controls + +//![declaration-and-color] +Window { + visible: true + + // here we use the Window.active and Window.palette ordinary properties + color: active ? palette.active.window : palette.inactive.window +//![declaration-and-color] + + // colors that are not customized here come from SystemPalette + palette.active.window: "peachpuff" + palette.windowText: "brown" + + Text { + anchors.centerIn: parent + // here we use the Window.active attached property and the Item.palette property + color: Window.active ? palette.active.windowText : palette.inactive.windowText + text: Window.active ? "active" : "inactive" + } + + Button { + text: "Button" + anchors { + bottom: parent.bottom + bottomMargin: 6 + horizontalCenter: parent.horizontalCenter + } + } +//![closing-brace] +} +//![closing-brace] +//![entire] diff --git a/src/quick/items/qquickpalette.cpp b/src/quick/items/qquickpalette.cpp index a717864441..814634fe31 100644 --- a/src/quick/items/qquickpalette.cpp +++ b/src/quick/items/qquickpalette.cpp @@ -61,8 +61,8 @@ QT_BEGIN_NAMESPACE \ingroup qtquick-visual \brief The QQuickPalette class contains color groups for each QML item state. - A palette consists of three color groups: Active, Disabled, and Inactive. - Active color group is the default group, its colors are used for other groups + A palette consists of three color groups: \c active, \c disabled, and \c inactive. + The \c active color group is the default group: its colors are used for other groups if colors of these groups aren't explicitly specified. In the following example, color is applied for all color groups: @@ -104,18 +104,19 @@ QT_BEGIN_NAMESPACE \endcode It is also possible to specify colors like this: - \code - palette { - buttonText: "azure" - button: "khaki" - disabled { - buttonText: "lavender" - button: "coral" - } - } - \endcode - This approach is convenient when you need to specify a whole palette with all color groups. + \snippet qtquickcontrols-custom-palette-buttons.qml palette + + This approach is especially convenient when you need to specify a whole + palette with all color groups; but as with the other cases above, the + colors that are not specified are initialized from SystemPalette, or + potentially the \l {Styling Qt Quick Controls}{Qt Quick Controls style}, + if one is in use. + + \note Some Controls styles use some palette colors, but many styles use + independent colors. + + \sa Window::palette, Item::palette, Popup::palette, SystemPalette */ /*! diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f8ba2119fc..6c1c98208a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3522,6 +3522,9 @@ void QQuickWindow::endExternalCommands() The active status of the window. + \snippet qml/windowPalette.qml declaration-and-color + \snippet qml/windowPalette.qml closing-brace + \sa requestActivate() */ @@ -4150,10 +4153,11 @@ void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType) palette which serves as a default for all application windows. You can also set the default palette for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML. - ApplicationWindow propagates explicit palette properties to child controls. If you change a specific - property on the window's palette, that property propagates to all child controls in the window, + Window propagates explicit palette properties to child items and controls, overriding any system defaults for that property. + \snippet qml/windowPalette.qml entire + \sa Item::palette, Popup::palette, ColorGroup, SystemPalette //! internal \sa QQuickAbstractPaletteProvider, QQuickPalette */ diff --git a/src/quick/util/qquicksystempalette.cpp b/src/quick/util/qquicksystempalette.cpp index 67c006622b..1f40af78d2 100644 --- a/src/quick/util/qquicksystempalette.cpp +++ b/src/quick/util/qquicksystempalette.cpp @@ -63,8 +63,8 @@ public: The SystemPalette type provides access to the Qt application palettes. This provides information about the standard colors used for application windows, buttons and other features. These colors - are grouped into three \e {color groups}: \c Active, \c Inactive, - and \c Disabled. See the QPalette documentation for details about + are grouped into three \e {color groups}: \c active, \c inactive, + and \c disabled. See the QPalette documentation for details about color groups and the properties provided by SystemPalette. This can be used to color items in a way that provides a more diff --git a/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml b/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml new file mode 100644 index 0000000000..82180fe465 --- /dev/null +++ b/src/quickcontrols2/doc/snippets/qtquickcontrols-custom-palette-buttons.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//![entire] +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +ApplicationWindow { + visible: true + + //![palette] + palette { + buttonText: "red" + button: "khaki" + + disabled { + buttonText: "lavender" + button: "coral" + } + } + //![palette] + + ColumnLayout { + id: layout + anchors.fill: parent + anchors.margins: 3 + Button { + text: qsTr("Disabled button") + enabled: false + } + + Button { + text: qsTr("Enabled button") + } + + TextField { + Layout.fillWidth: true + placeholderText: "type something here" + } + } +} +//![entire] -- cgit v1.2.3 From b6f777824e5bdfbbad50dd84ad205e53ce54ba01 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 13 Jun 2023 08:29:52 +0200 Subject: CMake: Remove superfluous depfile in QML type registration In _qt_internal_qml_type_registration we created a depfile that contained the *_metatypes.json files of target's SOURCES. This depfile was used in the custom command that produces ${target}_qmltyperegistration.cpp. It doesn't seem necessary to create a depfile, and one could use the DEPENDS option of add_custom_command instead. And indeed, commit ce950d619aef3ad0534544b2e029a6f025b256a5 added the same dependencies this way. Presumably, this was done to fix dependencies for older CMake versions and generators that do not support depfiles. This commit removes the creation of the depfile. This double dependency declaration was found when investigating QTBUG-106683, and it turns out that removing the depfile fixes the problem of re-generating ${target}_qmltyperegistration.cpp for Visual Studio generators. Task-number: QTBUG-106683 Change-Id: I6cf909dfb4a9b31b76599b7ad35ad6f595ca0891 Reviewed-by: Alexey Edelev Reviewed-by: Ulf Hermann Reviewed-by: Fabian Kosmale (cherry picked from commit 470fee3217794c66f0eaf19f1a3cb229cfb85fc1) --- src/qml/Qt6QmlMacros.cmake | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index bda7b9b83a..764be448fb 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -1822,28 +1822,6 @@ function(_qt_internal_qml_type_registration target) message(FATAL_ERROR "Need target metatypes.json file") endif() - cmake_policy(PUSH) - - set(registration_cpp_file_dep_args) - if (CMAKE_GENERATOR MATCHES "Ninja" OR - (CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles")) - if(POLICY CMP0116) - # Without explicitly setting this policy to NEW, we get a warning - # even though we ensure there's actually no problem here. - # See https://gitlab.kitware.com/cmake/cmake/-/issues/21959 - cmake_policy(SET CMP0116 NEW) - set(relative_to_dir ${CMAKE_CURRENT_BINARY_DIR}) - else() - set(relative_to_dir ${CMAKE_BINARY_DIR}) - endif() - set(dependency_file_cpp "${target_binary_dir}/qmltypes/${type_registration_cpp_file_name}.d") - set(registration_cpp_file_dep_args DEPFILE ${dependency_file_cpp}) - file(RELATIVE_PATH cpp_file_name "${relative_to_dir}" "${type_registration_cpp_file}") - file(GENERATE OUTPUT "${dependency_file_cpp}" - CONTENT "${cpp_file_name}: $,\\\n$, \\\n>" - ) - endif() - add_custom_command( OUTPUT ${type_registration_cpp_file} @@ -1863,12 +1841,9 @@ function(_qt_internal_qml_type_registration target) ${CMAKE_COMMAND} -E make_directory "${generated_marker_dir}" COMMAND ${CMAKE_COMMAND} -E touch "${generated_marker_file}" - ${registration_cpp_file_dep_args} COMMENT "Automatic QML type registration for target ${target}" ) - cmake_policy(POP) - # The ${target}_qmllint targets need to depend on the generation of all # *.qmltypes files in the build. We have no way of reliably working out # which QML modules a given target depends on at configure time, so we -- cgit v1.2.3 From bdf7e59e7651c0c55ed5136669c4861a8d2efd8d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 14 Jun 2023 12:50:27 +0200 Subject: QQmlPropertyBinding: Re-fetch binding data after reset() Fixes: QTBUG-114430 Change-Id: I13773e7eba3603350b492c7649102a8131a9829c Reviewed-by: Fabian Kosmale (cherry picked from commit 662c3c3545275f412eb2cf7b9baef6003757d128) Reviewed-by: Qt Cherry-pick Bot --- src/qml/qml/qqmlpropertybinding.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp index 3fcdc92d03..fc7d37e4be 100644 --- a/src/qml/qml/qqmlpropertybinding.cpp +++ b/src/qml/qml/qqmlpropertybinding.cpp @@ -263,10 +263,17 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void setIsUndefined(true); //suspend binding evaluation state for reset and subsequent read auto state = QtPrivate::suspendCurrentBindingStatus(); - prop.reset(); + prop.reset(); // May re-allocate the bindingData QVariant currentValue = QVariant(prop.propertyMetaType(), propertyDataPtr); QtPrivate::restoreBindingStatus(state); writeBackCurrentValue(std::move(currentValue)); + + // Re-fetch binding data + bindingData = storage->bindingData(propertyDataPtr); + if (!bindingData) + bindingData = bindingDataFromPropertyData(propertyDataPtr, propertyData->propType()); + bindingDataPointer = QPropertyBindingDataPointer {bindingData}; + // reattach the binding (without causing a new notification) if (Q_UNLIKELY(bindingData->d() & QtPrivate::QPropertyBindingData::BindingBit)) { qCWarning(lcQQPropertyBinding) << "Resetting " << prop.name() << "due to the binding becoming undefined caused a new binding to be installed\n" -- cgit v1.2.3 From 9e5596570267571d1f0c01ce85f56c0f2cb8e704 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Sat, 17 Jun 2023 14:31:31 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Id7969f28857f16668c63212b33bad0c67390e764 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 5b5c3ec982..1a5e9e4d6e 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 6db61454eab4815d73e6a85ca1347c9d8a6efd69 + ref: bcde7eeb01b922b2037bde75ef71dcf24222080f required: true ../tqtc-qtimageformats: - ref: ab2d58abd81a374176f10bc3322443bb0213d398 + ref: d6f6c088637b8fbf6ed1202c22bb03c0a4854543 required: false ../tqtc-qtshadertools: - ref: 3c37d17ec6fd203f142dac8c45827bd89a264fed + ref: b61522dfe7d843e89f1bd335a28a11ff8b23843b required: false ../tqtc-qtsvg: - ref: 303fe013a0aabe971504354ccedf2e4d61234bef + ref: f80df57e55fcb3ceb8dc7a4754113d4de8bf741d required: false -- cgit v1.2.3 From 4d209a4556b71c0d51c112241d6eebb5574988b0 Mon Sep 17 00:00:00 2001 From: Tarja Sundqvist Date: Wed, 14 Jun 2023 13:48:12 +0300 Subject: Bump version to 6.2.10 Change-Id: I41e32bd4d54c49acac4e8419a6926f1c09212c72 Reviewed-by: Tarja Sundqvist --- .cmake.conf | 2 +- .qmake.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.cmake.conf b/.cmake.conf index beccb22d04..31a36e3b07 100644 --- a/.cmake.conf +++ b/.cmake.conf @@ -1,2 +1,2 @@ -set(QT_REPO_MODULE_VERSION "6.2.9") +set(QT_REPO_MODULE_VERSION "6.2.10") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "") diff --git a/.qmake.conf b/.qmake.conf index 1763149193..4c0875fa17 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -5,4 +5,4 @@ DEFINES += QT_NO_JAVA_STYLE_ITERATORS QQC2_SOURCE_TREE = $$PWD -MODULE_VERSION = 6.2.9 +MODULE_VERSION = 6.2.10 -- cgit v1.2.3 From 3d7a02a02f1f3959a9d3b2576f0389744155026a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 26 May 2023 13:24:23 +0200 Subject: Remove DA::deliverMatchingPointsToItem's second sendFilteredPointerEvent a2209698d3584a7c05d0c12aa61de050fe0e78fd added sendFilteredPointerEvent earlier in this function; it doesn't make sense to do it multiple times. Fixes: QTBUG-109995 Fixes: QTBUG-113653 Change-Id: I9a9cb36ba060ec924ec3467fff0d7b0e3d474da3 Reviewed-by: Doris Verria (cherry picked from commit 2cb75d3126f0514701475077c719b5ea5ad9864e) Reviewed-by: Shawn Rutledge --- src/quick/util/qquickdeliveryagent.cpp | 5 -- .../quick/qquicklistview2/tst_qquicklistview2.cpp | 31 +++++++++---- .../tst_qquickmultipointtoucharea.cpp | 53 ++++++++-------------- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 2 +- 4 files changed, 41 insertions(+), 50 deletions(-) diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index 894f52ce3d..804acf3669 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -2116,11 +2116,6 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b if (item->acceptTouchEvents()) { qCDebug(lcTouch) << "considering delivering" << &touchEvent << " to " << item; - // If any parent filters the event, we're done. - hasFiltered.clear(); - if (sendFilteredPointerEvent(&touchEvent, item)) - return; - // Deliver the touch event to the given item qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item; QCoreApplication::sendEvent(item, &touchEvent); diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp index 4c6f9c70e2..fb3ad3a93f 100644 --- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp +++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp @@ -260,29 +260,31 @@ void tst_QQuickListView2::tapDelegateDuringFlicking_data() { QTest::addColumn("qmlFile"); QTest::addColumn("boundsBehavior"); + QTest::addColumn("expectCanceled"); QTest::newRow("Button StopAtBounds") << QByteArray("buttonDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) << false; QTest::newRow("MouseArea StopAtBounds") << QByteArray("mouseAreaDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) << true; QTest::newRow("Button DragOverBounds") << QByteArray("buttonDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) << false; QTest::newRow("MouseArea DragOverBounds") << QByteArray("mouseAreaDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) << true; QTest::newRow("Button OvershootBounds") << QByteArray("buttonDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) << false; QTest::newRow("MouseArea OvershootBounds") << QByteArray("mouseAreaDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) << true; QTest::newRow("Button DragAndOvershootBounds") << QByteArray("buttonDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) << false; QTest::newRow("MouseArea DragAndOvershootBounds") << QByteArray("mouseAreaDelegate.qml") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds); + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) << true; } void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832 { QFETCH(QByteArray, qmlFile); QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior); + QFETCH(bool, expectCanceled); QQuickView window; QVERIFY(QQuickTest::showView(window, testFileUrl(qmlFile.constData()))); @@ -295,7 +297,16 @@ void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832 QVERIFY(listView->isFlicking()); // we want to test the case when it's still moving while we tap // @y = 400 we pressed the 4th delegate; started flicking, and the press was canceled QCOMPARE(listView->property("pressedDelegates").toList().first(), 4); - QCOMPARE(listView->property("canceledDelegates").toList().first(), 4); + // At first glance one would expect MouseArea and Button would be consistent about this; + // but in fact, before ListView takes over the grab via filtering, + // Button.pressed transitions to false because QQuickAbstractButtonPrivate::handleMove + // sees that the touchpoint has strayed outside its bounds, but it does NOT emit the canceled signal + if (expectCanceled) { + const QVariantList canceledDelegates = listView->property("canceledDelegates").toList(); + QCOMPARE(canceledDelegates.size(), 1); + QCOMPARE(canceledDelegates.first(), 4); + } + QCOMPARE(listView->property("releasedDelegates").toList().size(), 0); // press a delegate during flicking (at y > 501 + 100, so likely delegate 6) QTest::touchEvent(&window, touchDevice.data()).press(0, {100, 100}); @@ -318,7 +329,7 @@ void tst_QQuickListView2::tapDelegateDuringFlicking() // QTBUG-103832 QVERIFY(lastPressed > 5); QCOMPARE(releasedDelegates.last(), lastPressed); QCOMPARE(tappedDelegates.last(), lastPressed); - QCOMPARE(canceledDelegates.count(), 1); // only the first press was canceled, not the second + QCOMPARE(canceledDelegates.size(), expectCanceled ? 1 : 0); // only the first press was canceled, not the second } void tst_QQuickListView2::flickDuringFlicking_data() diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 299241b768..44b5920cdf 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -694,12 +694,16 @@ void tst_QQuickMultiPointTouchArea::inFlickable() // test that dragging out of a Flickable containing a MPTA doesn't harm Flickable's state. void tst_QQuickMultiPointTouchArea::inFlickable2() { + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); QScopedPointer window(createAndShowView("inFlickable2.qml")); QVERIFY(window->rootObject() != nullptr); QQuickFlickable *flickable = window->rootObject()->findChild("flickable"); QVERIFY(flickable != nullptr); + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild(); + QVERIFY(mpta); + QQuickTouchPoint *point11 = window->rootObject()->findChild("point1"); QVERIFY(point11); @@ -712,25 +716,12 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QQuickTouchUtils::flush(window.data()); QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1); - p1 += QPoint(15,0); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); - QTest::mouseMove(window.data(), p1); - - p1 += QPoint(15,0); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); - QTest::mouseMove(window.data(), p1); - - p1 += QPoint(15,0); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); - QTest::mouseMove(window.data(), p1); - - p1 += QPoint(15,0); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); - QTest::mouseMove(window.data(), p1); + for (int i = 0; i < 4; ++i) { + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); + QTest::mouseMove(window.data(), p1); + } QVERIFY(!flickable->isMoving()); QVERIFY(point11->pressed()); @@ -743,27 +734,21 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QTRY_VERIFY(!flickable->isMoving()); // Check that we can still move the Flickable + QSignalSpy gestureStartedSpy(mpta, &QQuickMultiPointTouchArea::gestureStarted); p1 = QPoint(50,100); QTest::touchEvent(window.data(), device).press(0, p1); QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); - p1 += QPoint(0,15); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); - - p1 += QPoint(0,15); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); - - p1 += QPoint(0,15); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); - - p1 += QPoint(0,15); - QTest::touchEvent(window.data(), device).move(0, p1); - QQuickTouchUtils::flush(window.data()); + for (int i = 0; i < 4; ++i) { + p1 += QPoint(0, dragThreshold); + QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); + // QTBUG-113653: gestureStarted is emitted when touch delta exceeds drag threshold, + // regardless of the filtering Flickable parent + QCOMPARE(gestureStartedSpy.size(), i > 0 ? 1 : 0); + } QVERIFY(flickable->contentY() < 0); QVERIFY(flickable->isMoving()); diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 66dde3e26c..c80ceddcc3 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -738,7 +738,7 @@ void tst_TouchMouse::touchButtonOnFlickable() QTRY_COMPARE(eventItem2->touchUngrabCount, 1); qCDebug(lcTests) << "expected delivered events: press(touch) move(touch)" << eventItem2->eventList; - QCOMPARE(eventItem2->eventList.size(), 2); + QCOMPARE(eventItem2->eventList.size(), 3); QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate); QCOMPARE(grabMonitor.exclusiveGrabber, flickable); // both EventItem and Flickable handled the actual touch, so synth-mouse doesn't happen -- cgit v1.2.3 From cb1b1b5f94391d09821aafe731d630e1f3acbe58 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 16 May 2023 06:54:55 +0200 Subject: doc: Make Window snippets testable; remove import versions Change-Id: I35ba013152628591d016c632828a1c4e64cf5978 Reviewed-by: Oliver Eftevaag (cherry picked from commit 78d2cec488f8ab4ab45433ae9a9039f9cb7a5771) Reviewed-by: Shawn Rutledge --- .../snippets/qml/nestedWindowTransientParent.qml | 70 ++++++++++++++++++++++ .../doc/snippets/qml/windowActiveAttached.qml | 57 ++++++++++++++++++ src/quick/items/qquickwindow.cpp | 22 +------ 3 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 src/quick/doc/snippets/qml/nestedWindowTransientParent.qml create mode 100644 src/quick/doc/snippets/qml/windowActiveAttached.qml diff --git a/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml new file mode 100644 index 0000000000..1946ee4af0 --- /dev/null +++ b/src/quick/doc/snippets/qml/nestedWindowTransientParent.qml @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick + +//![0] +Window { + // visible is false by default + Window { + transientParent: null + visible: true + } +//![0] + + id: outer + Timer { + interval: 2000 + running: true + onTriggered: outer.visible = true + } +//![1] +} +//![1] diff --git a/src/quick/doc/snippets/qml/windowActiveAttached.qml b/src/quick/doc/snippets/qml/windowActiveAttached.qml new file mode 100644 index 0000000000..9d2ddc2b99 --- /dev/null +++ b/src/quick/doc/snippets/qml/windowActiveAttached.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//![entire] +import QtQuick + +Text { + text: Window.active ? "active" : "inactive" +} +//![entire] diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 6c1c98208a..ee11b94c57 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3466,17 +3466,8 @@ void QQuickWindow::endExternalCommands() Item or Window within which it was declared, you can remove that relationship by setting \c transientParent to \c null: - \qml - import QtQuick.Window 2.13 - - Window { - // visible is false by default - Window { - transientParent: null - visible: true - } - } - \endqml + \snippet qml/nestedWindowTransientParent.qml 0 + \snippet qml/nestedWindowTransientParent.qml 1 In order to cause the window to be centered above its transient parent by default, depending on the window manager, it may also be necessary to set @@ -3538,14 +3529,7 @@ void QQuickWindow::endExternalCommands() Here is an example which changes a label to show the active state of the window in which it is shown: - \qml - import QtQuick 2.4 - import QtQuick.Window 2.2 - - Text { - text: Window.active ? "active" : "inactive" - } - \endqml + \snippet qml/windowActiveAttached.qml entire */ /*! -- cgit v1.2.3 From 498b7dbf609453cc5b36653722f393ae1ce7775b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 16 May 2023 10:51:36 +0200 Subject: doc: Use part of window example for Window.flags property snippet Change-Id: I5b5c1663f626ce3bfe4c01edbc5480729a1b91f6 Reviewed-by: Oliver Eftevaag (cherry picked from commit 94475c9e533dc8b05c606c92b91ab2b645960ac0) Reviewed-by: Shawn Rutledge --- src/quick/doc/snippets/qml/images/qt-logo.png | Bin 0 -> 6208 bytes src/quick/doc/snippets/qml/splashWindow.qml | 104 ++++++++++++++++++++++++++ src/quick/items/qquickwindow.cpp | 4 +- 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/quick/doc/snippets/qml/images/qt-logo.png create mode 100644 src/quick/doc/snippets/qml/splashWindow.qml diff --git a/src/quick/doc/snippets/qml/images/qt-logo.png b/src/quick/doc/snippets/qml/images/qt-logo.png new file mode 100644 index 0000000000..30c621c9c6 Binary files /dev/null and b/src/quick/doc/snippets/qml/images/qt-logo.png differ diff --git a/src/quick/doc/snippets/qml/splashWindow.qml b/src/quick/doc/snippets/qml/splashWindow.qml new file mode 100644 index 0000000000..17d4c674e3 --- /dev/null +++ b/src/quick/doc/snippets/qml/splashWindow.qml @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [entire] +import QtQuick + +Window { + id: mainWindow + title: "Main Window" + color: "#456" + property real defaultSpacing: 10 + + property Splash splash: Splash { + onTimeout: mainWindow.show() + } + + component Splash: Window { + id: splash + + // a splash screen has no titlebar + flags: Qt.SplashScreen + // the transparent color lets background behind the image edges show through + color: "transparent" + modality: Qt.ApplicationModal // in case another application window is showing + title: "Splash Window" // for the taskbar/dock, task switcher etc. + visible: true + + // here we use the Screen attached property to center the splash window + //! [screen-properties] + x: (Screen.width - splashImage.width) / 2 + y: (Screen.height - splashImage.height) / 2 + //! [screen-properties] + width: splashImage.width + height: splashImage.height + + property int timeoutInterval: 2000 + signal timeout + + Image { + id: splashImage + source: "images/qt-logo.png" + } + + TapHandler { + onTapped: splash.timeout() + } + + Timer { + interval: splash.timeoutInterval; running: true; repeat: false + onTriggered: { + splash.visible = false + splash.timeout() + } + } + } +} +//! [entire] diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ee11b94c57..634561f04e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3269,9 +3269,11 @@ void QQuickWindow::endExternalCommands() whether it's a dialog, popup, or a regular window, and whether it should have a title bar, etc. - The flags which you read from this property might differ from the ones + The flags that you read from this property might differ from the ones that you set if the requested flags could not be fulfilled. + \snippet qml/splashWindow.qml entire + \sa Qt::WindowFlags */ -- cgit v1.2.3 From 9445aaf9611b75a8152ded5ab95193fd1e696527 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 16 Jun 2023 11:33:30 +0200 Subject: QML: Guard QProperty change triggers against deletion of target Previously, we relied on QObject* pointers being unique even after deletion of the objects. That's not good. Fixes: QTBUG-114329 Change-Id: Ia0a2c1d2cb5d8a0d47ec00e73424c959c59c09bc Reviewed-by: Fabian Kosmale (cherry picked from commit 46842ec7c67ce16ade08076c0fd7e78e96f33887) --- src/qml/qml/qqmlengine_p.h | 13 ++++-- src/qml/qml/qqmljavascriptexpression.cpp | 46 +++++++++++++------- src/qml/qml/qqmljavascriptexpression_p.h | 2 + .../data/invalidateQPropertyChangeTriggers.qml | 50 ++++++++++++++++++++++ tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 29 +++++++++++++ 5 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 257b5371b6..3e52b3a166 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -130,9 +130,16 @@ public: }; struct QPropertyChangeTrigger : QPropertyObserver { - QPropertyChangeTrigger(QQmlJavaScriptExpression *expression) : QPropertyObserver(&QPropertyChangeTrigger::trigger), m_expression(expression) {} - QQmlJavaScriptExpression * m_expression; - QObject *target = nullptr; + Q_DISABLE_COPY_MOVE(QPropertyChangeTrigger) + + QPropertyChangeTrigger(QQmlJavaScriptExpression *expression) + : QPropertyObserver(&QPropertyChangeTrigger::trigger) + , m_expression(expression) + { + } + + QPointer target; + QQmlJavaScriptExpression *m_expression; int propertyIndex = 0; static void trigger(QPropertyObserver *, QUntypedPropertyData *); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 0a039efa1d..0c9daa9a64 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -390,19 +390,35 @@ void QQmlPropertyCapture::captureProperty( captureNonBindableProperty(o, propertyData->notifyIndex(), propertyData->coreIndex(), doNotify); } +bool QQmlJavaScriptExpression::needsPropertyChangeTrigger(QObject *target, int propertyIndex) +{ + TriggerList **prev = &qpropertyChangeTriggers; + TriggerList *current = qpropertyChangeTriggers; + while (current) { + if (!current->target) { + *prev = current->next; + QRecyclePool::Delete(current); + current = *prev; + } else if (current->target == target && current->propertyIndex == propertyIndex) { + return false; // already installed + } else { + prev = ¤t->next; + current = current->next; + } + } + + return true; +} + void QQmlPropertyCapture::captureTranslation() { // use a unique invalid index to avoid needlessly querying the metaobject for // the correct index of of the translationLanguage property int const invalidIndex = -2; - for (auto trigger = expression->qpropertyChangeTriggers; trigger; - trigger = trigger->next) { - if (trigger->target == engine && trigger->propertyIndex == invalidIndex) - return; // already installed + if (expression->needsPropertyChangeTrigger(engine, invalidIndex)) { + auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex); + trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage); } - auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex); - - trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage); } void QQmlPropertyCapture::captureBindableProperty( @@ -412,16 +428,14 @@ void QQmlPropertyCapture::captureBindableProperty( // the automatic capturing process already takes care of everything if (!expression->mustCaptureBindableProperty()) return; - for (auto trigger = expression->qpropertyChangeTriggers; trigger; - trigger = trigger->next) { - if (trigger->target == o && trigger->propertyIndex == c) - return; // already installed + + if (expression->needsPropertyChangeTrigger(o, c)) { + auto trigger = expression->allocatePropertyChangeTrigger(o, c); + QUntypedBindable bindable; + void *argv[] = { &bindable }; + metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv); + bindable.observe(trigger); } - auto trigger = expression->allocatePropertyChangeTrigger(o, c); - QUntypedBindable bindable; - void *argv[] = { &bindable }; - metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv); - bindable.observe(trigger); } void QQmlPropertyCapture::captureNonBindableProperty(QObject *o, int n, int c, bool doNotify) diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index ed83a3e4c1..01ea7ba396 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -166,6 +166,8 @@ public: QQmlEngine *engine() const { return m_context ? m_context->engine() : nullptr; } bool hasUnresolvedNames() const { return m_context && m_context->hasUnresolvedNames(); } + + bool needsPropertyChangeTrigger(QObject *target, int propertyIndex); QPropertyChangeTrigger *allocatePropertyChangeTrigger(QObject *target, int propertyIndex); protected: diff --git a/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml new file mode 100644 index 0000000000..bfa832c1c8 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/invalidateQPropertyChangeTriggers.qml @@ -0,0 +1,50 @@ +import QtQml + +QtObject { + id: root + objectName: column.text + + property Component c: Component { + id: comp + QtObject { } + } + + property QtObject rectItem: null + + property bool running: false + + property Timer t: Timer { + id: column + interval: 200 + running: root.running + repeat: true + + property string text: { + let item = root.rectItem + let result = rectItem ? rectItem.objectName : "Create Object" + return result + } + + onTriggered: { + let rectItem = root.rectItem + + // If rectItem exists destory it. + if (rectItem) { + rectItem.destroy() + return + } + + // Otherwise create a new object + let newRectItem = comp.createObject(column, {}) + + + // Setting the objectName before setting root.rectItem seems to work. + // newRectItem.width = 1200 + root.rectItem = newRectItem + + // But setting the objectName after setting root.rectItem seems to + // cause the issue. + newRectItem.objectName = "1300" + } + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 7ea9b4183e..896150b33e 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -206,6 +206,8 @@ private slots: void bindToNonQObjectTarget(); + void invalidateQPropertyChangeTriggers(); + private: QQmlEngine engine; }; @@ -2376,6 +2378,33 @@ void tst_qqmlproperty::bindToNonQObjectTarget() QVERIFY(!o.isNull()); } +void tst_qqmlproperty::invalidateQPropertyChangeTriggers() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("invalidateQPropertyChangeTriggers.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer root(component.create()); + QVERIFY(!root.isNull()); + + QStringList names; + QObject::connect(root.data(), &QObject::objectNameChanged, [&](const QString &name) { + if (names.length() == 10) + root->setProperty("running", false); + else + names.append(name); + }); + + root->setProperty("running", true); + QTRY_VERIFY(!root->property("running").toBool()); + + QCOMPARE(names, (QStringList { + u""_qs, u"1300"_qs, u"Create Object"_qs, + u""_qs, u"1300"_qs, u"Create Object"_qs, + u""_qs, u"1300"_qs, u"Create Object"_qs, + u""_qs + })); +} + QTEST_MAIN(tst_qqmlproperty) #include "tst_qqmlproperty.moc" -- cgit v1.2.3 From 03dbf7ff1453c4b1d6385b73d89345b0fad8fd93 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 16 Jun 2023 13:50:40 +0200 Subject: QmlCompiler: Do not recurse when looking for builtins.qmltypes A builtins.qmltypes in some subdirectory is generally not something we want to use. Change-Id: Ic774800048917f6b61e3343fcb7d12b00bcee9bd Reviewed-by: Fabian Kosmale (cherry picked from commit 74fac24a2785af1fe7a3252b1c804c762ede400b) --- src/qmlcompiler/qqmljsimporter.cpp | 2 +- .../qmllint/data/bad_builtins/builtins.qmltypes | 513 +++++++++++++++++++++ 2 files changed, 514 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp index 3a9c1f8fc9..191bb4c75d 100644 --- a/src/qmlcompiler/qqmljsimporter.cpp +++ b/src/qmlcompiler/qqmljsimporter.cpp @@ -403,7 +403,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper() QStringLiteral("jsroot.qmltypes") }; const auto importBuiltins = [&](const QStringList &imports) { for (auto const &dir : imports) { - QDirIterator it { dir, qmltypesFiles, QDir::NoFilter, QDirIterator::Subdirectories }; + QDirIterator it { dir, qmltypesFiles, QDir::NoFilter }; while (it.hasNext() && !qmltypesFiles.isEmpty()) { readQmltypes(it.next(), &result.objects, &result.dependencies); qmltypesFiles.removeOne(it.fileName()); diff --git a/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes new file mode 100644 index 0000000000..96f98dd14e --- /dev/null +++ b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes @@ -0,0 +1,513 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -builtins' + +Module { + dependencies: [] + Component { + name: "Qt" + Enum { + name: "GlobalColor" + values: { + "color0": 0, + "color1": 1, + "black": 2, + "white": 3, + "darkGray": 4, + "gray": 5, + "lightGray": 6, + "red": 7, + "green": 8, + "blue": 9, + "cyan": 10, + "magenta": 11, + "yellow": 12, + "darkRed": 13, + "darkGreen": 14, + "darkBlue": 15, + "darkCyan": 16, + "darkMagenta": 17, + "darkYellow": 18, + "transparent": 19 + } + } + Enum { + name: "KeyboardModifiers" + values: { + "NoModifier": 0, + "ShiftModifier": 33554432, + "ControlModifier": 67108864, + "AltModifier": 134217728, + "MetaModifier": 268435456, + "KeypadModifier": 536870912, + "GroupSwitchModifier": 1073741824, + "KeyboardModifierMask": -33554432 + } + } + Enum { + name: "MouseButtons" + values: { + "NoButton": 0, + "LeftButton": 1, + "RightButton": 2, + "MidButton": 4, // For backwards compatibility + "MiddleButton": 4, + "BackButton": 8, + "XButton1": 8, + "ExtraButton1": 8, + "ForwardButton": 16, + "XButton2": 16, + "ExtraButton2": 16, + "TaskButton": 32, + "ExtraButton3": 32, + "ExtraButton4": 64, + "ExtraButton5": 128, + "ExtraButton6": 256, + "ExtraButton7": 512, + "ExtraButton8": 1024, + "ExtraButton9": 2048, + "ExtraButton10": 4096, + "ExtraButton11": 8192, + "ExtraButton12": 16384, + "ExtraButton13": 32768, + "ExtraButton14": 65536, + "ExtraButton15": 131072, + "ExtraButton16": 262144, + "ExtraButton17": 524288, + "ExtraButton18": 1048576, + "ExtraButton19": 2097152, + "ExtraButton20": 4194304, + "ExtraButton21": 8388608, + "ExtraButton22": 16777216, + "ExtraButton23": 33554432, + "ExtraButton24": 67108864, + "AllButtons": 134217727, + "MaxMouseButton": 67108864, + "MouseButtonMask": -1 + } + } + Enum { + name: "Orientation" + values: { + "Horizontal": 1, + "Vertical": 2 + } + } + Enum { + name: "Orientations" + values: { + "Horizontal": 1, + "Vertical": 2 + } + } + Enum { + name: "FocusPolicy" + values: { + "NoFocus": 0, + "TabFocus": 1, + "ClickFocus": 2, + "StrongFocus": 11, + "WheelFocus": 15 + } + } + Enum { + name: "TabFocusBehavior" + values: { + "NoTabFocus": 0, + "TabFocusTextControls": 1, + "TabFocusListControls": 2, + "TabFocusAllControls": 255 + } + } + Enum { + name: "SortOrder" + values: { + "AscendingOrder": 0, + "DescendingOrder": 1 + } + } + Enum { + name: "SplitBehavior" + values: { + "KeepEmptyParts": 0, + "SkipEmptyParts": 1 + } + } + Enum { + name: "Alignment" + values: { + "AlignLeft": 1, + "AlignLeading": 1, + "AlignRight": 2, + "AlignTrailing": 2, + "AlignHCenter": 4, + "AlignJustify": 8, + "AlignAbsolute": 16, + "AlignHorizontal_Mask": 31, + "AlignTop": 32, + "AlignBottom": 64, + "AlignVCenter": 128, + "AlignBaseline": 256, + "AlignVertical_Mask": 480, + "AlignCenter": 132 + } + } + Enum { + name: "TextFlag" + values: { + "TextSingleLine": 256, + "TextDontClip": 512, + "TextExpandTabs": 1024, + "TextShowMnemonic": 2048, + "TextWordWrap": 4096, + "TextWrapAnywhere": 8192, + "TextDontPrint": 16384, + "TextIncludeTrailingSpaces": 134217728, + "TextHideMnemonic": 32768, + "TextJustificationForced": 65536, + "TextForceLeftToRight": 131072, + "TextForceRightToLeft": 262144, + "TextLongestVariant": 524288, + "TextBypassShaping": 1048576 + } + } + Enum { + name: "TextElideMode" + values: { + "ElideLeft": 0, + "ElideRight": 1, + "ElideMiddle": 2, + "ElideNone": 3 + } + } + Enum { + name: "WindowType" + values: { + "Widget": 0, + "Window": 1, + "Dialog": 3, + "Sheet": 5, + "Drawer": 7, + "Popup": 9, + "Tool": 11, + "ToolTip": 13, + "SplashScreen": 15, + "Desktop": 17, + "SubWindow": 18, + "ForeignWindow": 33, + "CoverWindow": 65, + "WindowType_Mask": 255, + "MSWindowsFixedSizeDialogHint": 256, + "MSWindowsOwnDC": 512, + "BypassWindowManagerHint": 1024, + "X11BypassWindowManagerHint": 1024, + "FramelessWindowHint": 2048, + "WindowTitleHint": 4096, + "WindowSystemMenuHint": 8192, + "WindowMinimizeButtonHint": 16384, + "WindowMaximizeButtonHint": 32768, + "WindowMinMaxButtonsHint": 49152, + "WindowContextHelpButtonHint": 65536, + "WindowShadeButtonHint": 131072, + "WindowStaysOnTopHint": 262144, + "WindowTransparentForInput": 524288, + "WindowOverridesSystemGestures": 1048576, + "WindowDoesNotAcceptFocus": 2097152, + "MaximizeUsingFullscreenGeometryHint": 4194304, + "CustomizeWindowHint": 33554432, + "WindowStaysOnBottomHint": 67108864, + "WindowCloseButtonHint": 134217728, + "MacWindowToolBarButtonHint": 268435456, + "BypassGraphicsProxyWidget": 536870912, + "NoDropShadowWindowHint": 1073741824, + "WindowFullscreenButtonHint": -2147483648 + } + } + Enum { + name: "WindowFlags" + values: { + "Widget": 0, + "Window": 1, + "Dialog": 3, + "Sheet": 5, + "Drawer": 7, + "Popup": 9, + "Tool": 11, + "ToolTip": 13, + "SplashScreen": 15, + "Desktop": 17, + "SubWindow": 18, + "ForeignWindow": 33, + "CoverWindow": 65, + "WindowType_Mask": 255, + "MSWindowsFixedSizeDialogHint": 256, + "MSWindowsOwnDC": 512, + "BypassWindowManagerHint": 1024, + "X11BypassWindowManagerHint": 1024, + "FramelessWindowHint": 2048, + "WindowTitleHint": 4096, + "WindowSystemMenuHint": 8192, + "WindowMinimizeButtonHint": 16384, + "WindowMaximizeButtonHint": 32768, + "WindowMinMaxButtonsHint": 49152, + "WindowContextHelpButtonHint": 65536, + "WindowShadeButtonHint": 131072, + "WindowStaysOnTopHint": 262144, + "WindowTransparentForInput": 524288, + "WindowOverridesSystemGestures": 1048576, + "WindowDoesNotAcceptFocus": 2097152, + "MaximizeUsingFullscreenGeometryHint": 4194304, + "CustomizeWindowHint": 33554432, + "WindowStaysOnBottomHint": 67108864, + "WindowCloseButtonHint": 134217728, + "MacWindowToolBarButtonHint": 268435456, + "BypassGraphicsProxyWidget": 536870912, + "NoDropShadowWindowHint": 1073741824, + "WindowFullscreenButtonHint": -2147483648 + } + } + Enum { + name: "WindowState" + values: { + "WindowNoState": 0, + "WindowMinimized": 1, + "WindowMaximized": 2, + "WindowFullScreen": 4, + "WindowActive": 8 + } + } + Enum { + name: "WindowStates" + values: { + "WindowNoState": 0, + "WindowMinimized": 1, + "WindowMaximized": 2, + "WindowFullScreen": 4, + "WindowActive": 8 + } + } + Enum { + name: "ApplicationState" + values: { + "ApplicationSuspended": 0, + "ApplicationHidden": 1, + "ApplicationInactive": 2, + "ApplicationActive": 4 + } + } + Enum { + name: "ScreenOrientation" + values: { + "PrimaryOrientation": 0, + "PortraitOrientation": 1, + "LandscapeOrientation": 2, + "InvertedPortraitOrientation": 4, + "InvertedLandscapeOrientation": 8 + } + } + Enum { + name: "ScreenOrientations" + values: { + "PrimaryOrientation": 0, + "PortraitOrientation": 1, + "LandscapeOrientation": 2, + "InvertedPortraitOrientation": 4, + "InvertedLandscapeOrientation": 8 + } + } + Enum { + name: "WidgetAttribute" + values: { + "WA_Disabled": 0, + "WA_UnderMouse": 1, + "WA_MouseTracking": 2, + "WA_ContentsPropagated": 3, + "WA_OpaquePaintEvent": 4, + "WA_NoBackground": 4, + "WA_StaticContents": 5, + "WA_LaidOut": 7, + "WA_PaintOnScreen": 8, + "WA_NoSystemBackground": 9, + "WA_UpdatesDisabled": 10, + "WA_Mapped": 11, + "WA_MacNoClickThrough": 12, + "WA_InputMethodEnabled": 14, + "WA_WState_Visible": 15, + "WA_WState_Hidden": 16, + "WA_ForceDisabled": 32, + "WA_KeyCompression": 33, + "WA_PendingMoveEvent": 34, + "WA_PendingResizeEvent": 35, + "WA_SetPalette": 36, + "WA_SetFont": 37, + "WA_SetCursor": 38, + "WA_NoChildEventsFromChildren": 39, + "WA_WindowModified": 41, + "WA_Resized": 42, + "WA_Moved": 43, + "WA_PendingUpdate": 44, + "WA_InvalidSize": 45, + "WA_MacBrushedMetal": 46, + "WA_MacMetalStyle": 46, + "WA_CustomWhatsThis": 47, + "WA_LayoutOnEntireRect": 48, + "WA_OutsideWSRange": 49, + "WA_GrabbedShortcut": 50, + "WA_TransparentForMouseEvents": 51, + "WA_PaintUnclipped": 52, + "WA_SetWindowIcon": 53, + "WA_NoMouseReplay": 54, + "WA_DeleteOnClose": 55, + "WA_RightToLeft": 56, + "WA_SetLayoutDirection": 57, + "WA_NoChildEventsForParent": 58, + "WA_ForceUpdatesDisabled": 59, + "WA_WState_Created": 60, + "WA_WState_CompressKeys": 61, + "WA_WState_InPaintEvent": 62, + "WA_WState_Reparented": 63, + "WA_WState_ConfigPending": 64, + "WA_WState_Polished": 66, + "WA_WState_DND": 67, + "WA_WState_OwnSizePolicy": 68, + "WA_WState_ExplicitShowHide": 69, + "WA_ShowModal": 70, + "WA_MouseNoMask": 71, + "WA_GroupLeader": 72, + "WA_NoMousePropagation": 73, + "WA_Hover": 74, + "WA_InputMethodTransparent": 75, + "WA_QuitOnClose": 76, + "WA_KeyboardFocusChange": 77, + "WA_AcceptDrops": 78, + "WA_DropSiteRegistered": 79, + "WA_ForceAcceptDrops": 79, + "WA_WindowPropagation": 80, + "WA_NoX11EventCompression": 81, + "WA_TintedBackground": 82, + "WA_X11OpenGLOverlay": 83, + "WA_AlwaysShowToolTips": 84, + "WA_MacOpaqueSizeGrip": 85, + "WA_SetStyle": 86, + "WA_SetLocale": 87, + "WA_MacShowFocusRect": 88, + "WA_MacNormalSize": 89, + "WA_MacSmallSize": 90, + "WA_MacMiniSize": 91, + "WA_LayoutUsesWidgetRect": 92, + "WA_StyledBackground": 93, + "WA_MSWindowsUseDirect3D": 94, + "WA_CanHostQMdiSubWindowTitleBar": 95, + "WA_MacAlwaysShowToolWindow": 96, + "WA_StyleSheet": 97, + "WA_ShowWithoutActivating": 98, + "WA_X11BypassTransientForHint": 99, + "WA_NativeWindow": 100, + "WA_DontCreateNativeAncestors": 101, + "WA_MacVariableSize": 102, + "WA_DontShowOnScreen": 103, + "WA_X11NetWmWindowTypeDesktop": 104, + "WA_X11NetWmWindowTypeDock": 105, + "WA_X11NetWmWindowTypeToolBar": 106, + "WA_X11NetWmWindowTypeMenu": 107, + "WA_X11NetWmWindowTypeUtility": 108, + "WA_X11NetWmWindowTypeSplash": 109, + "WA_X11NetWmWindowTypeDialog": 110, + "WA_X11NetWmWindowTypeDropDownMenu": 111, + "WA_X11NetWmWindowTypePopupMenu": 112, + "WA_X11NetWmWindowTypeToolTip": 113, + "WA_X11NetWmWindowTypeNotification": 114, + "WA_X11NetWmWindowTypeCombo": 115, + "WA_X11NetWmWindowTypeDND": 116, + "WA_MacFrameworkScaled": 117, + "WA_SetWindowModality": 118, + "WA_WState_WindowOpacitySet": 119, + "WA_TranslucentBackground": 120, + "WA_AcceptTouchEvents": 121, + "WA_WState_AcceptedTouchBeginEvent": 122, + "WA_TouchPadAcceptSingleTouchEvents": 123, + "WA_X11DoNotAcceptFocus": 126, + "WA_MacNoShadow": 127, + "WA_AlwaysStackOnTop": 128, + "WA_TabletTracking": 129, + "WA_ContentsMarginsRespectsSafeArea": 130, + "WA_StyleSheetTarget": 131, + "WA_AttributeCount": 132 + } + } + Enum { + name: "ApplicationAttribute" + values: { + "AA_ImmediateWidgetCreation": 0, + "AA_MSWindowsUseDirect3DByDefault": 1, + "AA_DontShowIconsInMenus": 2, + "AA_NativeWindows": 3, + "AA_DontCreateNativeWidgetSiblings": 4, + "AA_PluginApplication": 5, + "AA_MacPluginApplication": 5, + "AA_DontUseNativeMenuBar": 6, + "AA_MacDontSwapCtrlAndMeta": 7, + "AA_Use96Dpi": 8, + "AA_X11InitThreads": 10, + "AA_SynthesizeTouchForUnhandledMouseEvents": 11, + "AA_SynthesizeMouseForUnhandledTouchEvents": 12, + "AA_UseHighDpiPixmaps": 13, + "AA_ForceRasterWidgets": 14, + "AA_UseDesktopOpenGL": 15, + "AA_UseOpenGLES": 16, + "AA_UseSoftwareOpenGL": 17, + "AA_ShareOpenGLContexts": 18, + "AA_SetPalette": 19, + "AA_EnableHighDpiScaling": 20, + "AA_DisableHighDpiScaling": 21, + "AA_UseStyleSheetPropagationInWidgetStyles": 22, + "AA_DontUseNativeDialogs": 23, + "AA_SynthesizeMouseForUnhandledTabletEvents": 24, + "AA_CompressHighFrequencyEvents": 25, + "AA_DontCheckOpenGLContextThreadAffinity": 26, + "AA_DisableShaderDiskCache": 27, + "AA_DontShowShortcutsInContextMenus": 28, + "AA_CompressTabletEvents": 29, + "AA_DisableWindowContextHelpButton": 30, + "AA_DisableSessionManager": 31, + "AA_AttributeCount": 32 + } + } + Enum { + name: "ImageConversionFlags" + values: { + "ColorMode_Mask": 3, + "AutoColor": 0, + "ColorOnly": 3, + "MonoOnly": 2, + "AlphaDither_Mask": 12, + "ThresholdAlphaDither": 0, + "OrderedAlphaDither": 4, + "DiffuseAlphaDither": 8, + "NoAlpha": 12, + "Dither_Mask": 48, + "DiffuseDither": 0, + "OrderedDither": 16, + "ThresholdDither": 32, + "DitherMode_Mask": 192, + "AutoDither": 0, + "PreferDither": 64, + "AvoidDither": 128, + "NoOpaqueDetection": 256, + "NoFormatConversion": 512 + } + } + Enum { + name: "BGMode" + values: { + "TransparentMode": 0, + "OpaqueMode": 1 + } + } + } + Component { name: "QEasingCurve"; prototype: "QQmlEasingValueType" } +} -- cgit v1.2.3 From 1e05b4039321f5dcbb8ac808d734cac523c52dc2 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 19 Jun 2023 11:12:27 +0200 Subject: Update jsroot.qmltypes The Qt object has a "url" method these days. Amends commit 21a2dd46841e75a0100124611820d9afba2e0126 Change-Id: I5a77b16b1eae4a6a5e800a0ac6f9074c3a294281 Reviewed-by: Fabian Kosmale --- src/imports/builtins/jsroot.qmltypes | 1 + 1 file changed, 1 insertion(+) diff --git a/src/imports/builtins/jsroot.qmltypes b/src/imports/builtins/jsroot.qmltypes index 0a8b70babe..39878d8cf3 100644 --- a/src/imports/builtins/jsroot.qmltypes +++ b/src/imports/builtins/jsroot.qmltypes @@ -1356,6 +1356,7 @@ Module { Method { name: "formatTime" } Method { name: "formatDateTime" } Method { name: "locale" } + Method { name: "url" } Method { name: "resolvedUrl" } Method { name: "openUrlExternally" } Method { name: "font" } -- cgit v1.2.3 From f5866fb27e6344910e0996f62d626fa1334c45ef Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 16 Jun 2023 15:03:33 +0200 Subject: QML: Allow conversion of symbols to QVariant We produce their descriptiveString or simply a QVariant containing a QJSValue, depending on whether we're supposed to convert objects without equivalent C++ type or not. Fixes: QTBUG-113854 Change-Id: I22b6038c936d860fdd8aa227f9dfe704e3265a77 Reviewed-by: Fabian Kosmale (cherry picked from commit 8c451bba7aa6474c8aaec01b8fb02201e0237835) --- src/qml/jsapi/qjsvalue.cpp | 3 +- src/qml/jsruntime/qv4engine.cpp | 49 ++++++++++++++++++++---------- src/qml/jsruntime/qv4engine_p.h | 4 ++- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 15 +++++++++ 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 13bf47ce73..1ce7355ed4 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -200,10 +200,11 @@ /*! \enum QJSValue::ObjectConversionBehavior - This enum is used to specify how JavaScript objects without an equivalent + This enum is used to specify how JavaScript objects and symbols without an equivalent native Qt type should be treated when converting to QVariant. \value ConvertJSObjects A best-effort, possibly lossy, conversion is attempted. + Symbols are converted to QString. \value RetainJSObjects The value is retained as QJSValue wrapped in QVariant. */ diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index c0a1edc982..8172b99492 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1506,7 +1506,9 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() // Variant conversion code typedef QSet V4ObjectSet; -static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects); +static QVariant toVariant( + QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType typeHint, + bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects); static QObject *qtObjectFromJS(const QV4::Value &value); static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr); static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result); @@ -1518,7 +1520,9 @@ static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant & return v4->metaTypeToJS(value.metaType(), value.constData()); } -static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType metaType, bool createJSValueForObjects, V4ObjectSet *visitedObjects) +static QVariant toVariant( + QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType metaType, + bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects) { Q_ASSERT (!value.isEmpty()); QV4::Scope scope(e); @@ -1657,6 +1661,9 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet return *ld->d()->locale; #endif if (const QV4::DateObject *d = value.as()) { + // NOTE: since we convert QTime to JS Date, + // round trip will change the variant type (to QDateTime)! + auto dt = d->toQDateTime(); // See ExecutionEngine::metaTypeFromJS()'s handling of QMetaType::Date: if (typeHint == QMetaType::QDate) { @@ -1670,7 +1677,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet return d->toQUrl(); if (const ArrayBuffer *d = value.as()) return d->asByteArray(); - // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! + if (const Symbol *symbol = value.as()) { + return createJSValueForObjectsAndSymbols + ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue())) + : symbol->descriptiveString(); + } QV4::ScopedObject o(scope, value); Q_ASSERT(o); @@ -1680,16 +1691,17 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet return re->toQRegularExpression(); #endif - if (createJSValueForObjects) + if (createJSValueForObjectsAndSymbols) return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue())); return objectToVariant(e, o, visitedObjects); } -QVariant ExecutionEngine::toVariant(const Value &value, QMetaType typeHint, bool createJSValueForObjects) +QVariant ExecutionEngine::toVariant( + const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols) { - return ::toVariant(this, value, typeHint, createJSValueForObjects, nullptr); + return ::toVariant(this, value, typeHint, createJSValueForObjectsAndSymbols, nullptr); } static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects) @@ -1720,7 +1732,8 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V int length = a->getLength(); for (int ii = 0; ii < length; ++ii) { v = a->get(ii); - list << ::toVariant(e, v, QMetaType {}, /*createJSValueForObjects*/false, visitedObjects); + list << ::toVariant( + e, v, QMetaType {}, /*createJSValueForObjectsAndSymbols*/false, visitedObjects); } result = list; @@ -1736,7 +1749,9 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V break; QString key = name->toQStringNoThrow(); - map.insert(key, ::toVariant(e, val, /*type hint*/ QMetaType {}, /*createJSValueForObjects*/false, visitedObjects)); + map.insert(key, ::toVariant( + e, val, /*type hint*/ QMetaType {}, + /*createJSValueForObjectsAndSymbols*/false, visitedObjects)); } result = map; @@ -2428,7 +2443,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi const QV4::ArrayObject *a = value.as(); if (a) { *reinterpret_cast(data) = a->engine()->toVariant( - *a, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false).toList(); + *a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false).toList(); return true; } break; @@ -2442,18 +2457,20 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi break; } case QMetaType::QVariant: - if (const QV4::Managed *m = value.as()) - *reinterpret_cast(data) = m->engine()->toVariant(value, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false); - else if (value.isNull()) + if (const QV4::Managed *m = value.as()) { + *reinterpret_cast(data) = m->engine()->toVariant( + value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false); + } else if (value.isNull()) { *reinterpret_cast(data) = QVariant::fromValue(nullptr); - else if (value.isUndefined()) + } else if (value.isUndefined()) { *reinterpret_cast(data) = QVariant(); - else if (value.isBoolean()) + } else if (value.isBoolean()) { *reinterpret_cast(data) = QVariant(value.booleanValue()); - else if (value.isInteger()) + } else if (value.isInteger()) { *reinterpret_cast(data) = QVariant(value.integerValue()); - else if (value.isDouble()) + } else if (value.isDouble()) { *reinterpret_cast(data) = QVariant(value.doubleValue()); + } return true; case QMetaType::QJsonValue: *reinterpret_cast(data) = QV4::JsonObject::toJsonValue(value); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 43831777ad..b4125563a0 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -682,7 +682,9 @@ public: QQmlError catchExceptionAsQmlError(); // variant conversions - QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects = true); + QVariant toVariant( + const QV4::Value &value, QMetaType typeHint, + bool createJSValueForObjectsAndSymbols = true); QV4::ReturnedValue fromVariant(const QVariant &); QVariantMap variantMapFromJS(const QV4::Object *o); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index a5e8713700..65a492aa29 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -284,6 +284,8 @@ private slots: void thisInConstructor(); void forOfAndGc(); + void symbolToVariant(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -5589,6 +5591,19 @@ void tst_QJSEngine::forOfAndGc() QTRY_VERIFY(o->property("count").toInt() > 32768); } +void tst_QJSEngine::symbolToVariant() +{ + QJSEngine engine; + const QJSValue val = engine.newSymbol("asymbol"); + QCOMPARE(val.toVariant(), QStringLiteral("Symbol(asymbol)")); + + const QVariant retained = val.toVariant(QJSValue::RetainJSObjects); + QCOMPARE(retained.metaType(), QMetaType::fromType()); + QVERIFY(retained.value().strictlyEquals(val)); + + QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)")); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3 From e3343609c8495d02fdfa6e73cf1afb517aef4420 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 15 Jun 2023 15:31:20 +0800 Subject: automotive: fix the dial's height and background assets - Don't allow it to fill vertically, because it results in downscaling artifacts. - Update the background images to be 128x128 so that no downscaling occurs (when using the correct size hints in QML). Fixes: QTBUG-114492 Change-Id: I473ee26f5ad630bbfa1f89c2a977081fae9eaa4e Reviewed-by: Oliver Eftevaag (cherry picked from commit fda497e892c5aa09e44c9fd1cfb05722f11b412c) --- .../imagine-assets/dial-background-hovered.png | Bin 2532 -> 0 bytes .../imagine-assets/dial-background-hovered@2x.png | Bin 4629 -> 0 bytes .../imagine-assets/dial-background-pressed.png | Bin 1271 -> 0 bytes .../imagine-assets/dial-background-pressed@2x.png | Bin 3230 -> 0 bytes .../automotive/imagine-assets/dial-background.png | Bin 1037 -> 0 bytes .../imagine-assets/dial-background@2x.png | Bin 2817 -> 0 bytes .../imagine/automotive/qml/automotive.qml | 42 ++++++++++----------- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png index 26add20cfc..e69de29bb2 100644 Binary files a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png and b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered.png differ diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png index 01d8136d51..e69de29bb2 100644 Binary files a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png and b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-hovered@2x.png differ diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png index 435acd1466..e69de29bb2 100644 Binary files a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png and b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed.png differ diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png index 9bab57904d..e69de29bb2 100644 Binary files a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png and b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background-pressed@2x.png differ diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png index 8aab4d3280..e69de29bb2 100644 Binary files a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png and b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background.png differ diff --git a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png index a856971185..e69de29bb2 100644 Binary files a/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png and b/examples/quickcontrols2/imagine/automotive/imagine-assets/dial-background@2x.png differ diff --git a/examples/quickcontrols2/imagine/automotive/qml/automotive.qml b/examples/quickcontrols2/imagine/automotive/qml/automotive.qml index 4734175870..7a8ce2224f 100644 --- a/examples/quickcontrols2/imagine/automotive/qml/automotive.qml +++ b/examples/quickcontrols2/imagine/automotive/qml/automotive.qml @@ -160,7 +160,7 @@ ApplicationWindow { Item {} ColumnLayout { - spacing: 16 + spacing: 12 ButtonGroup { id: viewButtonGroup @@ -176,6 +176,7 @@ ApplicationWindow { Button { text: qsTr("Compact") font.pixelSize: fontSizeExtraSmall + checkable: true checked: true Layout.fillWidth: true @@ -203,13 +204,8 @@ ApplicationWindow { stepSize: 1 Layout.alignment: Qt.AlignHCenter - Layout.minimumWidth: 64 - Layout.minimumHeight: 64 Layout.preferredWidth: 128 Layout.preferredHeight: 128 - Layout.maximumWidth: 128 - Layout.maximumHeight: 128 - Layout.fillHeight: true Label { text: volumeDial.value.toFixed(0) @@ -224,7 +220,7 @@ ApplicationWindow { } RowLayout { - Layout.topMargin: 16 + Layout.topMargin: 8 LargeLabel { id: radioOption @@ -282,18 +278,18 @@ ApplicationWindow { } model: ListModel { - ListElement { name: "V-Radio"; frequency: "105.5 MHz" } - ListElement { name: "World News"; frequency: "93.4 MHz" } - ListElement { name: "TekStep FM"; frequency: "95.0 MHz" } - ListElement { name: "Classic Radio"; frequency: "89.9 MHz" } - ListElement { name: "Buena Vista FM"; frequency: "100.8 MHz" } - ListElement { name: "Drive-by Radio"; frequency: "99.1 MHz" } - ListElement { name: "Unknown #1"; frequency: "104.5 MHz" } - ListElement { name: "Unknown #2"; frequency: "91.2 MHz" } - ListElement { name: "Unknown #3"; frequency: "93.8 MHz" } - ListElement { name: "Unknown #4"; frequency: "80.4 MHz" } - ListElement { name: "Unknown #5"; frequency: "101.1 MHz" } - ListElement { name: "Unknown #6"; frequency: "92.2 MHz" } + ListElement { name: "V-Radio"; frequency: "105.5" } + ListElement { name: "World News"; frequency: "93.4" } + ListElement { name: "TekStep FM"; frequency: "95.0" } + ListElement { name: "Classic Radio"; frequency: "89.9" } + ListElement { name: "Buena Vista FM"; frequency: "100.8" } + ListElement { name: "Drive-by Radio"; frequency: "99.1" } + ListElement { name: "Unknown #1"; frequency: "104.5" } + ListElement { name: "Unknown #2"; frequency: "91.2" } + ListElement { name: "Unknown #3"; frequency: "93.8" } + ListElement { name: "Unknown #4"; frequency: "80.4" } + ListElement { name: "Unknown #5"; frequency: "101.1" } + ListElement { name: "Unknown #6"; frequency: "92.2" } } delegate: ItemDelegate { id: stationDelegate @@ -309,12 +305,16 @@ ApplicationWindow { text: model.name font: stationDelegate.font horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + Layout.fillWidth: true } Label { text: model.frequency font: stationDelegate.font horizontalAlignment: Text.AlignRight + elide: Text.ElideRight + Layout.fillWidth: true } } @@ -325,14 +325,12 @@ ApplicationWindow { Frame { Layout.fillWidth: true - RowLayout { + ColumnLayout { anchors.fill: parent Label { text: qsTr("Sort by") font.pixelSize: fontSizeExtraSmall - - Layout.alignment: Qt.AlignTop } ColumnLayout { -- cgit v1.2.3 From d75ed550b525d2cfdd3f53d0675c37460df3322e Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Thu, 22 Jun 2023 04:02:38 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: I11d44cfd13dcf71ab58c730c8e3cda04a7f20d90 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 1a5e9e4d6e..5a1a9fb19c 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: bcde7eeb01b922b2037bde75ef71dcf24222080f + ref: 6fb561ab836e2ddf1fa745e4ef6222e458383d58 required: true ../tqtc-qtimageformats: - ref: d6f6c088637b8fbf6ed1202c22bb03c0a4854543 + ref: b28c1b2613e3e5bee31c59dddca275e00ff2145a required: false ../tqtc-qtshadertools: - ref: b61522dfe7d843e89f1bd335a28a11ff8b23843b + ref: c1dbd53d88887695849b4b35e15641dda88e1bc4 required: false ../tqtc-qtsvg: - ref: f80df57e55fcb3ceb8dc7a4754113d4de8bf741d + ref: ab5d59b6f2c767e4c34d855c8a8de2921a3f6bec required: false -- cgit v1.2.3 From a6acd50fe15d15130a38c2945176afb259d35de0 Mon Sep 17 00:00:00 2001 From: Paul Wicking Date: Wed, 21 Jun 2023 07:07:23 +0200 Subject: Doc: Remove mention of module version requirement Fixes: QTBUG-114715 Change-Id: I7b5821820ba582fb21eaa56e3cf102fc3d61be00 Reviewed-by: Fabian Kosmale (cherry picked from commit 9c33258ff56f4028a62c13fda866452bdb9caf38) Reviewed-by: Qt Cherry-pick Bot --- src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc index 493b031b60..6c235ef388 100644 --- a/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc +++ b/src/qml/doc/src/qmllanguageref/modules/identifiedmodules.qdoc @@ -177,7 +177,6 @@ An identified module has several restrictions upon it: \li the module must register its types into the module identifier type namespace \li the module may not register types into any other module's namespace -\li clients must specify a version when importing the module \endlist For example, if an identified module is installed into @@ -194,10 +193,9 @@ module com.example.CustomUi \endcode Clients will then be able to import the above module with the following import -statement (assuming that the module registers types into version 1.0 of its -namespace): +statement: \qml -import com.example.CustomUi 1.0 +import com.example.CustomUi \endqml */ -- cgit v1.2.3 From 3eac494d04b0efee582d074efa999e7e86cc9669 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 26 Jun 2023 11:00:56 +0200 Subject: QRecyclePool: fix potential UB Return the pointer returned by placement new, not the pointer used as input to placement new. There is a subtle difference and this grey zone of the C++ standard is best avoided (keyword: std::launder()). Change-Id: I27c159cdb29a5837120f3d44aa6c95da040fd1a2 Reviewed-by: Ulf Hermann (cherry picked from commit 7381110745572478ffa3c68000574bc4ccb2396c) Reviewed-by: Qt Cherry-pick Bot --- src/qml/qml/ftw/qrecyclepool_p.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/qml/qml/ftw/qrecyclepool_p.h b/src/qml/qml/ftw/qrecyclepool_p.h index ad725cd6d8..c8995c8f77 100644 --- a/src/qml/qml/ftw/qrecyclepool_p.h +++ b/src/qml/qml/ftw/qrecyclepool_p.h @@ -132,8 +132,7 @@ template T *QRecyclePool::New() { T *rv = d->allocate(); - new (rv) T; - return rv; + return new (rv) T; } template @@ -141,8 +140,7 @@ template T *QRecyclePool::New(const T1 &a) { T *rv = d->allocate(); - new (rv) T(a); - return rv; + return new (rv) T(a); } template @@ -150,8 +148,7 @@ template T *QRecyclePool::New(T1 &a) { T *rv = d->allocate(); - new (rv) T(a); - return rv; + return new (rv) T(a); } template -- cgit v1.2.3 From 5c6d1e23d739df53ec38378987dc31296c2eef96 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 27 Jun 2023 13:02:45 +0200 Subject: QQmlJs::MemoryPool: fix potential UB (pointer overflow) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A check like (p1 + s op p2) is dangerous, because p1 + s may overflow, and that would be UB, so the compiler can assume it doesn't happen and break the check. Reformulate the expression by subtracting p1 from both sides. Cast the ptrdiff_t to size_t to avoid -Wsign-compare. This is safe because _end is always ≥ _ptr. As a drive-by, remove extra parentheses. Change-Id: If240d685fe48196ab5ceb7ff39736b73c8997e30 Reviewed-by: Ulf Hermann (cherry picked from commit 8a39f7655f4cfbc35c1886b49e2f3a9ada263e39) Reviewed-by: Qt Cherry-pick Bot --- src/qml/common/qqmljsmemorypool_p.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/qml/common/qqmljsmemorypool_p.h b/src/qml/common/qqmljsmemorypool_p.h index e290d6ada7..f5ce3404c3 100644 --- a/src/qml/common/qqmljsmemorypool_p.h +++ b/src/qml/common/qqmljsmemorypool_p.h @@ -81,13 +81,12 @@ public: free(_blocks); } - qDeleteAll(strings); } inline void *allocate(size_t size) { size = (size + 7) & ~size_t(7); - if (Q_LIKELY(_ptr && (_ptr + size < _end))) { + if (Q_LIKELY(_ptr && size < size_t(_end - _ptr))) { void *addr = _ptr; _ptr += size; return addr; @@ -105,9 +104,8 @@ public: template Tp *New(Ta... args) { return new (this->allocate(sizeof(Tp))) Tp(args...); } - QStringView newString(const QString &string) { - strings.append(new QString(string)); - return QStringView(*strings.last()); + QStringView newString(QString string) { + return strings.emplace_back(std::move(string)); } private: @@ -151,7 +149,7 @@ private: int _blockCount = -1; char *_ptr = nullptr; char *_end = nullptr; - QVector strings; + QStringList strings; enum { -- cgit v1.2.3 From 6a2484fa338fc4357adce78defc7c54a5fc00a77 Mon Sep 17 00:00:00 2001 From: Semih Yavuz Date: Thu, 29 Jun 2023 11:41:00 +0200 Subject: Add Qt::QuickTest check to tst_core tst_core has Qt::QuickTest dependency that makes compilation fail unless you don't build qtquick. Change-Id: I6e087cc35e55b62bed7e991dd4276cba2e413f0f Reviewed-by: Ulf Hermann --- tests/auto/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index fd4a6748c1..d9730fbb58 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -13,7 +13,9 @@ endif() if(TARGET Qt::Quick) add_subdirectory(quicktest) endif() -add_subdirectory(core) +if (TARGET Qt::QuickTest) + add_subdirectory(core) +endif() add_subdirectory(qmldevtools) add_subdirectory(toolsupport) if(NOT UIKIT AND NOT ANDROID AND NOT QNX) # FIXME: QTBUG-92591 QTBUG-100202 -- cgit v1.2.3 From 6a39ab58af2e81810a5cd672a1b4af1da7a60a15 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 28 Jun 2023 09:40:37 +0200 Subject: QQmlPropertyBinding: Do re-attach observer after handling undefined As noted in the comments above, we need to restore the binding to its original state after resetting the property. This includes re-adding any observers. Fixes: QTBUG-114874 Change-Id: I2113a59f1bed59753c77ca3e0b00fed4c0487584 Reviewed-by: Fabian Kosmale Reviewed-by: Qt CI Bot (cherry picked from commit 52fcaec65d0a13831b0b8c75957c58235d6fdcc4) --- src/qml/qml/qqmlpropertybinding.cpp | 4 +++- .../data/restoreObserverAfterReset.qml | 20 ++++++++++++++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 12 ++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp index fc7d37e4be..ba3188fe12 100644 --- a/src/qml/qml/qqmlpropertybinding.cpp +++ b/src/qml/qml/qqmlpropertybinding.cpp @@ -284,8 +284,10 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void // reset might have changed observers (?), so refresh firstObserver firstObserver = bindingDataPointer.firstObserver(); bindingData->d_ref() = reinterpret_cast(this) | QtPrivate::QPropertyBindingData::BindingBit; - if (firstObserver) + if (firstObserver) { bindingDataPointer.setObservers(firstObserver.ptr); + prependObserver(firstObserver); + } } else { QQmlError qmlError; auto location = jsExpression()->sourceLocation(); diff --git a/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml new file mode 100644 index 0000000000..2933d9b4d5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml @@ -0,0 +1,20 @@ +import QtQuick + +Item { + height: undefined + implicitHeight: 30 + property int steps: 0 + + Behavior on height { + NumberAnimation { + duration: 500 + } + } + + onHeightChanged: ++steps + + Component.onCompleted: { + height = Qt.binding(() => implicitHeight); + implicitHeight = 60; + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index f47c7132a2..a472f826ed 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -393,6 +393,7 @@ private slots: void qpropertyBindingHandlesUndefinedCorrectly(); void qpropertyBindingHandlesUndefinedWithoutResetCorrectly_data(); void qpropertyBindingHandlesUndefinedWithoutResetCorrectly(); + void qpropertyBindingRestoresObserverAfterReset(); void hugeRegexpQuantifiers(); void singletonTypeWrapperLookup(); void getThisObject(); @@ -9195,6 +9196,17 @@ void tst_qqmlecmascript::qpropertyBindingHandlesUndefinedWithoutResetCorrectly() QCOMPARE(root->property("value2").toInt(), 2); } +void tst_qqmlecmascript::qpropertyBindingRestoresObserverAfterReset() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreObserverAfterReset.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + QTRY_COMPARE(o->property("height").toDouble(), 60.0); + QVERIFY(o->property("steps").toInt() > 3); +} + void tst_qqmlecmascript::hugeRegexpQuantifiers() { QJSEngine engine; -- cgit v1.2.3 From 19e6b99dc89be08764301cc7f8ef2ebb570b02e6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 3 Jul 2023 19:07:55 +0200 Subject: doc: Remove Calendar Example link In 2dcad8f5c899ecec459d9e2a2c30211ee9e6c20c this was probably already wrong; it links to a widget-based example in 5.15 and 6.5 (which is removed in 6.6). Change-Id: I6bacf4f75c9aaeeb184528e0b46e0adcaf4fc9db Reviewed-by: Shawn Rutledge (cherry picked from commit a8117b10838d27a1e16c816544bb2a786b1b3b46) Reviewed-by: Qt Cherry-pick Bot --- src/quick/doc/src/examples.qdoc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc index b0cfa247ec..2f6c87e840 100644 --- a/src/quick/doc/src/examples.qdoc +++ b/src/quick/doc/src/examples.qdoc @@ -123,7 +123,6 @@ Creator. \b{QML Types and Controls} \list \li \l{Qt Quick Controls - Gallery}{Controls Gallery} - \li \l{Calendar Example} \li \l{tableview/gameoflife}{TableView} \li \l{Qt Quick Examples - Text}{Text and Fonts} \li \l{Qt Quick Examples - Toggle Switch}{Custom Toggle Switch} -- cgit v1.2.3 From b9716659f14114ea8b7e68b347d5cb05b4de4a2d Mon Sep 17 00:00:00 2001 From: Joni Poikelin Date: Mon, 19 Jun 2023 15:11:13 +0300 Subject: Fix primary screen change Fixes: QTBUG-114687 Change-Id: I9046ebf9ebcb847c4b0512057c54c976a717fb91 Reviewed-by: Liang Qi Reviewed-by: Shawn Rutledge (cherry picked from commit 214f7f89042ca12b8ff1b50f19ce3a994b8b13ab) Reviewed-by: Qt Cherry-pick Bot --- src/quick/util/qquickapplication.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp index 65584b16e4..c3afe762a4 100644 --- a/src/quick/util/qquickapplication.cpp +++ b/src/quick/util/qquickapplication.cpp @@ -235,6 +235,7 @@ QQuickApplication::QQuickApplication(QObject *parent) connect(guiApp, &QGuiApplication::applicationDisplayNameChanged, this, &QQuickApplication::displayNameChanged); + connect(guiApp, &QGuiApplication::primaryScreenChanged, this, &QQuickApplication::updateScreens); connect(guiApp, &QGuiApplication::screenAdded, this, &QQuickApplication::updateScreens); connect(guiApp, &QGuiApplication::screenRemoved, this, &QQuickApplication::updateScreens); updateScreens(); -- cgit v1.2.3 From 153556975afbc71e81133b8a6e945148592c03ce Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 4 Jul 2023 12:30:45 +0200 Subject: Pass render mode correctly to material for all cases One of the code paths that call createShader() was not passing along the render mode, causing it to default to 2D rendering. This could cause issues when rendering a 2D item in 3D if the material has special handling of perspective transforms, like e.g. with text which needs a different shader to get correct antialiasing in 3D. Change-Id: I4a22642863df58351daeec63e99b54a986b4b45e Reviewed-by: Laszlo Agocs (cherry picked from commit ca558d3d593515835e4192f417f48b0c3648defc) Reviewed-by: Qt Cherry-pick Bot --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 6ae95aaea4..1674f3da99 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -3251,7 +3251,7 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren // unmerged batch since the material (and so the shaders) is the same. QSGGeometry *g = gn->geometry(); QSGMaterial *material = gn->activeMaterial(); - ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g); + ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode); if (!sms) return false; -- cgit v1.2.3 From eb57fe58d081b0902eb15a1167ca77b5ae4f17b8 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Mon, 5 Jun 2023 12:13:52 +0200 Subject: Update test case for Tumbler to flick within Flickable The patch set 024f91c55f67e9c9c1c0b47aa981e6b99827ad20 fixes this issue. It's a follow-up patch to update the tumbler test case. Task-number: QTBUG-109995 Change-Id: Ic12c4b4789a0c619a8531d9321a70d85a006b63c Reviewed-by: Shawn Rutledge (cherry picked from commit 9573506fe04d9581f8c60df6b6e2e20473639bf8) --- .../quickcontrols2/controls/data/tst_tumbler.qml | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml b/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml index fff6fa7933..0fc2e87b8a 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml @@ -1278,4 +1278,46 @@ TestCase { tumbler = createTemporaryObject(initialCurrentIndexTumbler, testCase, {wrap: false}); compare(tumbler.currentIndex, 4); } + + // QTBUG-109995 + Component { + id: flickTumbler + Flickable { + width: 50 + height: 200 + interactive: true + contentHeight: 400 + property alias tumblerItem: noWrapTumbler + Tumbler { + id: noWrapTumbler + anchors.fill: parent + model: 20 + wrap: false + } + } + } + + function test_flick() { + let control = createTemporaryObject(flickTumbler, testCase) + verify(control) + + let tumbler = control.tumblerItem + compare(tumbler.count, 20) + compare(tumbler.wrap, false) + + let touch = touchEvent(tumbler) + let tumblerView = findView(tumbler) + let delegateHeight = tumblerView.children[0].children[0].height + + // Move delegates through touch operation and check the current index + touch.press(0, tumblerView, control.width / 2, control.height / 2).commit() + // Move slowly, otherwise its considered as flick which cause current index + // to be varied according to its velocity + var scrollOffset = control.height / 2 + for (; scrollOffset > delegateHeight / 2; scrollOffset-=5) { + touch.move(0, tumblerView, control.width / 2, scrollOffset).commit() + } + touch.release(0, tumblerView, control.width / 2, scrollOffset).commit() + tryCompare(tumblerView, "currentIndex", 2) + } } -- cgit v1.2.3 From 17769f50c3c8047d34dbe3ff3bd1325bd18a8370 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 6 Jul 2023 10:55:27 +0200 Subject: QtQml: Do not crash on invalid import URLs We get rather unusual file URLs if we let an import with an invalid URL pass. Once those files are referenced, we hit some asserts. Change-Id: If7acefcd07c844e680be9db6e49b5ef366baea4b Reviewed-by: Fabian Kosmale (cherry picked from commit 8a073501ac31c42fdf36cfdfba32d67c3fdd1d75) --- src/qml/qml/qqmlimport.cpp | 9 +++++++++ tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml | 6 ++++++ tests/auto/qml/qqmlimport/tst_qqmlimport.cpp | 13 +++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 5e60f9fb95..5b5475893e 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1519,6 +1519,15 @@ QTypeRevision QQmlImportsPrivate::addFileImport( // The url for the path containing files for this import QString url = resolveLocalUrl(base, uri); + if (url.isEmpty()) { + QQmlError error; + error.setDescription( + QQmlImportDatabase::tr("Cannot resolve URL for import \"%1\"").arg(uri)); + error.setUrl(baseUrl); + errors->prepend(error); + return QTypeRevision(); + } + if (!url.endsWith(Slash) && !url.endsWith(Backslash)) url += Slash; diff --git a/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml new file mode 100644 index 0000000000..83dbd566f3 --- /dev/null +++ b/tests/auto/qml/qqmlimport/data/fileDotSlashImport.qml @@ -0,0 +1,6 @@ +import QtQml +import 'file://./MyModuleName' as MyModuleName + +QtObject { + objectName: MyModuleName.Font.exampleVar +} diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index 7b6c33bcc9..35b035ae9c 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -61,6 +61,7 @@ private slots: void preferResourcePath(); void invalidFileImport_data(); void invalidFileImport(); + void invalidImportUrl(); }; void tst_QQmlImport::cleanup() @@ -136,6 +137,18 @@ void tst_QQmlImport::invalidFileImport() "but not absolute paths or resource paths.").arg(import))); } +void tst_QQmlImport::invalidImportUrl() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("fileDotSlashImport.qml"); + QQmlComponent component(&engine, url); + QVERIFY(component.isError()); + QCOMPARE( + component.errorString(), + url.toString() + QLatin1String( + ":2 Cannot resolve URL for import \"file://./MyModuleName\"\n")); +} + void tst_QQmlImport::testDesignerSupported() { QQuickView *window = new QQuickView(); -- cgit v1.2.3 From 53bdc05d195f0ab2d49fac7ae88039d8ee8c3636 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 5 Jul 2023 13:12:01 +0200 Subject: QML: Check result when constructing object from metaobject If you pass insufficient arguments to call the ctor, the resulting object is null and cannot be used. Fixes: QTBUG-114910 Change-Id: Ib184684b6a7665bcdc1a3fe8f8a2401a33a8ac1c Reviewed-by: Semih Yavuz Reviewed-by: Fabian Kosmale (cherry picked from commit 3110117bfaa36c5363bfa46cd82b01ff6723dd8d) --- src/qml/jsruntime/qv4qobjectwrapper.cpp | 8 +++++--- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 942c0a70f9..f52b4564b9 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -2335,9 +2335,11 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) objectOrGadget, d()->constructors, d()->constructorCount, v4, callData)) { object = CallPrecise(objectOrGadget, *ctor, v4, callData, QMetaObject::CreateInstance); } - Scoped metaObject(scope, this); - object->defineDefaultProperty(v4->id_constructor(), metaObject); - object->setPrototypeOf(const_cast(this)); + if (object) { + Scoped metaObject(scope, this); + object->defineDefaultProperty(v4->id_constructor(), metaObject); + object->setPrototypeOf(const_cast(this)); + } return object.asReturnedValue(); } diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 65a492aa29..55d5a97f35 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -997,6 +997,17 @@ private: int m_called = 1; }; +class TestQMetaObject2 : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE TestQMetaObject2(int a) : m_called(a) {} + int called() const { return m_called; } + +private: + int m_called = 1; +}; + void tst_QJSEngine::newQObjectPropertyCache() { QScopedPointer obj(new QObject); @@ -1071,6 +1082,18 @@ void tst_QJSEngine::newQMetaObject() { QCOMPARE(metaObject.property("C").toInt(), 2); } + { + QJSEngine engine; + const QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject2::staticMetaObject); + engine.globalObject().setProperty(QLatin1String("Example"), metaObject); + + const QJSValue invalid = engine.evaluate(QLatin1String("new Example()")); + QVERIFY(invalid.isError()); + QCOMPARE(invalid.toString(), QLatin1String("Error: Insufficient arguments")); + + const QJSValue valid = engine.evaluate(QLatin1String("new Example(123)")); + QCOMPARE(qjsvalue_cast(valid)->called(), 123); + } } void tst_QJSEngine::exceptionInSlot() -- cgit v1.2.3 From 70c37779da58eb09eebeaefcd8c2d9a651299c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Tue, 11 Jul 2023 15:15:09 +0200 Subject: Fix pointer delivery to child items of items with clip:true QQuickDeliveryAgentPrivate::pointerTargets() can visit a lot of items and their handlers, before actual event delivery really starts. One optimization in place since 6adc36115f4fc658fb907ee8af3013f2609ae761 is that if an item is clipped, and the point is outside its bounds, we can be sure that it's also irrelevant to the item's children, because the parts of any children that may be under that point are clipped away and invisible. At the time that was written, QQuickItem::contains() was only doing a simple bounding-rect check. Since then, bf74a908cb0591c2adc024a6f93d566c7348c125 added containmentMask; and we should also keep in mind the precedence of the PointerHandler.margin property (currently, TapHandler.margin does not expand the sensitive area beyond a clipped Rectangle, or beyond the containmentMask either). So it seems we now need to check clipRect().contains() explicitly: a child item may be outside its parent's containmentMask, and containmentMask does not affect clipping, so one would expect to still be able to interact with the child. The current definition of clipRect() is from 9b62f4c27ac3fb3dc563c7f4657094c14d752bac. It's virtual, but documented as "the region intended to remain visible if clip is true". Fixes: QTBUG-115179 Change-Id: I6ae8f492b99725459cdff2a89ac8508da5167102 Reviewed-by: Shawn Rutledge (cherry picked from commit f33146ed0abb67cfd82b491ba63e7279da9d95b5) --- src/quick/util/qquickdeliveryagent.cpp | 2 +- .../qquickpointerhandler/data/clip.qml | 36 ++++++++++++++ .../tst_qquickpointerhandler.cpp | 58 ++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index 804acf3669..bc4ec0670c 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -1833,7 +1833,7 @@ QVector QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *ite qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point; // if the item clips, we can potentially return early if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - if (!relevant) + if (!item->clipRect().contains(itemPos)) return targets; } diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml new file mode 100644 index 0000000000..7bc3907c8c --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/clip.qml @@ -0,0 +1,36 @@ +import QtQuick +import Qt.test 1.0 + +Item { + width: 200 + height: 200 + + Rectangle { + id: circle + y: 0 + width: 100 + height: width + radius: width/2 + color: "#3e1" + clip: true + + // Rectangle contains() is not affected by its 'radius' property + containmentMask: QtObject { + property alias radius: circle.radius + function contains(point: point) : bool { + return (Math.pow(point.x - radius, 2) + Math.pow(point.y - radius, 2)) < Math.pow(radius, 2) + } + } + EventHandler { + objectName: "circle eventHandler" + } + Rectangle { + width: circle.width/2 + height: width + color: "red" + EventHandler { + objectName: "eventHandler" + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp index 9cab8f8ed6..51c94ea8c8 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -252,6 +252,7 @@ private slots: void handlerInWindow(); void dynamicCreationInWindow(); void cppConstruction(); + void clip(); protected: bool eventFilter(QObject *, QEvent *event) override @@ -734,6 +735,63 @@ void tst_PointerHandlers::cppConstruction() QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); } +void tst_PointerHandlers::clip() +{ + QScopedPointer windowPtr; + createView(windowPtr, "clip.qml"); + QQuickView * window = windowPtr.data(); + QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + EventHandler *handler = window->contentItem()->findChild("eventHandler"); + EventHandler *circleHandler = window->contentItem()->findChild("circle eventHandler"); + + QCOMPARE(handler->pressEventCount, 0); + QCOMPARE(circleHandler->pressEventCount, 0); + QCOMPARE(handler->releaseEventCount, 0); + QCOMPARE(circleHandler->releaseEventCount, 0); + + const QPoint rectPt = QPoint(1, 1); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectPt); + QCOMPARE(handler->pressEventCount, 1); + QCOMPARE(circleHandler->pressEventCount, 0); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectPt); + QCOMPARE(handler->releaseEventCount, 1); + QCOMPARE(circleHandler->releaseEventCount, 0); + + + handler->pressEventCount = 0; + circleHandler->pressEventCount = 0; + handler->releaseEventCount = 0; + circleHandler->releaseEventCount = 0; + + const QPoint rectAndCirclePt = QPoint(49 ,49); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt); + QCOMPARE(handler->pressEventCount, 1); + QCOMPARE(circleHandler->pressEventCount, 1); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt); + QCOMPARE(handler->releaseEventCount, 1); + QCOMPARE(circleHandler->releaseEventCount, 1); + + + handler->pressEventCount = 0; + circleHandler->pressEventCount = 0; + handler->releaseEventCount = 0; + circleHandler->releaseEventCount = 0; + + const QPoint circlePt = QPoint(51 ,51); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, circlePt); + QCOMPARE(handler->pressEventCount, 0); + QCOMPARE(circleHandler->pressEventCount, 1); + + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, circlePt); + QCOMPARE(handler->releaseEventCount, 0); + QCOMPARE(circleHandler->releaseEventCount, 1); +} + QTEST_MAIN(tst_PointerHandlers) #include "tst_qquickpointerhandler.moc" -- cgit v1.2.3 From 7307ece74255d3fd14129d5bc4ffa82188e6a9a2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Moskal Date: Tue, 4 Jul 2023 14:27:05 +0200 Subject: Doc: add information about preeditText in text property The documentation for the TextEdit::text and TextInput::text properties was missing information about preeditText. This may result in the expectation that text property will always contain all displayed text. Fixes: QTBUG-109306 Change-Id: Ifa0b214238633a42e9772f796547f776a0d4251c Reviewed-by: Volker Hilsheimer Reviewed-by: Mitch Curtis Reviewed-by: Assam Boudjelthia (cherry picked from commit 1ca4bb63477f8b15f99780815325334d552acbdb) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicktextedit.cpp | 12 +++++++++++- src/quick/items/qquicktextinput.cpp | 13 +++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 5466902df5..d28c140893 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -396,7 +396,12 @@ QString QQuickTextEdit::text() const remarkably better performance for modifying especially large rich text content. - \sa clear(), textFormat + Note that some keyboards use a predictive function. In this case, + the text being composed by the input method is not part of this property. + The part of the text related to the predictions is underlined and stored in + the \l preeditText property. + + \sa clear(), preeditText, textFormat */ void QQuickTextEdit::setText(const QString &text) { @@ -428,6 +433,11 @@ void QQuickTextEdit::setText(const QString &text) \since 5.7 This property contains partial text input from an input method. + + To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText + flag in inputMethodHints. + + \sa inputMethodHints */ QString QQuickTextEdit::preeditText() const { diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index f9b1caaa48..d31275a38c 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -125,7 +125,13 @@ void QQuickTextInput::componentComplete() The text in the TextInput. - \sa clear() + Note that some keyboards use a predictive function. In this case, + the text being composed by the input method is not part of this property. + The part of the text related to the predictions is underlined and stored in + the \l preeditText property. To get whole text displayed in the TextInput + use \l displayText property. + + \sa clear(), displayText, preeditText */ QString QQuickTextInput::text() const { @@ -2391,7 +2397,10 @@ QString QQuickTextInput::displayText() const This property contains partial text input from an input method. - \sa displayText + To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText + flag in inputMethodHints. + + \sa displayText, inputMethodHints */ QString QQuickTextInput::preeditText() const { -- cgit v1.2.3 From b355ab1cc4fd8bd325bbb855e8bb037221ec8f68 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Fri, 14 Jul 2023 14:22:50 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Iff43cdeb8adae62e8ca5afe986276a0527bb823e Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 5a1a9fb19c..93a24b6e0a 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 6fb561ab836e2ddf1fa745e4ef6222e458383d58 + ref: 3d0ef9b4f4710fc64892bdcf03d7271e0e02ef05 required: true ../tqtc-qtimageformats: - ref: b28c1b2613e3e5bee31c59dddca275e00ff2145a + ref: aa53b38786d065a812b7714609d0f98056b8657f required: false ../tqtc-qtshadertools: - ref: c1dbd53d88887695849b4b35e15641dda88e1bc4 + ref: d420dd0b983d7ea637970b9824f9a94d8f975e77 required: false ../tqtc-qtsvg: - ref: ab5d59b6f2c767e4c34d855c8a8de2921a3f6bec + ref: e5dc6d27ec7f6e6b267193088883fda1ae1fed90 required: false -- cgit v1.2.3 From 5143bdd6477f50592588a3ff7e0f725cc34a7829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20De=20Canni=C3=A8re?= Date: Mon, 17 Jul 2023 09:44:11 +0200 Subject: QML: Export de-inlined functions in ObjectWrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amends 63b622d5908ec2960ce5dfa301e9d3fd4d92fdb4 Change-Id: I61a37fb9b66eef7f141f1f110c7688c660baff93 Reviewed-by: Fabian Kosmale (cherry picked from commit b460361c4cab20ec2e2db7a5c974ebf17f62068d) Reviewed-by: Qt CI Bot Reviewed-by: Olivier De Cannière --- src/qml/jsruntime/qv4qobjectwrapper_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index c7611b3d5a..25b6ee11b1 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -100,8 +100,8 @@ private: Member(class, NoMark, QV4QPointer, qObj) \ Member(class, NoMark, int, index) -DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { - DECLARE_MARKOBJECTS(QObjectMethod); +DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) { + DECLARE_MARKOBJECTS(QObjectMethod) QQmlPropertyData *methods; int methodCount; -- cgit v1.2.3 From a00de49e4147263531026e0989f76a8d271c4529 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 18 Jul 2023 17:08:44 +0800 Subject: QQuickMenu: remove contentItem children change listeners on destruct QQuickMenu causes a race condition (crash) on destruction, due to not removed children listeners in some cases. This patch removes the listeners manually on destruction. Fixes: QTBUG-108144 Change-Id: I4c166c782ae014236970a1f9e145f6862cd7c6a9 Reviewed-by: Mitch Curtis (cherry picked from commit a7c4507b14ae7c92bdad8c1652b8b5ed63636fad) Reviewed-by: Volker Hilsheimer --- src/quicktemplates2/qquickmenu.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp index ba10008dd2..ee5acc9ad3 100644 --- a/src/quicktemplates2/qquickmenu.cpp +++ b/src/quicktemplates2/qquickmenu.cpp @@ -745,6 +745,12 @@ QQuickMenu::~QQuickMenu() // been destroyed before that is called. while (d->contentModel->count() > 0) d->removeItem(0, d->itemAt(0)); + + if (d->contentItem) { + const auto children = d->contentItem->childItems(); + for (QQuickItem *child : std::as_const(children)) + QQuickItemPrivate::get(child)->removeItemChangeListener(d, QQuickItemPrivate::SiblingOrder); + } } /*! -- cgit v1.2.3 From 9d4eac0c11b6aec03f54abbb17074e8e02633581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amanda=20Hamblin-Tru=C3=A9?= Date: Fri, 7 Jul 2023 16:42:08 +0200 Subject: Add testcase verifying unshift misbehaving When calling sort after unshift, array entries unexpectedly become undefined. Task-number: QTBUG-58718 Change-Id: Ie577e6d983f22e02ab4ade3d1f722e7c44c2a981 Reviewed-by: Qt CI Bot Reviewed-by: Ulf Hermann (cherry picked from commit 7e65bb98c599fd0766e275d26889c82674ccfc19) Reviewed-by: Fabian Kosmale --- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 55d5a97f35..00886d79af 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -108,6 +108,7 @@ private slots: void collectGarbageNestedWrappersTwoEngines(); void gcWithNestedDataStructure(); void stacktrace(); + void unshiftAndSort(); void numberParsing_data(); void numberParsing(); void automaticSemicolonInsertion(); @@ -2043,6 +2044,47 @@ void tst_QJSEngine::stacktrace() } } +void tst_QJSEngine::unshiftAndSort() +{ + QJSEngine engine; + QJSValue func = engine.evaluate(R"""( + + (function (objectArr, currIdx) { + + objectArr.unshift({"sortIndex": currIdx}); + objectArr.sort(function(a, b) { + if(a.sortIndex > b.sortIndex) { + return 1 + } + if(a.sortIndex < b.sortIndex) { + return -1; + } + return 0; + }); + + ++currIdx; + return objectArr; + }) + )"""); + QVERIFY(func.isCallable()); + QJSValue objectArr = engine.newArray(); + + for (int i = 0; i < 5; ++i) { + objectArr = func.call({objectArr, i}); + QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString())); + const int length = objectArr.property("length").toInt(); + bool isAnyUndefined = false; + for (int x = 0; x < length; ++x) { + if (objectArr.property(x).isUndefined()) { + isAnyUndefined = true; + } + } + if (i == 4) + QEXPECT_FAIL("", "QTBUG-58718: entry becomes undefined", Continue); + QVERIFY(!isAnyUndefined); + } +} + void tst_QJSEngine::numberParsing_data() { QTest::addColumn("string"); -- cgit v1.2.3 From 31b93a4991e146bb4072e89ca58a58ec4f3e2994 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 24 Jul 2023 15:31:33 +0200 Subject: V4: Use memmove() for tail calls If you have many arguments, the argument ranges can overlap. In that case memcpy is UB. Fixes: QTBUG-115320 Change-Id: I54dd4d7762e7278502954b8ac2cb4af1197ce88c Reviewed-by: Fabian Kosmale (cherry picked from commit d4a6673d2e66d7f0e26cb8a4265adfc5c0ef0577) Reviewed-by: Qt Cherry-pick Bot --- src/qml/jsruntime/qv4runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 200c1b06d4..bf500e6913 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1608,7 +1608,7 @@ ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine return checkedResult(engine, fo.call(&thisObject, argv, argc)); } - memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); + memmove(frame->jsFrame->args, argv, argc * sizeof(Value)); frame->init(fo.function(), frame->jsFrame->argValues(), argc, frame->callerCanHandleTailCall()); frame->setupJSFrame(frame->framePointer(), fo, fo.scope(), thisObject, -- cgit v1.2.3 From 4eb095be8a97c65b8aadb3fa4ae8c1f8119fe878 Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Wed, 19 Jul 2023 19:04:19 +0200 Subject: Fix the plugin class name handling in qt6_add_qml_plugin If the CLASS_NAME is not specified and the BACKING_LIBRARY exists we should get the plugin CLASS_NAME from the backing library, but not from the non-existing plugin target. Change-Id: I51ba2290f8f9cb8bcf4217f535c6aa4e676f7293 Reviewed-by: Alexandru Croitor (cherry picked from commit e97f1631c1983915cbf4a14ec6813d2fc0aafc67) Reviewed-by: Qt Cherry-pick Bot --- src/qml/Qt6QmlMacros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index 764be448fb..d3f958fe38 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -1053,7 +1053,7 @@ function(qt6_add_qml_plugin target) if(NOT arg_CLASS_NAME) if(NOT "${arg_BACKING_TARGET}" STREQUAL "") - get_target_property(arg_CLASS_NAME ${target} QT_QML_MODULE_CLASS_NAME) + get_target_property(arg_CLASS_NAME ${arg_BACKING_TARGET} QT_QML_MODULE_CLASS_NAME) endif() if(NOT arg_CLASS_NAME) _qt_internal_compute_qml_plugin_class_name_from_uri("${arg_URI}" arg_CLASS_NAME) -- cgit v1.2.3 From f0893061bc5ab4cadaad0689d127c5d1867dce61 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 11 Jul 2023 11:16:43 +0200 Subject: QV4::ArrayData: Fix offset calculation for sort() We cannot just sort the raw values. We have to take the offset into account. If the array wraps around the end of the allocation, we have to move it around to be contiguous. Fixes: QTBUG-58718 Change-Id: I1866b3f271d97352e250d687955af3fc54340334 Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel Reviewed-by: Qt CI Bot (cherry picked from commit f0b2fbcf47f00071a7f5f1a04258d451badcb81a) --- src/qml/jsruntime/qv4arraydata.cpp | 38 +++++++++++++--- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 69 ++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 22 deletions(-) diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 929d926534..e6e97d3bb9 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -670,8 +670,8 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const return p1s->toQString() < p2s->toQString(); } -template -void sortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan) +template +void sortHelper(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan) { top: int span = int(end - start); @@ -716,7 +716,7 @@ top: ++low; qSwap(*end, *low); - sortHelper(start, low, t, lessThan); + sortHelper(start, low, lessThan); start = low + 1; ++end; @@ -816,8 +816,36 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c ArrayElementLessThan lessThan(engine, static_cast(comparefn)); - Value *begin = thisObject->arrayData()->values.values; - sortHelper(begin, begin + len, *begin, lessThan); + const auto thisArrayData = thisObject->arrayData(); + uint startIndex = thisArrayData->mappedIndex(0); + uint endIndex = thisArrayData->mappedIndex(len - 1) + 1; + if (startIndex < endIndex) { + // Values are contiguous. Sort right away. + sortHelper( + thisArrayData->values.values + startIndex, + thisArrayData->values.values + endIndex, + lessThan); + } else { + // Values wrap around the end of the allocation. Close the gap to form a contiguous array. + // We're going to sort anyway. So we don't need to care about order. + + // ArrayElementLessThan sorts empty and undefined to the end of the array anyway, but we + // probably shouldn't rely on the unused slots to be actually undefined or empty. + + const uint gap = startIndex - endIndex; + const uint allocEnd = thisArrayData->values.alloc - 1; + for (uint i = 0; i < gap; ++i) { + const uint from = allocEnd - i; + const uint to = endIndex + i; + if (from < startIndex) + break; + + std::swap(thisArrayData->values.values[from], thisArrayData->values.values[to]); + } + + thisArrayData->offset = 0; + sortHelper(thisArrayData->values.values, thisArrayData->values.values + len, lessThan); + } #ifdef CHECK_SPARSE_ARRAYS thisObject->initSparseArray(); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 00886d79af..e048942e99 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -109,6 +109,7 @@ private slots: void gcWithNestedDataStructure(); void stacktrace(); void unshiftAndSort(); + void unshiftAndPushAndSort(); void numberParsing_data(); void numberParsing(); void automaticSemicolonInsertion(); @@ -2048,22 +2049,16 @@ void tst_QJSEngine::unshiftAndSort() { QJSEngine engine; QJSValue func = engine.evaluate(R"""( - (function (objectArr, currIdx) { - objectArr.unshift({"sortIndex": currIdx}); objectArr.sort(function(a, b) { - if(a.sortIndex > b.sortIndex) { - return 1 - } - if(a.sortIndex < b.sortIndex) { + if (a.sortIndex > b.sortIndex) + return 1; + if (a.sortIndex < b.sortIndex) return -1; - } return 0; }); - - ++currIdx; - return objectArr; + return objectArr; }) )"""); QVERIFY(func.isCallable()); @@ -2073,15 +2068,55 @@ void tst_QJSEngine::unshiftAndSort() objectArr = func.call({objectArr, i}); QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString())); const int length = objectArr.property("length").toInt(); - bool isAnyUndefined = false; + + // It did add one element + QCOMPARE(length, i + 1); + for (int x = 0; x < length; ++x) { - if (objectArr.property(x).isUndefined()) { - isAnyUndefined = true; - } + // We didn't sort cruft into the array. + QVERIFY(!objectArr.property(x).isUndefined()); + + // The array is actually sorted. + QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x); + } + } +} + +void tst_QJSEngine::unshiftAndPushAndSort() +{ + QJSEngine engine; + QJSValue func = engine.evaluate(R"""( + (function (objectArr, currIdx) { + objectArr.unshift({"sortIndex": currIdx}); + objectArr.push({"sortIndex": currIdx + 1}); + objectArr.sort(function(a, b) { + if (a.sortIndex > b.sortIndex) + return 1; + if (a.sortIndex < b.sortIndex) + return -1; + return 0; + }); + return objectArr; + }) + )"""); + QVERIFY(func.isCallable()); + QJSValue objectArr = engine.newArray(); + + for (int i = 0; i < 20; i += 2) { + objectArr = func.call({objectArr, i}); + QVERIFY2(!objectArr.isError(), qPrintable(objectArr.toString())); + const int length = objectArr.property("length").toInt(); + + // It did add 2 elements + QCOMPARE(length, i + 2); + + for (int x = 0; x < length; ++x) { + // We didn't sort cruft into the array. + QVERIFY(!objectArr.property(x).isUndefined()); + + // The array is actually sorted. + QCOMPARE(objectArr.property(x).property("sortIndex").toInt(), x); } - if (i == 4) - QEXPECT_FAIL("", "QTBUG-58718: entry becomes undefined", Continue); - QVERIFY(!isAnyUndefined); } } -- cgit v1.2.3 From fa00e39435fe8d1a0fa292401241dc8ed4eadc22 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 7 Jul 2023 13:50:07 +0200 Subject: QML: Unify treatment of wrappers when dealing with QObjectMethod A method can belong to a QObjectWrapper, a QQmlValueTypeWrapper, or a QQmlTypeWrapper. store only one wrapper pointer and allow for all variants. Furthermore, keep a reference to the wrapper so that it doesn't get garbage collected. We still need it to retrieve the metaobject, even if we're calling the method on a different instance. Fixes: QTBUG-115115 Change-Id: I1759fb687918ff79829fef776e0a93d29373b30f Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale (cherry picked from commit 63b622d5908ec2960ce5dfa301e9d3fd4d92fdb4) --- src/qml/jsruntime/qv4qmlcontext.cpp | 18 +++-- src/qml/jsruntime/qv4qobjectwrapper.cpp | 101 ++++++++++++++++++----------- src/qml/jsruntime/qv4qobjectwrapper_p.h | 30 +++++---- src/qml/qml/qqmltypewrapper.cpp | 29 +++++++-- src/qml/qml/qqmltypewrapper_p.h | 1 + src/qml/qml/qqmlvaluetypewrapper_p.h | 1 + tests/auto/qml/qjsengine/tst_qjsengine.cpp | 92 ++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 63 deletions(-) diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 5968813727..c4437a4506 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -294,8 +294,12 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r bool hasProp = false; QQmlPropertyData *propertyData = nullptr; - QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData)); + + QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, scopeObject)); + QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty( + v4, context, wrapper->d(), scopeObject, name, + QV4::QObjectWrapper::CheckRevision, &hasProp, + &propertyData)); if (hasProp) { if (hasProperty) *hasProperty = true; @@ -321,9 +325,10 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r if (QObject *contextObject = context->contextObject()) { bool hasProp = false; QQmlPropertyData *propertyData = nullptr; - result = QV4::QObjectWrapper::getQmlProperty(v4, context, contextObject, - name, QV4::QObjectWrapper::CheckRevision, - &hasProp, &propertyData); + QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, contextObject)); + result = QV4::QObjectWrapper::getQmlProperty( + v4, context, wrapper->d(), contextObject, name, + QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData); if (hasProp) { if (hasProperty) *hasProperty = true; @@ -682,8 +687,9 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec // Search context object if (QObject *contextObject = context->contextObject()) { bool hasProp = false; + QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(engine, contextObject)); result = QV4::QObjectWrapper::getQmlProperty( - engine, context, contextObject, name, + engine, context, wrapper->d(), contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp); if (hasProp) { if (base) diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index f52b4564b9..00957b9b69 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -248,7 +248,9 @@ QQmlPropertyData *QObjectWrapper::findProperty( return result; } -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property) +ReturnedValue QObjectWrapper::getProperty( + ExecutionEngine *engine, Heap::Object *wrapper, QObject *object, + QQmlPropertyData *property) { QQmlData::flushPendingBinding(object, property->coreIndex()); @@ -262,13 +264,13 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje ScopedContext global(scope, engine->qmlContext()); if (!global) global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex()); + return QV4::QObjectMethod::create(global, wrapper, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); return engine->memoryManager->allocate(object, property->coreIndex())->asReturnedValue(); } else { ExecutionContext *global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex()); + return QV4::QObjectMethod::create(global, wrapper, property->coreIndex()); } } @@ -287,7 +289,8 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje } } -static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr) +static OptionalReturnedValue getDestroyOrToStringMethod( + ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr) { int index = 0; if (name->equals(v4->id_destroy())) @@ -345,7 +348,7 @@ ReturnedValue QObjectWrapper::getQmlProperty( ExecutionEngine *v4 = engine(); - if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty)) + if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty)) return *methodValue; QQmlPropertyData local; @@ -373,13 +376,13 @@ ReturnedValue QObjectWrapper::getQmlProperty( if (hasProperty) *hasProperty = true; - return getProperty(v4, d()->object(), result); + return getProperty(v4, d(), d()->object(), result); } ReturnedValue QObjectWrapper::getQmlProperty( QV4::ExecutionEngine *engine, const QQmlRefPointer &qmlContext, - QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, - QQmlPropertyData **property) + Heap::Object *wrapper, QObject *object, String *name, + QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property) { if (QQmlData::wasDeleted(object)) { if (hasProperty) @@ -387,7 +390,7 @@ ReturnedValue QObjectWrapper::getQmlProperty( return QV4::Encode::null(); } - if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty)) + if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty)) return *methodValue; QQmlData *ddata = QQmlData::get(object, false); @@ -409,7 +412,7 @@ ReturnedValue QObjectWrapper::getQmlProperty( if (property && result != &local) *property = result; - return getProperty(engine, object, result); + return getProperty(engine, wrapper, object, result); } else { // Check if this object is already wrapped. if (!ddata || (ddata->jsWrapper.isUndefined() && @@ -429,13 +432,13 @@ ReturnedValue QObjectWrapper::getQmlProperty( Q_ASSERT(ddata); QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, wrap(engine, object)); - if (!wrapper) { + QV4::Scoped rewrapped(scope, wrap(engine, object)); + if (!rewrapped) { if (hasProperty) *hasProperty = false; return QV4::Encode::null(); } - return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty); + return rewrapped->getQmlProperty(qmlContext, name, revisionMode, hasProperty); } @@ -867,7 +870,7 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro if (pd) { QQmlPropertyData local; local.load(property); - pd->value = that->getProperty(thatEngine, thatObject, &local); + pd->value = that->getProperty(thatEngine, that->d(), thatObject, &local); } return propName->toPropertyKey(); } @@ -892,7 +895,7 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro if (pd) { QQmlPropertyData local; local.load(method); - pd->value = that->getProperty(thatEngine, thatObject, &local); + pd->value = that->getProperty(thatEngine, that->d(), thatObject, &local); } return methodName->toPropertyKey(); } @@ -925,14 +928,14 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E if (QQmlData::wasDeleted(qobj)) return QV4::Encode::undefined(); - if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) + if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d())) return *methodValue; QQmlData *ddata = QQmlData::get(qobj, false); if (!ddata || !ddata->propertyCache) { QQmlPropertyData local; QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, &local); - return property ? getProperty(engine, qobj, property) : QV4::Encode::undefined(); + return property ? getProperty(engine, This->d(), qobj, property) : QV4::Encode::undefined(); } QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); @@ -2069,35 +2072,51 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) } } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) +ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::Object *wrapper, int index) { Scope valueScope(scope); - Scoped method(valueScope, valueScope.engine->memoryManager->allocate(scope)); - method->d()->setObject(object); - - method->d()->index = index; + Scoped method( + valueScope, + valueScope.engine->memoryManager->allocate(scope, wrapper, index)); return method.asReturnedValue(); } ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); - Scoped method(valueScope, valueScope.engine->memoryManager->allocate(scope)); - method->d()->index = index; - method->d()->valueTypeWrapper.set(valueScope.engine, valueType); + Scoped method( + valueScope, + valueScope.engine->memoryManager->allocate(scope, valueType, index)); return method.asReturnedValue(); } -void Heap::QObjectMethod::init(QV4::ExecutionContext *scope) +void Heap::QObjectMethod::init(QV4::ExecutionContext *scope, Object *object, int methodIndex) { Heap::FunctionObject::init(scope); + wrapper.set(internalClass->engine, object); + index = methodIndex; } const QMetaObject *Heap::QObjectMethod::metaObject() { - if (valueTypeWrapper) - return valueTypeWrapper->metaObject(); - return object()->metaObject(); + Scope scope(internalClass->engine); + if (Scoped objectWrapper(scope, wrapper); objectWrapper) + return objectWrapper->metaObject(); + if (Scoped typeWrapper(scope, wrapper); typeWrapper) + return typeWrapper->metaObject(); + if (Scoped valueWrapper(scope, wrapper); valueWrapper) + return valueWrapper->metaObject(); + return nullptr; +} + +QObject *Heap::QObjectMethod::object() const +{ + Scope scope(internalClass->engine); + if (Scoped objectWrapper(scope, wrapper); objectWrapper) + return objectWrapper->object(); + if (Scoped typeWrapper(scope, wrapper); typeWrapper) + return typeWrapper->object(); + return nullptr; } void Heap::QObjectMethod::ensureMethodsCache() @@ -2193,14 +2212,19 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * d()->ensureMethodsCache(); Scope scope(v4); - QQmlObjectOrGadget object(d()->object()); - - if (!d()->object()) { - if (!d()->valueTypeWrapper) - return Encode::undefined(); - - object = QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr()); - } + Heap::QQmlValueTypeWrapper *valueWrapper = nullptr; + QQmlObjectOrGadget object = [&]() { + QV4::Scope scope(v4); + if (QV4::Scoped qobject(scope, d()->wrapper); qobject) + return QQmlObjectOrGadget(qobject->object()); + if (QV4::Scoped type(scope, d()->wrapper); type) + return QQmlObjectOrGadget(type->object()); + if (QV4::Scoped value(scope, d()->wrapper); value) { + valueWrapper = value->d(); + return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr()); + } + Q_UNREACHABLE(); + }(); JSCallData cData(thisObject, argv, argc); CallData *callData = cData.callData(scope); @@ -2211,8 +2235,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * // The method might change the value. const auto doCall = [&](const auto &call) { if (!method->isConstant()) { - QV4::Scoped valueTypeReference( - scope, d()->valueTypeWrapper.get()); + QV4::Scoped valueTypeReference(scope, valueWrapper); if (valueTypeReference) { QV4::ScopedValue rv(scope, call()); valueTypeReference->d()->writeBack(); diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 25b6ee11b1..82efbd2b12 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -96,7 +96,7 @@ private: }; #define QObjectMethodMembers(class, Member) \ - Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) \ + Member(class, Pointer, Object *, wrapper) \ Member(class, NoMark, QV4QPointer, qObj) \ Member(class, NoMark, int, index) @@ -107,21 +107,18 @@ DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) { int methodCount; alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)]; - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionContext *scope, Object *wrapper, int index); void destroy() { if (methods != reinterpret_cast(&_singleMethod)) delete[] methods; - qObj.destroy(); FunctionObject::destroy(); } void ensureMethodsCache(); const QMetaObject *metaObject(); - QObject *object() const { return qObj.data(); } - void setObject(QObject *o) { qObj = o; } - + QObject *object() const; }; struct QMetaObjectWrapper : FunctionObject { @@ -160,6 +157,13 @@ struct Q_QML_EXPORT QObjectWrapper : public Object static void initializeBindings(ExecutionEngine *engine); + const QMetaObject *metaObject() const + { + if (QObject *o = object()) + return o->metaObject(); + return nullptr; + } + QObject *object() const { return d()->object(); } ReturnedValue getQmlProperty( @@ -169,8 +173,8 @@ struct Q_QML_EXPORT QObjectWrapper : public Object \ static ReturnedValue getQmlProperty( ExecutionEngine *engine, const QQmlRefPointer &qmlContext, - QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, - QQmlPropertyData **property = nullptr); + Heap::Object *wrapper, QObject *object, String *name, RevisionMode revisionMode, + bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr); static bool setQmlProperty( ExecutionEngine *engine, const QQmlRefPointer &qmlContext, @@ -186,7 +190,9 @@ struct Q_QML_EXPORT QObjectWrapper : public Object void destroyObject(bool lastCall); - static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property); + static ReturnedValue getProperty( + ExecutionEngine *engine, Heap::Object *wrapper, QObject *object, + QQmlPropertyData *property); static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -243,7 +249,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE if (!o || o->internalClass != lookup->qobjectLookup.ic) return revertLookup(); - const Heap::QObjectWrapper *This = static_cast(o); + Heap::QObjectWrapper *This = static_cast(o); QObject *qobj = This->object(); if (QQmlData::wasDeleted(qobj)) return QV4::Encode::undefined(); @@ -271,7 +277,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE return revertLookup(); } - return getProperty(engine, qobj, property); + return getProperty(engine, This, qobj, property); } struct QQmlValueTypeWrapper; @@ -283,7 +289,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject enum { DestroyMethod = -1, ToStringMethod = -2 }; - static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index); + static ReturnedValue create(QV4::ExecutionContext *scope, Heap::Object *wrapper, int index); static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index); int methodIndex() const { return d()->index; } diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 17520f40f0..f705230f71 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -85,19 +85,31 @@ bool QQmlTypeWrapper::isSingleton() const return d()->type().isSingleton(); } +const QMetaObject *QQmlTypeWrapper::metaObject() const +{ + const QQmlType type = d()->type(); + if (!type.isValid()) + return nullptr; + + if (type.isSingleton()) + return type.metaObject(); + + return type.attachedPropertiesType(QQmlEnginePrivate::get(engine()->qmlEngine())); +} + QObject *QQmlTypeWrapper::object() const { const QQmlType type = d()->type(); if (!type.isValid()) return nullptr; - QQmlEngine *qmlEngine = engine()->qmlEngine(); + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine()); if (type.isSingleton()) - return QQmlEnginePrivate::get(qmlEngine)->singletonInstance(type); + return qmlEngine->singletonInstance(type); return qmlAttachedPropertiesObject( d()->object, - type.attachedPropertiesFunction(QQmlEnginePrivate::get(qmlEngine))); + type.attachedPropertiesFunction(qmlEngine)); } QObject* QQmlTypeWrapper::singletonObject() const @@ -226,7 +238,9 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // check for property. bool ok; - const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); + const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty( + v4, context, w->d(), qobjectSingleton, name, + QV4::QObjectWrapper::IgnoreRevision, &ok); if (hasProperty) *hasProperty = ok; @@ -267,8 +281,11 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons QObject *ao = qmlAttachedPropertiesObject( object, type.attachedPropertiesFunction(QQmlEnginePrivate::get(v4->qmlEngine()))); - if (ao) - return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty); + if (ao) { + return QV4::QObjectWrapper::getQmlProperty( + v4, context, w->d(), ao, name, QV4::QObjectWrapper::IgnoreRevision, + hasProperty); + } // Fall through to base implementation } diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index e2ca8e92ac..53c0997cdc 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -102,6 +102,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object V4_NEEDS_DESTROY bool isSingleton() const; + const QMetaObject *metaObject() const; QObject *object() const; QObject *singletonObject() const; diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index bcc46b1038..489239b6a7 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -163,6 +163,7 @@ public: int typeId() const; QMetaType type() const; bool write(QObject *target, int propertyIndex) const; + const QMetaObject *metaObject() const { return d()->metaObject(); } QQmlPropertyData dataForPropertyKey(PropertyKey id) const; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index e048942e99..2d5e04d2fc 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -288,6 +288,7 @@ private slots: void symbolToVariant(); + void garbageCollectedObjectMethodBase(); public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -5704,6 +5705,97 @@ void tst_QJSEngine::symbolToVariant() QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)")); } +class PACHelper : public QObject { + Q_OBJECT +public: + Q_INVOKABLE bool shExpMatch(const QString &, const QString &) { return false; } + Q_INVOKABLE QString dnsResolve(const QString &) { return QString{}; } +}; + +class ProxyAutoConf { +public: + void exposeQObjectMethodsAsGlobal(QJSEngine *engine, QObject *object) + { + QJSValue helper = engine->newQObject(object); + QJSValue g = engine->globalObject(); + QJSValueIterator it(helper); + while (it.hasNext()) { + it.next(); + if (!it.value().isCallable()) + continue; + g.setProperty(it.name(), it.value()); + } + } + + bool parse(const QString & pacBytes) + { + jsFindProxyForURL = QJSValue(); + engine = std::make_unique(); + exposeQObjectMethodsAsGlobal(engine.get(), new PACHelper); + engine->evaluate(pacBytes); + jsFindProxyForURL = engine->globalObject().property(QStringLiteral("FindProxyForURL")); + return true; + } + + QString findProxyForUrl(const QString &url, const QString &host) + { + QJSValueList args; + args << url << host; + engine->collectGarbage(); + QJSValue callResult = jsFindProxyForURL.call(args); + return callResult.toString().trimmed(); + } + +private: + std::unique_ptr engine; + QJSValue jsFindProxyForURL; +}; + +QString const pacstring = R"js( +function FindProxyForURL(host) { + list_split_all = Array( + "oneoneoneoneo.oneo.oneo.oneoneo.one", + "twotwotwotwotw.otwo.twot.wotwotw.otw", + "threethreethr.eeth.reet.hreethr.eet", + "fourfourfourfo.urfo.urfo.urfourf.our", + "fivefivefivef.ivef.ivef.ivefive.fiv", + "sixsixsixsixsi.xsix.sixs.ixsixsi.xsi", + "sevensevenseve.nsev.ense.venseve.nse", + "eight.eighteigh.tei", + "*.nin.eninen.ine" + ) + list_myip_direct = + "10.254.0.0/255.255.0.0" + for (i = 0; i < list_split_all.length; ++i) + for (j = 0; j < list_myip_direct.length; ++j) + shExpMatch(host, list_split_all) + shExpMatch() + dnsResolve()} +)js"; + +void tst_QJSEngine::garbageCollectedObjectMethodBase() +{ + ProxyAutoConf proxyConf; + bool pac_read = false; + + const auto processUrl = [&](QString const &url, QString const &host) + { + if (!pac_read) { + proxyConf.parse(pacstring); + pac_read = true; + } + return proxyConf.findProxyForUrl(url, host); + }; + + const QString url = QStringLiteral("https://servername.domain.test"); + const QString host = QStringLiteral("servername.domain.test"); + + for (size_t i = 0; i < 5; ++i) { + auto future = std::async(processUrl, url, host); + QCOMPARE(future.get(), QLatin1String("Error: Insufficient arguments")); + } +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3 From 8133cba371d90c0ab9925bb308a2facd68879d19 Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Sun, 7 May 2023 00:19:47 +0200 Subject: Make sure that source file exists when copying QML RESOURCES Some resource files could be generated after the configure step. We need to make sure that we attempt to copy source files only if file already exists when we configuring the project. Otherwise we postpone this operation till build time. Fixes: QTBUG-113439 Change-Id: Id939d70eeeb482da8831b8bc5322af50b5ab7f88 Reviewed-by: Joerg Bornemann Reviewed-by: Amir Masoud Abdol (cherry picked from commit 4ffea9dd1617aeaf6efaeb0d9a361848e34dbd11) Reviewed-by: Alexandru Croitor --- src/qml/Qt6QmlMacros.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index d3f958fe38..9d8456969d 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -1429,9 +1429,11 @@ function(qt6_target_qml_sources target) get_filename_component(file_out_dir ${file_out} DIRECTORY) file(MAKE_DIRECTORY ${file_out_dir}) - execute_process(COMMAND - ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out} - ) + if(EXISTS "${file_absolute}") + execute_process(COMMAND + ${CMAKE_COMMAND} -E copy_if_different ${file_absolute} ${file_out} + ) + endif() add_custom_command(OUTPUT ${file_out} COMMAND ${CMAKE_COMMAND} -E copy ${file_src} ${file_out} -- cgit v1.2.3 From b048fa40db2a55c5dcb0641005a5af6f205a3cc0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 27 Jul 2023 22:07:37 +0200 Subject: QQuickWidget: give each mapped mouse event the same QEventPoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Counter-intuitively, this is done by setting the timestamp. Every time you construct a new mouse event, you always need to call setTimestamp() for the obvious reason: the timestamp is not a ctor argument, but it's important for some event receivers. But the non-obvious reason is that QMutableEventPoint::setTimestamp() has other useful side effects: the velocity calculation is done there, sensibly enough. But to make that possible, it also looks up the persistent QEventPoint with the same ID as the one in the QMouseEvent, and that's the most important to fix QTBUG-114258 specifically. QQuickFlickablePrivate::handleMoveEvent() calculates delta as QEventPoint::position() - mapFromGlobal(globalPressPosition()) and then QQuickFlickablePrivate::drag() does the drag threshold check. If globalPressPosition() is 0,0 because the persistent QEventPoint was not passed along in the mapped QMouseEvent, that delta is nearly always over the drag threshold. So Flickable took the exclusive grab at the first possible opportunity: the second move after press. Fixes: QTBUG-114258 Change-Id: Icaf7fb8fde0ef01b486ccf16852ef0f6cfb6a64c Reviewed-by: Tor Arne Vestbø (cherry picked from commit 7ae07f49c1f7f86f37512c74bf0a49244ae46e1f) Reviewed-by: Qt Cherry-pick Bot --- src/quickwidgets/qquickwidget.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 3bb9d9fbe1..a25c185905 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1466,6 +1466,8 @@ void QQuickWidget::mouseMoveEvent(QMouseEvent *e) // top-level window always. QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + // It's not just the timestamp but also the globalPressPosition, velocity etc. + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); e->setAccepted(mappedEvent.isAccepted()); } @@ -1481,10 +1483,12 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e) // See QTBUG-25831 QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + pressEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent); e->setAccepted(pressEvent.isAccepted()); QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); } @@ -1544,6 +1548,7 @@ void QQuickWidget::mousePressEvent(QMouseEvent *e) QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); e->setAccepted(mappedEvent.isAccepted()); } @@ -1557,6 +1562,7 @@ void QQuickWidget::mouseReleaseEvent(QMouseEvent *e) QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(), e->button(), e->buttons(), e->modifiers(), e->source()); + mappedEvent.setTimestamp(e->timestamp()); QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent); e->setAccepted(mappedEvent.isAccepted()); } -- cgit v1.2.3 From b82a9b7b71bbe5fde0beeb265f46e079480e991e Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 31 Jul 2023 08:53:25 +0200 Subject: QQmlPropertyBinding: Correctly link observers after undefined valued binding Calling both setObservers and prependObservers doesn't make sense: setObservers is for the case where we don't have a binding, but obviously we do have one here. Note that the test case still passes even without the fix - it will however cause a memory leak which can be detected by ASAN. The leak is fixed by this patch. Fixes: QTBUG-115251 Change-Id: I4b420e05f49acf764da6a05af522390005276f49 Reviewed-by: Qt CI Bot Reviewed-by: Ulf Hermann Reviewed-by: Mitch Curtis (cherry picked from commit 366fee74fc42d3c1db44fd8540f37ff290c3b662) Reviewed-by: Fabian Kosmale --- src/qml/qml/qqmlpropertybinding.cpp | 4 +--- tests/auto/qml/qqmlecmascript/CMakeLists.txt | 1 + .../data/qpropertyResetCorrectlyLinked.qml | 8 ++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 18 ++++++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp index ba3188fe12..be37b33c81 100644 --- a/src/qml/qml/qqmlpropertybinding.cpp +++ b/src/qml/qml/qqmlpropertybinding.cpp @@ -284,10 +284,8 @@ void QQmlPropertyBinding::handleUndefinedAssignment(QQmlEnginePrivate *ep, void // reset might have changed observers (?), so refresh firstObserver firstObserver = bindingDataPointer.firstObserver(); bindingData->d_ref() = reinterpret_cast(this) | QtPrivate::QPropertyBindingData::BindingBit; - if (firstObserver) { - bindingDataPointer.setObservers(firstObserver.ptr); + if (firstObserver) prependObserver(firstObserver); - } } else { QQmlError qmlError; auto location = jsExpression()->sourceLocation(); diff --git a/tests/auto/qml/qqmlecmascript/CMakeLists.txt b/tests/auto/qml/qqmlecmascript/CMakeLists.txt index 70f010c082..bb1040db16 100644 --- a/tests/auto/qml/qqmlecmascript/CMakeLists.txt +++ b/tests/auto/qml/qqmlecmascript/CMakeLists.txt @@ -21,6 +21,7 @@ qt_internal_add_test(tst_qqmlecmascript Qt::Network Qt::QmlPrivate Qt::QuickTestUtilsPrivate + Qt::QuickPrivate TESTDATA ${test_data} ) diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml new file mode 100644 index 0000000000..490fec2dc8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml @@ -0,0 +1,8 @@ +import QtQuick + +Item { + property var val: undefined + property var observes: width + width: val + implicitWidth: 200 +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index a472f826ed..df75729d8e 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -52,6 +52,8 @@ #include #include #include +#include +#include #include #include @@ -394,6 +396,7 @@ private slots: void qpropertyBindingHandlesUndefinedWithoutResetCorrectly_data(); void qpropertyBindingHandlesUndefinedWithoutResetCorrectly(); void qpropertyBindingRestoresObserverAfterReset(); + void qpropertyBindingObserverCorrectlyLinkedAfterReset(); void hugeRegexpQuantifiers(); void singletonTypeWrapperLookup(); void getThisObject(); @@ -9207,6 +9210,21 @@ void tst_qqmlecmascript::qpropertyBindingRestoresObserverAfterReset() QVERIFY(o->property("steps").toInt() > 3); } +void tst_qqmlecmascript::qpropertyBindingObserverCorrectlyLinkedAfterReset() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("qpropertyResetCorrectlyLinked.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + std::unique_ptr o(c.create()); + QVERIFY(o); + QCOMPARE(o->property("width"), 200); + auto item = qobject_cast(o.get()); + auto itemPriv = QQuickItemPrivate::get(item); + QBindingStorage *storage = qGetBindingStorage(itemPriv); + QPropertyBindingDataPointer ptr { storage->bindingData(&itemPriv->width) }; + QCOMPARE(ptr.observerCount(), 1); +} + void tst_qqmlecmascript::hugeRegexpQuantifiers() { QJSEngine engine; -- cgit v1.2.3 From 646f5a4e9048356aae7b6c213e53fb3e663888fd Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 24 Jul 2023 19:10:23 +0200 Subject: sg: Fix dirty bit when changing mirror for QSGSimpleTextureNode Not setting DirtyGeometry leads to subtle update problems in scenes where the value is dynamically toggled. Most commonly the changed mirroring value does not always take effect, but rather only when something else triggers something geometry-related. Change-Id: I6a00e1399ebeec3120b0f695cf0e10507f0ad807 Reviewed-by: Andy Nichols (cherry picked from commit 45baa640b934977737300876e80e36012a08c5b2) Reviewed-by: Qt Cherry-pick Bot --- src/quick/scenegraph/util/qsgsimpletexturenode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp index 5feda2384a..aa18bc6819 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -292,7 +292,7 @@ void QSGSimpleTextureNode::setTextureCoordinatesTransform(QSGSimpleTextureNode:: return; d->texCoordMode = mode; qsgsimpletexturenode_update(&m_geometry, texture(), m_rect, d->sourceRect, d->texCoordMode); - markDirty(DirtyMaterial); + markDirty(DirtyGeometry | DirtyMaterial); } /*! -- cgit v1.2.3 From 6749c203fca34b1e77e0fcfcdc199edec31bc2d8 Mon Sep 17 00:00:00 2001 From: Andreas Eliasson Date: Wed, 2 Aug 2023 12:06:20 +0200 Subject: Doc: Add \br between \value and \table Whenever a \table follows a \value, the contents of the two commands merge, which breaks the table formatting. Fix this by adding a \br before the \table. Fixes: QTBUG-115537 Change-Id: I8e5836a8fc82dc9e59eb5fe210da9996d4f66d95 Reviewed-by: Safiyyah Moosa Reviewed-by: Paul Wicking (cherry picked from commit e80921b01f225021d1936a8ac0079553a2a186b6) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquickimage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index c11bba8b9d..1b7fad62c5 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -286,6 +286,7 @@ void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap) \value Image.TileVertically the image is stretched horizontally and tiled vertically \value Image.TileHorizontally the image is stretched vertically and tiled horizontally \value Image.Pad the image is not transformed + \br \table -- cgit v1.2.3 From d1e0af7a2689f364b19f1c0cfba9c2f6ec8acd98 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Wed, 2 Aug 2023 03:48:31 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Id8939a0d63e342a561cfebcdec2b4239a8e82db4 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 93a24b6e0a..8b61709844 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 3d0ef9b4f4710fc64892bdcf03d7271e0e02ef05 + ref: c6e2686555ba46ddd5c39f5a14a6d5188a423af3 required: true ../tqtc-qtimageformats: - ref: aa53b38786d065a812b7714609d0f98056b8657f + ref: 4175b6b78ed824bff579ef47ac627d3571b0e3a5 required: false ../tqtc-qtshadertools: - ref: d420dd0b983d7ea637970b9824f9a94d8f975e77 + ref: c0dc87a2dbaa5f295fe4720c8816f97fccde9910 required: false ../tqtc-qtsvg: - ref: e5dc6d27ec7f6e6b267193088883fda1ae1fed90 + ref: 71e12739ca3835fdd0ce052fbf7a2d069fe91520 required: false -- cgit v1.2.3 From 72ff7b1737634726e4b95ec526f522fef812ce52 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Fri, 4 Aug 2023 13:46:02 +0200 Subject: ListView: add extra note about reusing items The current documentation leads the reader to think that all items are always pooled as soon as they are flicked out the viewport. Add a small note that explains that this is not always the case. Task-number: QTBUG-115317 Change-Id: I31bc7920ae7fa2477764f1f2ce33a29ef6825a3f Reviewed-by: Mitch Curtis (cherry picked from commit c0ab315b8582e39c95e0755e26424bb331a755d5) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquicklistview.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index cde6c87dbc..c7ccf4dbad 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -2213,6 +2213,10 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject * \note While an item is in the pool, it might still be alive and respond to connected signals and bindings. + \note For an item to be pooled, it needs to be completely flicked out of the bounds + of the view, \e including the extra margins set with \l {ListView::}{cacheBuffer.} + Some items will also never be pooled or reused, such as \l currentItem. + The following example shows a delegate that animates a spinning rectangle. When it is pooled, the animation is temporarily paused: -- cgit v1.2.3 From 7fbf81c315978e48982d4fa72cbcca71927219d3 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Fri, 11 Aug 2023 11:16:08 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: I545d350ba70070b8828e71e77e5ddcf5e73d1046 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 8b61709844..f45928d9b3 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: c6e2686555ba46ddd5c39f5a14a6d5188a423af3 + ref: 5ed7eb0d724f4ec52f9dd3056934de95cc83da23 required: true ../tqtc-qtimageformats: - ref: 4175b6b78ed824bff579ef47ac627d3571b0e3a5 + ref: 00eb4e90600c4192876c6bb03a8251c4015d7059 required: false ../tqtc-qtshadertools: - ref: c0dc87a2dbaa5f295fe4720c8816f97fccde9910 + ref: ae9a62ae575f83814070c5a469cca1185f0590a4 required: false ../tqtc-qtsvg: - ref: 71e12739ca3835fdd0ce052fbf7a2d069fe91520 + ref: 0fc239edb7794d29bae5c4934b2a906e0ec47cd1 required: false -- cgit v1.2.3 From aaa1b060fa1b798469165bc480593ef0f763c1dd Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 8 Aug 2023 14:54:01 +0200 Subject: QML: Make notify list thread safe We keep the notifyList itself alive until the QQmlData itself is deleted. This way any isSignalConnected() called while an intermediate dtor runs can safely access it. We use atomics to make the concurrent access to the pointer and the connection mask defined behavior. However, we never need anything but relaxed semantics when accessing it. Fixes: QTBUG-105090 Change-Id: I82537be86e5cc33c2a3d76ec639fcbac87eb45ad Reviewed-by: Fabian Kosmale Reviewed-by: Qt CI Bot (cherry picked from commit 691956654c1acab356ce704c58602cc3a99fabc3) Reviewed-by: Sami Shalayel --- src/qml/qml/qqmldata_p.h | 64 ++++++++++++++++++++++------------ src/qml/qml/qqmlengine.cpp | 85 ++++++++++++++++++++++++++++++---------------- 2 files changed, 98 insertions(+), 51 deletions(-) diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 377a55461e..a8ede300bf 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -155,24 +155,24 @@ public: }; struct NotifyList { - quint64 connectionMask; - - quint16 maximumTodoIndex; - quint16 notifiesSize; - - QQmlNotifierEndpoint *todo; - QQmlNotifierEndpoint**notifies; + QAtomicInteger connectionMask; + QQmlNotifierEndpoint *todo = nullptr; + QQmlNotifierEndpoint**notifies = nullptr; + quint16 maximumTodoIndex = 0; + quint16 notifiesSize = 0; void layout(); private: void layout(QQmlNotifierEndpoint*); }; - NotifyList *notifyList; + QAtomicPointer notifyList; - inline QQmlNotifierEndpoint *notify(int index); + inline QQmlNotifierEndpoint *notify(int index) const; void addNotify(int index, QQmlNotifierEndpoint *); int endpointCount(int index); bool signalHasEndpoint(int index) const; - void disconnectNotifiers(); + + enum class DeleteNotifyList { Yes, No }; + void disconnectNotifiers(DeleteNotifyList doDelete); // The context that created the C++ object; not refcounted to prevent cycles QQmlContextData *context = nullptr; @@ -326,23 +326,31 @@ bool QQmlData::wasDeleted(const QObject *object) return ddata && ddata->isQueuedForDeletion; } -QQmlNotifierEndpoint *QQmlData::notify(int index) +inline bool isIndexInConnectionMask(quint64 connectionMask, int index) +{ + return connectionMask & (1ULL << quint64(index % 64)); +} + +QQmlNotifierEndpoint *QQmlData::notify(int index) const { + // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. + Q_ASSERT(index <= 0xFFFF); - if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) { + NotifyList *list = notifyList.loadRelaxed(); + if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index)) return nullptr; - } else if (index < notifyList->notifiesSize) { - return notifyList->notifies[index]; - } else if (index <= notifyList->maximumTodoIndex) { - notifyList->layout(); - } - if (index < notifyList->notifiesSize) { - return notifyList->notifies[index]; - } else { - return nullptr; + if (index < list->notifiesSize) + return list->notifies[index]; + + if (index <= list->maximumTodoIndex) { + list->layout(); + if (index < list->notifiesSize) + return list->notifies[index]; } + + return nullptr; } /* @@ -351,7 +359,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index) */ inline bool QQmlData::signalHasEndpoint(int index) const { - return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64))); + // This can be called from any thread. + // We still use relaxed semantics. If we're on a thread different from the "home" thread + // of the QQmlData, two interesting things might happen: + // + // 1. The list might go away while we hold it. In that case we are dealing with an object whose + // QObject dtor is being executed concurrently. This is UB already without the notify lists. + // Therefore, we don't need to consider it. + // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case + // we "misreport" the endpoint. Since ordering of events across threads is inherently + // nondeterministic, either result is correct in that case. We can accept it. + + NotifyList *list = notifyList.loadRelaxed(); + return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index); } bool QQmlData::hasBindingBit(int coreIndex) const diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 0f79b005db..46245458eb 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -529,7 +529,7 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) // Disconnect the notifiers now - during object destruction this would be too late, since // the disconnect call wouldn't be able to call disconnectNotify(), as it isn't possible to // get the metaobject anymore. - d->disconnectNotifiers(); + d->disconnectNotifiers(QQmlData::DeleteNotifyList::No); } } @@ -590,7 +590,10 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in // QQmlEngine to emit signals from a different thread. These signals are then automatically // marshalled back onto the QObject's thread and handled by QML from there. This is tested // by the qqmlecmascript::threadSignal() autotest. - if (!ddata->notifyList) + + // Relaxed semantics here. If we're on a different thread we might schedule a useless event, + // but that should be rare. + if (!ddata->notifyList.loadRelaxed()) return; auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed(); @@ -1447,49 +1450,73 @@ void QQmlData::releaseDeferredData() void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint) { - if (!notifyList) { - notifyList = (NotifyList *)malloc(sizeof(NotifyList)); - notifyList->connectionMask = 0; - notifyList->maximumTodoIndex = 0; - notifyList->notifiesSize = 0; - notifyList->todo = nullptr; - notifyList->notifies = nullptr; + // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. + + NotifyList *list = notifyList.loadRelaxed(); + + if (!list) { + list = new NotifyList; + // We don't really care when this change takes effect on other threads. The notifyList can + // only become non-null once in the life time of a QQmlData. It becomes null again when the + // underlying QObject is deleted. At that point any interaction with the QQmlData is UB + // anyway. So, for all intents and purposese, the list becomes non-null once and then stays + // non-null "forever". We can apply relaxed semantics. + notifyList.storeRelaxed(list); } Q_ASSERT(!endpoint->isConnected()); index = qMin(index, 0xFFFF - 1); - notifyList->connectionMask |= (1ULL << quint64(index % 64)); - if (index < notifyList->notifiesSize) { + // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other + // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying + // the conenctionMask in the presence of concurrent modification, any result is correct. + list->connectionMask.storeRelaxed( + list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64))); - endpoint->next = notifyList->notifies[index]; + if (index < list->notifiesSize) { + endpoint->next = list->notifies[index]; if (endpoint->next) endpoint->next->prev = &endpoint->next; - endpoint->prev = ¬ifyList->notifies[index]; - notifyList->notifies[index] = endpoint; - + endpoint->prev = &list->notifies[index]; + list->notifies[index] = endpoint; } else { - notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index); + list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index); - endpoint->next = notifyList->todo; + endpoint->next = list->todo; if (endpoint->next) endpoint->next->prev = &endpoint->next; - endpoint->prev = ¬ifyList->todo; - notifyList->todo = endpoint; + endpoint->prev = &list->todo; + list->todo = endpoint; } } -void QQmlData::disconnectNotifiers() +void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete) { - if (notifyList) { - while (notifyList->todo) - notifyList->todo->disconnect(); - for (int ii = 0; ii < notifyList->notifiesSize; ++ii) { - while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii]) + // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. + if (NotifyList *list = notifyList.loadRelaxed()) { + while (QQmlNotifierEndpoint *todo = list->todo) + todo->disconnect(); + for (int ii = 0; ii < list->notifiesSize; ++ii) { + while (QQmlNotifierEndpoint *ep = list->notifies[ii]) ep->disconnect(); } - free(notifyList->notifies); - free(notifyList); - notifyList = nullptr; + free(list->notifies); + + if (doDelete == DeleteNotifyList::Yes) { + // We can only get here from QQmlData::destroyed(), and that can only come from the + // the QObject dtor. If you're still sending signals at that point you have UB already + // without any threads. Therefore, it's enough to apply relaxed semantics. + notifyList.storeRelaxed(nullptr); + delete list; + } else { + // We can use relaxed semantics here. The worst thing that can happen is that some + // signal is falsely reported as connected. Signal connectedness across threads + // is not quite deterministic anyway. + list->connectionMask.storeRelaxed(0); + list->maximumTodoIndex = 0; + list->notifiesSize = 0; + list->notifies = nullptr; + + } } } @@ -1573,7 +1600,7 @@ void QQmlData::destroyed(QObject *object) guard->objectDestroyed(object); } - disconnectNotifiers(); + disconnectNotifiers(DeleteNotifyList::Yes); if (extendedData) delete extendedData; -- cgit v1.2.3 From 1e944f6942dfcda424ddd8d3567426cfb9255117 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 15 Aug 2023 09:21:24 +0200 Subject: QJSEngine::evaluate: Cross-reference QQmlExpression QJSEngine::evaluate is meant for plain JavaScript; guide readers wanting to use it with QML to the correct place. Change-Id: If8016a4cca2999d95bac47e83159b1e10eb5a0d5 Reviewed-by: Ulf Hermann (cherry picked from commit fead337daa3e339676b05f0f152864455752dfb3) Reviewed-by: Qt Cherry-pick Bot --- src/qml/jsapi/qjsengine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 9035200fb5..03563ccd41 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -509,6 +509,9 @@ static QUrl urlForFileName(const QString &fileName) The script code will be evaluated in the context of the global object. + \note If you need to evaluate inside a QML context, use \l QQmlExpression + instead. + The evaluation of \a program can cause an \l{Script Exceptions}{exception} in the engine; in this case the return value will be the exception that was thrown (typically an \c{Error} object; see @@ -539,6 +542,8 @@ static QUrl urlForFileName(const QString &fileName) exception value will still be returned. Use \c exceptionStackTrace->isEmpty() to distinguish whether the value was a normal or an exceptional return value. + + \sa QQmlExpression::evaluate */ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber, QStringList *exceptionStackTrace) { -- cgit v1.2.3 From c461eb74cbbbb26a2855010397366b5a747aedad Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Wed, 16 Aug 2023 03:48:48 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Ib74f91e9f5ccf4f2f72083fa166e48713d518641 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index f45928d9b3..730452e88b 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 5ed7eb0d724f4ec52f9dd3056934de95cc83da23 + ref: c89afcc399b99825182183ff6b20b2585fbf2e52 required: true ../tqtc-qtimageformats: - ref: 00eb4e90600c4192876c6bb03a8251c4015d7059 + ref: 73d00867d0b39b8fce418f2a36e0fbddfd2ef0b2 required: false ../tqtc-qtshadertools: - ref: ae9a62ae575f83814070c5a469cca1185f0590a4 + ref: 77a945242eef5b60a0ecbb068892c37cde7dbc8a required: false ../tqtc-qtsvg: - ref: 0fc239edb7794d29bae5c4934b2a906e0ec47cd1 + ref: a59791b5bfef139b72530b4c7f5ff654731fb4e1 required: false -- cgit v1.2.3 From 7a2b1f486a219c02cbe739ba7c4ecb2408bfdae5 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Fri, 18 Aug 2023 04:50:25 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: I50c26df8e243fc451abdb30123f20196c2f1ede9 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 730452e88b..dbffb9ccc3 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: c89afcc399b99825182183ff6b20b2585fbf2e52 + ref: 4d30a3922bc8ee3c58d2611dabbefe0a5395316d required: true ../tqtc-qtimageformats: - ref: 73d00867d0b39b8fce418f2a36e0fbddfd2ef0b2 + ref: c03cb0b0fb9b113963210840cceb33451e0e0eaa required: false ../tqtc-qtshadertools: - ref: 77a945242eef5b60a0ecbb068892c37cde7dbc8a + ref: a7116808bfe244d7d44ab72f28badbc7e9e2d73e required: false ../tqtc-qtsvg: - ref: a59791b5bfef139b72530b4c7f5ff654731fb4e1 + ref: e56a72d7430d2e1763e5ab76ed948944061f40ea required: false -- cgit v1.2.3 From 0e2a389512b50d78a2f753fa9ebbd0aae6f4d5fc Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Wed, 23 Aug 2023 03:49:17 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: If75bd00a9f5970ad9dbaf969a2c4a11d179dba21 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index dbffb9ccc3..b16e01929d 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 4d30a3922bc8ee3c58d2611dabbefe0a5395316d + ref: a9fe8dde7a2e4699537b4609692419c77da9446c required: true ../tqtc-qtimageformats: - ref: c03cb0b0fb9b113963210840cceb33451e0e0eaa + ref: 3e5fd5653bddaf0d740d2f71787a0dcdee5348f1 required: false ../tqtc-qtshadertools: - ref: a7116808bfe244d7d44ab72f28badbc7e9e2d73e + ref: 75892adbdee96c51ad6286b57465786c3408ca53 required: false ../tqtc-qtsvg: - ref: e56a72d7430d2e1763e5ab76ed948944061f40ea + ref: b15ce59a224299b4a9bf0b4670b5f7080ad9ad70 required: false -- cgit v1.2.3 From ffa28d9e127e0f6a0b6501f4ff66e23a015a1a97 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 21 Aug 2023 17:06:14 +0200 Subject: QtQml: Reset context object when destroying it from QObjectWrapper There may be other objects that still hold on to the context data. We should not leave them with a dangling context obejct. Fixes: QTBUG-116228 Change-Id: I3dddd20b13956408d0e66c3a46e59377e45d91e5 Reviewed-by: Fabian Kosmale Reviewed-by: Qt CI Bot (cherry picked from commit 1d3385e9887e912a79835c7defc878edf4ab7ec5) --- src/qml/jsruntime/qv4qobjectwrapper.cpp | 2 ++ .../qqmlcontext/data/gcDeletesContextObject.qml | 5 ++++ tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp | 29 ++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 00957b9b69..c43a2d75cf 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1249,6 +1249,8 @@ void QObjectWrapper::destroyObject(bool lastCall) if (ddata && ddata->ownContext) { Q_ASSERT(ddata->ownContext.data() == ddata->context); ddata->ownContext->emitDestruction(); + if (ddata->ownContext->contextObject() == h->object()) + ddata->ownContext->setContextObject(nullptr); ddata->ownContext = nullptr; ddata->context = nullptr; } diff --git a/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml new file mode 100644 index 0000000000..a478a587df --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/gcDeletesContextObject.qml @@ -0,0 +1,5 @@ +import QtQml +QtObject { + property Component c: MyItem {} + property QtObject o: c.createObject() +} diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index e586604862..c839de05c1 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -75,6 +75,8 @@ private slots: void contextObjectHierarchy(); void destroyContextProperty(); + void gcDeletesContextObject(); + private: QQmlEngine engine; }; @@ -1004,6 +1006,33 @@ void tst_qqmlcontext::destroyContextProperty() // TODO: Or are we? } +void tst_qqmlcontext::gcDeletesContextObject() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("gcDeletesContextObject.qml")); + + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + + QVERIFY(!o.isNull()); + + QPointer contextObject = o->property("o").value(); + QVERIFY(contextObject != nullptr); + + QQmlData *data = QQmlData::get(contextObject); + QVERIFY(data); + QQmlRefPointer context = data->ownContext; + QVERIFY(context); + QCOMPARE(context->contextObject(), contextObject); + + o->setProperty("o", QVariant::fromValue(nullptr)); + QCOMPARE(o->property("o").value(), nullptr); + engine.collectGarbage(); + + QTRY_VERIFY(contextObject.isNull()); + QCOMPARE(context->contextObject(), nullptr); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" -- cgit v1.2.3 From c8f6fa66adeea6313d2f6c27c0b2efe6695d9321 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 17 Jul 2023 12:54:04 +0800 Subject: Doc: link to best practices from Interacting with QML Objects from C++ There's already a warning in that section, but we can also link to the best practices page. Change-Id: I31fcab885e7b95bac907605a038408c3019d297a Reviewed-by: Fabian Kosmale (cherry picked from commit ca83d345a58314eb7bf15afd982685d17ec3f37d) Reviewed-by: Qt Cherry-pick Bot --- src/qml/doc/src/cppintegration/topic.qdoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc index 14a27c9a3f..1a77039e7c 100644 --- a/src/qml/doc/src/cppintegration/topic.qdoc +++ b/src/qml/doc/src/cppintegration/topic.qdoc @@ -205,7 +205,8 @@ dynamically load and introspect objects through the Qt meta object system. \include warning.qdocinc For more information on accessing QML objects from C++, see the documentation on -\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}. +\l{qtqml-cppintegration-interactqmlfromcpp.html}{Interacting with QML Objects from C++}, +and the \l {Exposing Data from C++ to QML} section of the Best Practices page. \section1 Data Type Conversion Between QML and C++ -- cgit v1.2.3 From 539f90973c4ed220f7fde53624a5f1914a6f8f54 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Tue, 29 Aug 2023 03:54:38 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Id3002a9f15182d90296e5e7d3dfc4bb092ca0e98 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index b16e01929d..75afe6a193 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: a9fe8dde7a2e4699537b4609692419c77da9446c + ref: b0a0a0fcfee99e9dfdd355bc0ffe2091583c70de required: true ../tqtc-qtimageformats: - ref: 3e5fd5653bddaf0d740d2f71787a0dcdee5348f1 + ref: b1b49101af2e4798c7701fa3495a749644f4ba83 required: false ../tqtc-qtshadertools: - ref: 75892adbdee96c51ad6286b57465786c3408ca53 + ref: f10439f16eea9dd478d18cb2f0b2a4335b4c4c12 required: false ../tqtc-qtsvg: - ref: b15ce59a224299b4a9bf0b4670b5f7080ad9ad70 + ref: a2363ae9e080afb2fa93d399e9ec02d8931d209d required: false -- cgit v1.2.3 From 5d2504dbadb555bb872ab3b74b3640b6549766a1 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 2 Mar 2023 13:46:54 +0100 Subject: qmlformat: Fix handling of object destructuring in functions We weren't considering the bindingTarget of PatternElements, which broke functions using object destructuring in the parameter list. While the resulting formatting is not very pretty, it at least is syntactically correct and the formatter doesn't crash. Fixes: QTBUG-111511 Change-Id: Ie22fed2160538bf0c5733b5aced0fdac64621346 Reviewed-by: Ulf Hermann (cherry picked from commit 310428e292243770fb3bd3b48e9f136b56d68b42) Reviewed-by: Fabian Kosmale --- src/qmldom/qqmldomreformatter.cpp | 13 ++++++++++--- .../qmlformat/data/arrowFunctionWithBinding.formatted.qml | 8 ++++++++ tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml | 8 ++++++++ tests/auto/qml/qmlformat/tst_qmlformat.cpp | 3 +++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml create mode 100644 tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp index 1861bd1dd1..7a5b15cdcf 100644 --- a/src/qmldom/qqmldomreformatter.cpp +++ b/src/qmldom/qqmldomreformatter.cpp @@ -867,12 +867,15 @@ protected: out(ast->identifierToken); } out(ast->lparenToken); - if (ast->isArrowFunction && ast->formals && ast->formals->next) + const bool needParentheses = ast->formals && + (ast->formals->next || + (ast->formals->element && ast->formals->element->bindingTarget)); + if (ast->isArrowFunction && needParentheses) out("("); int baseIndent = lw.increaseIndent(1); accept(ast->formals); lw.decreaseIndent(1, baseIndent); - if (ast->isArrowFunction && ast->formals && ast->formals->next) + if (ast->isArrowFunction && needParentheses) out(")"); out(ast->rparenToken); if (ast->isArrowFunction && !ast->formals) @@ -963,7 +966,11 @@ protected: bool visit(FormalParameterList *ast) override { for (FormalParameterList *it = ast; it; it = it->next) { - out(it->element->bindingIdentifier.toString()); // TODO + // compare FormalParameterList::finish + if (auto id = it->element->bindingIdentifier.toString(); !id.startsWith(u"arg#")) + out(id); + if (it->element->bindingTarget) + accept(it->element->bindingTarget); if (it->next) out(", "); } diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml new file mode 100644 index 0000000000..2a47b2f152 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.formatted.qml @@ -0,0 +1,8 @@ +import QtQuick + +Item { Component.onCompleted: { let f = ([]) => {}; + let g = ([a]) => {}; + let h = ([a, b]) => {}; + let i = ([a, ...b]) => {}; + } +} diff --git a/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml new file mode 100644 index 0000000000..2a47b2f152 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/arrowFunctionWithBinding.qml @@ -0,0 +1,8 @@ +import QtQuick + +Item { Component.onCompleted: { let f = ([]) => {}; + let g = ([a]) => {}; + let h = ([a, b]) => {}; + let i = ([a, ...b]) => {}; + } +} diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp index 3837e193e5..aa8ed2e9da 100644 --- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp +++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp @@ -257,6 +257,9 @@ void TestQmlformat::testFormat_data() QTest::newRow("ecmaScriptClassInQml") << "ecmaScriptClassInQml.qml" << "ecmaScriptClassInQml.formatted.qml" << QStringList {}; + QTest::newRow("arrowFunctionWithBinding") + << "arrowFunctionWithBinding.qml" + << "arrowFunctionWithBinding.formatted.qml" << QStringList{}; } void TestQmlformat::testFormat() -- cgit v1.2.3 From a673dbd33abd03a65c50649c758925507f7933f4 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 19 Jan 2023 10:56:50 +0100 Subject: QJSEngine: Fix potential JS stack overflow cauased by spread operator createSpreadArguments could in theory allocate a (nearly) unbounded number of QV4::Values. Avoid this by checking whether we approach jsStackTop. This fixes CVE-2022-43591. Change-Id: I01aecb979da47b7261688c9f185dc33a50a579a5 Reviewed-by: Ulf Hermann (cherry picked from commit 6511aa4344c1d47ede8546540fe70bdff8523545) Reviewed-by: Fabian Kosmale --- src/qml/jsruntime/qv4runtime.cpp | 5 +++++ tests/auto/qml/qjsengine/tst_qjsengine.cpp | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index bf500e6913..8a8ff93019 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1544,6 +1544,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) if (done->booleanValue()) break; ++argCount; + constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements + if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) { + scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator")); + return { nullptr, 0 }; + } v = scope.alloc(); } } diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 2d5e04d2fc..81ac086294 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -289,6 +289,8 @@ private slots: void symbolToVariant(); void garbageCollectedObjectMethodBase(); + void spreadNoOverflow(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -5796,6 +5798,16 @@ void tst_QJSEngine::garbageCollectedObjectMethodBase() } } +void tst_QJSEngine::spreadNoOverflow() +{ + QJSEngine engine; + + const QString program = QString::fromLatin1("var a = [] ;a.length = 555840;Math.max(...a)"); + const QJSValue result = engine.evaluate(program); + QVERIFY(result.isError()); + QCOMPARE(result.errorType(), QJSValue::RangeError); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3 From cc8c757381a0680591492788dfb88d48b51519e9 Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Wed, 30 Aug 2023 14:41:09 +0200 Subject: Fix linking of static plugins to android module targets All android targets are created as module targets, using the 'add_library( MODULE ...)' call. When we select the linking type for static QML modules, we don't take into account those and this ends up with interface linking of Android apps to static QML modules. Consider module library targets in this case. Change-Id: I8151764eca6391feb4b0bd76f313314f22d50f7b Reviewed-by: Ulf Hermann Reviewed-by: Alexandru Croitor (cherry picked from commit 982a4d68af1b7483d91290a7b7c1c335fe9a52bc) Reviewed-by: Qt Cherry-pick Bot --- src/qml/Qt6QmlMacros.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index 9d8456969d..f7dd8ca6e4 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -2143,7 +2143,8 @@ but this file does not exist. Possible reasons include: # across those libraries to the end target (executable or shared library). # The plugin initializers will be linked via usage requirements from the plugin target. get_target_property(target_type ${target} TYPE) - if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY") + if(target_type STREQUAL "EXECUTABLE" OR target_type STREQUAL "SHARED_LIBRARY" + OR target_type STREQUAL "MODULE_LIBRARY") set(link_type "PRIVATE") else() set(link_type "INTERFACE") -- cgit v1.2.3 From 97584434b1f36d469ee5ae36bb137ddac23d6026 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Thu, 31 Aug 2023 03:52:48 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: I4764a24ea2816cbf52bbef9af20e5132c7e055ba Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 75afe6a193..00a0cdd700 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: b0a0a0fcfee99e9dfdd355bc0ffe2091583c70de + ref: d705543c2099e6cd690b6d8471a2250117c16d65 required: true ../tqtc-qtimageformats: - ref: b1b49101af2e4798c7701fa3495a749644f4ba83 + ref: e08e3323ef423981a90cb490eb95d68ffacd0b98 required: false ../tqtc-qtshadertools: - ref: f10439f16eea9dd478d18cb2f0b2a4335b4c4c12 + ref: 57f8556e87c15df34fdb356ef0304f60c09d0abf required: false ../tqtc-qtsvg: - ref: a2363ae9e080afb2fa93d399e9ec02d8931d209d + ref: c00b438586174fc7668451934f577511aee8c6c8 required: false -- cgit v1.2.3 From 25144668297fff4aa6e4f910270561705e8f37ab Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Tue, 29 Aug 2023 09:50:58 +0800 Subject: Doc: Fix unqualified access in FolderListModel example Change-Id: Iacbb8cc5f9d887cdd7f14f1d948e37e27e5c6083 Reviewed-by: Ulf Hermann (cherry picked from commit 6afb1ec82bdca7a1ea3a7a5e12a3f143962860ea) Reviewed-by: Qt Cherry-pick Bot --- src/labs/folderlistmodel/qquickfolderlistmodel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp index bf44bd6acd..6b89967252 100644 --- a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp @@ -304,6 +304,7 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path) Component { id: fileDelegate + required property string fileName Text { text: fileName } } -- cgit v1.2.3 From 6346bc171e4f6ddb52d7960e3c8f8247586f8f2e Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Thu, 31 Aug 2023 14:09:55 +0800 Subject: Doc: Fix incorrect default value for FolderListModel.folder This behavior has been in place since Qt 5 (or perhaps even earlier) Change-Id: If60192e4302f5b1316d29dc42139eb66855789a1 Reviewed-by: Leena Miettinen Reviewed-by: Fabian Kosmale (cherry picked from commit e041ba46f2f407a306746b6a1dbe7d48a40401b7) Reviewed-by: Qt Cherry-pick Bot --- src/labs/folderlistmodel/qquickfolderlistmodel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp index 6b89967252..696c570035 100644 --- a/src/labs/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/labs/folderlistmodel/qquickfolderlistmodel.cpp @@ -421,7 +421,8 @@ QModelIndex QQuickFolderListModel::index(int row, int , const QModelIndex &) con The value must be a \c file: or \c qrc: URL, or a relative URL. - The default value is an invalid URL. + The default value is the application's working directory at the time + when the FolderListModel is first initialized. */ QUrl QQuickFolderListModel::folder() const { -- cgit v1.2.3 From f40894d76c716a4f6b76cf999d9fb26fae71e37e Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Mon, 4 Sep 2023 03:54:55 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Ibf2de7eb0f07e9e6a4c4426b74a8483c4db4d448 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 00a0cdd700..a5aca2ce23 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: d705543c2099e6cd690b6d8471a2250117c16d65 + ref: 09ede199597db386660abe5c66c1628c0e394ceb required: true ../tqtc-qtimageformats: - ref: e08e3323ef423981a90cb490eb95d68ffacd0b98 + ref: 22db5b4fee6a33c5b44e6121701d80f1417a0439 required: false ../tqtc-qtshadertools: - ref: 57f8556e87c15df34fdb356ef0304f60c09d0abf + ref: 17866751887d3431c573a892b219fc22253d17a6 required: false ../tqtc-qtsvg: - ref: c00b438586174fc7668451934f577511aee8c6c8 + ref: 81e44ff3905dd8c39920298d5d0e48505dedb6ca required: false -- cgit v1.2.3 From c1be4de1f286095d660e6a83be2f2c262c1c29cf Mon Sep 17 00:00:00 2001 From: Tony Leinonen Date: Wed, 22 Dec 2021 18:25:57 +0200 Subject: Fix incorrect behavior after displace transition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to.x and to.y use itemX and itemY. This caused them to be off sync. Override item's fromValues with itemX and itemY calls to sync them back. Task-number: QTBUG-89193 Change-Id: If65f11a55101ee7cbc463806bcef0072fea00ebd Reviewed-by: Qt CI Bot Reviewed-by: Tomi Korpipää Reviewed-by: Shawn Rutledge (cherry picked from commit ebcdde7128c542acddcb07f7abf164492485f4a1) Reviewed-by: Ulf Hermann --- src/quick/items/qquickitemviewtransition.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp index a074f77aea..cfda0165af 100644 --- a/src/quick/items/qquickitemviewtransition.cpp +++ b/src/quick/items/qquickitemviewtransition.cpp @@ -125,6 +125,8 @@ void QQuickItemViewTransitionJob::startTransition(QQuickItemViewTransitionableIt actions << QQuickStateAction(item->item, QLatin1String("x"), QVariant(to.x())); actions << QQuickStateAction(item->item, QLatin1String("y"), QVariant(to.y())); + actions[0].fromValue = item->itemX(); + actions[1].fromValue = item->itemY(); m_transitioner->runningJobs << this; QQuickTransitionManager::transition(actions, trans, item->item); } -- cgit v1.2.3 From 74448f94137e3e3d9237de13e2837b6a98e95df8 Mon Sep 17 00:00:00 2001 From: Tony Leinonen Date: Tue, 4 May 2021 17:12:49 +0300 Subject: GridView: Set content position when changing cell size Content size was calculated before the new data was applied. This caused problems when calculating the column count while resizing the window. Fixes: QTBUG-92998 Change-Id: Ia4401c0e34cd11dc9e6c24a0320dbf0ca2f59ab9 Reviewed-by: Shawn Rutledge (cherry picked from commit 6254b383309a0c3cd273264466105a7e3599a1ed) --- src/quick/items/qquickgridview.cpp | 2 + .../auto/quick/qquickgridview/data/qtbug92998.qml | 53 ++++++++++++++++++++++ .../quick/qquickgridview/tst_qquickgridview.cpp | 20 ++++++++ 3 files changed, 75 insertions(+) create mode 100644 tests/auto/quick/qquickgridview/data/qtbug92998.qml diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 6fc80157f9..788b0a7ed8 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -1673,6 +1673,7 @@ void QQuickGridView::setCellWidth(qreal cellWidth) d->updateViewport(); emit cellWidthChanged(); d->forceLayoutPolish(); + QQuickFlickable::setContentX(d->contentXForPosition(d->position())); } } @@ -1690,6 +1691,7 @@ void QQuickGridView::setCellHeight(qreal cellHeight) d->updateViewport(); emit cellHeightChanged(); d->forceLayoutPolish(); + QQuickFlickable::setContentY(d->contentYForPosition(d->position())); } } /*! diff --git a/tests/auto/quick/qquickgridview/data/qtbug92998.qml b/tests/auto/quick/qquickgridview/data/qtbug92998.qml new file mode 100644 index 0000000000..d7db5fb843 --- /dev/null +++ b/tests/auto/quick/qquickgridview/data/qtbug92998.qml @@ -0,0 +1,53 @@ +import QtQuick 2.12 +import QtQuick.Window 2.12 + +Item { + width: 450 + height: 650 + GridView { + objectName: "gridview" + id: gridView + width: 450 + height: 650 + layoutDirection: Qt.RightToLeft + property int cells: calcCells(width) + cellWidth: width / cells + cellHeight: cellWidth + + delegate: Component { + Item { + width: gridView.cellWidth + height: gridView.cellHeight + Rectangle { + anchors { + fill: parent + margins: 10 + } + color: "green" + } + } + } + model: [ + { number: "1" }, + { number: "2" }, + { number: "3" }, + { number: "4" }, + { number: "5" }, + { number: "6" }, + { number: "7" }, + { number: "8" }, + { number: "9" }, + { number: "10" }, + { number: "11" }, + { number: "12" }, + { number: "13" }, + { number: "14" }, + { number: "15" }, + { number: "16" }]; + function calcCells(w) { + var rw = 120; + var c = Math.max(1, Math.round(w / rw)); + return c; + } + } +} diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 7e319fb28a..9532aa546c 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -215,6 +215,7 @@ private slots: void positionViewAtBeginningAfterResizingCells(); void QTBUG_48870_fastModelUpdates(); void QTBUG_86255(); + void resizeDynamicCellWidthRtL(); void keyNavigationEnabled(); void releaseItems(); @@ -6839,6 +6840,25 @@ void tst_QQuickGridView::QTBUG_86255() QTRY_COMPARE(view->isFlicking(), false); } +void tst_QQuickGridView::resizeDynamicCellWidthRtL() +{ + QScopedPointer window(createView()); + QTRY_VERIFY(window); + window->setSource(testFileUrl("qtbug92998.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickGridView *gridview = findItem(window->rootObject(), "gridview"); + QTRY_VERIFY(gridview != nullptr); + QVERIFY(QQuickTest::qWaitForItemPolished(gridview)); + gridview->setWidth(460); + QVERIFY(QQuickTest::qWaitForItemPolished(gridview)); + QTRY_COMPARE(gridview->contentX(), 0.f); + gridview->setWidth(360); + QVERIFY(QQuickTest::qWaitForItemPolished(gridview)); + QTRY_COMPARE(gridview->contentX(), 0.f); +} + void tst_QQuickGridView::releaseItems() { QScopedPointer view(createView()); -- cgit v1.2.3 From dccee331ce9f02b5722cb33227ec51e0df6e42dc Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 27 Feb 2023 11:27:31 +0100 Subject: qt_add_qml_module: Error out if singleton is marked as internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A singleton cannot be marked as internal – the qmldir parser cannot even parse such a declaration, and the runtime would have no idea how to deal with such a type. The cmake code so far generated two entries – one line where the type is a singleton, and the other one where the type is declared as internal – with the end result that the type was not treated as a singleton, at least not by qmllint. Once the engine actually supports internal singletons, we would need to adjust the qmldir file to have singleton and internal in one line. As that's not supported so far, we now simply error out with a fatal warning. Task-number: QTBUG-111532 Change-Id: Idf0843c5548b15f79700a45b4197c256a6dda5cd Reviewed-by: Alexey Edelev (cherry picked from commit 10a310c089e0d9731067abd6bb56771b51719043) --- src/qml/Qt6QmlMacros.cmake | 6 ++++++ tests/auto/cmake/CMakeLists.txt | 1 + .../cmake/test_internal_singleton/CMakeLists.txt | 23 ++++++++++++++++++++++ tests/auto/cmake/test_internal_singleton/Test.qml | 4 ++++ 4 files changed, 34 insertions(+) create mode 100644 tests/auto/cmake/test_internal_singleton/CMakeLists.txt create mode 100644 tests/auto/cmake/test_internal_singleton/Test.qml diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index f7dd8ca6e4..d805d37feb 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -1520,6 +1520,12 @@ function(qt6_target_qml_sources target) get_source_file_property(qml_file_singleton ${qml_file_src} QT_QML_SINGLETON_TYPE) get_source_file_property(qml_file_internal ${qml_file_src} QT_QML_INTERNAL_TYPE) + if (qml_file_singleton AND qml_file_internal) + message(FATAL_ERROR + "${qml_file_src} is marked as both internal and as a " + "singleton, but singletons cannot be internal!") + endif() + if (NOT qml_file_versions) set(qml_file_versions ${qml_module_files_versions}) endif() diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index ddd5f633e7..c4c8d8382c 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -65,6 +65,7 @@ if(TARGET Qt::Qml) _qt_internal_test_expect_pass(test_plugins) endif() _qt_internal_test_expect_pass(empty_qmldir) + _qt_internal_test_expect_fail(test_internal_singleton) endif() if(TARGET Qt::Quick) diff --git a/tests/auto/cmake/test_internal_singleton/CMakeLists.txt b/tests/auto/cmake/test_internal_singleton/CMakeLists.txt new file mode 100644 index 0000000000..f1db46a0f9 --- /dev/null +++ b/tests/auto/cmake/test_internal_singleton/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + + +cmake_minimum_required(VERSION 3.19) + +project(test_internal_singleton) + +find_package(Qt6 REQUIRED COMPONENTS Qml) + +qt_standard_project_setup() + +set_source_files_properties(Test.qml PROPERTIES + QT_QML_SINGLETON_TYPE TRUE + QT_QML_INTERNAL_TYPE TRUE +) + +qt_add_qml_module(test_internal_singleton + URI Controls + VERSION 1.0 + QML_FILES + Test.qml +) diff --git a/tests/auto/cmake/test_internal_singleton/Test.qml b/tests/auto/cmake/test_internal_singleton/Test.qml new file mode 100644 index 0000000000..a2eb03bd4e --- /dev/null +++ b/tests/auto/cmake/test_internal_singleton/Test.qml @@ -0,0 +1,4 @@ +pragma singleton +import QtQml + +QtObject {} -- cgit v1.2.3 From 15b94845717f187c3061fdae62de34c022c90dc3 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 8 Sep 2023 10:24:52 +0200 Subject: Fix extension lookup in QQmlJSScope Do not go over extension's base types since this is not how the engine logic work. Instead, only assess the direct extension type when traversing the base type hierarchy. The special cases are value types which still need base type extension traversal (e.g. due to Number and NumberPrototype extensions on primitives) and QObject type itself (we expect to see ObjectPrototype properties / methods of it) Change-Id: I92ba979202b33f16e1a7b948d4f1e79df37f2669 Reviewed-by: Ulf Hermann (cherry picked from commit a287e862d467b21a8007698f8390088bdf887b44) Reviewed-by: Sami Shalayel --- src/qmlcompiler/qqmljsscope.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index fa71242582..9629f081bf 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -70,21 +70,35 @@ static auto getQQmlJSScopeFromSmartPtr(const From &p) -> decltype(p.get()) template static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check) { + if (!type) + return false; + // NB: among other things, getQQmlJSScopeFromSmartPtr() also resolves const // vs non-const pointer issue, so use it's return value as the type using T = decltype( getQQmlJSScopeFromSmartPtr(std::declval())); + const bool isValueType = (type->accessSemantics() == QQmlJSScope::AccessSemantics::Value); + QDuplicateTracker seen; for (T scope = type; scope && !seen.hasSeen(scope); scope = getQQmlJSScopeFromSmartPtr(scope->baseType())) { - // Extensions override their base types - for (T extension = getQQmlJSScopeFromSmartPtr(scope->extensionType()); - extension && !seen.hasSeen(extension); - extension = getQQmlJSScopeFromSmartPtr(extension->baseType())) { + QDuplicateTracker seenExtensions; + // Extensions override the types they extend. However, usually base + // types of extensions are ignored. The unusual cases are when we + // have a value type or when we have the QObject type, in which case + // we also study the extension's base type hierarchy. + const bool isQObject = scope->internalName() == QLatin1String("QObject"); + T extension = getQQmlJSScopeFromSmartPtr(scope->extensionType()); + do { + if (!extension || seenExtensions.hasSeen(extension)) + break; + if (check(extension)) return true; - } + + extension = getQQmlJSScopeFromSmartPtr(extension->baseType()); + } while (isValueType || isQObject); if (check(scope)) return true; -- cgit v1.2.3 From 9544b9fffc7202df4bb462068dddcf2f642fb534 Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Fri, 8 Sep 2023 07:32:20 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: Ib731b7794d051e0ffd90f76e08e55cd4c5d566ee Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index a5aca2ce23..281a53180e 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 09ede199597db386660abe5c66c1628c0e394ceb + ref: 3fe5ac02a2d8b3ce034c4119d9ec1365b9372b41 required: true ../tqtc-qtimageformats: - ref: 22db5b4fee6a33c5b44e6121701d80f1417a0439 + ref: e18a7eb356353287b84c3db4d9669eae7abd7f0e required: false ../tqtc-qtshadertools: - ref: 17866751887d3431c573a892b219fc22253d17a6 + ref: 651dfc1a305d8cf0bbf2fc841befe68184a11d6d required: false ../tqtc-qtsvg: - ref: 81e44ff3905dd8c39920298d5d0e48505dedb6ca + ref: fdd1dae7cdb4e442ca5056daf79e2eeebbf9fe1d required: false -- cgit v1.2.3 From 1f09a96627c4a4a84a9bdd3220d8b7ac9c4dfd91 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 11 Aug 2023 20:56:14 +0800 Subject: SwipeView: reposition items that aren't visible after startup too 7a5bbc23150fe73f86e9eb81d252cb8ebc673784 repositioned items upon startup, but didn't account for items added afterwards. Do that here so that semi-transparent pages don't show pages beneath them. Fixes: QTBUG-115468 Change-Id: If5ca587a6c14ddae5108c0a45d13fa2d290d1865 Reviewed-by: Qt CI Bot Reviewed-by: Richard Moe Gustavsen (cherry picked from commit 2cf897f2a13a29c701ba39f18e88b75cc6694f3b) --- src/quicktemplates2/qquickswipeview.cpp | 34 ++++++++++++---------- .../quickcontrols2/controls/data/tst_swipeview.qml | 13 +++++++-- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/quicktemplates2/qquickswipeview.cpp b/src/quicktemplates2/qquickswipeview.cpp index 2f10a998d3..33bf68b091 100644 --- a/src/quicktemplates2/qquickswipeview.cpp +++ b/src/quicktemplates2/qquickswipeview.cpp @@ -110,7 +110,7 @@ class QQuickSwipeViewPrivate : public QQuickContainerPrivate Q_DECLARE_PUBLIC(QQuickSwipeView) public: - void resizeItem(QQuickItem *item); + void resizeItem(int index, QQuickItem *item); void resizeItems(); static QQuickSwipeViewPrivate *get(QQuickSwipeView *view); @@ -144,25 +144,29 @@ public: int currentIndex = -1; }; +void QQuickSwipeViewPrivate::resizeItem(int index, QQuickItem *item) +{ + QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors; + // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors + if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) { + qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item."; + item->setProperty("_q_QQuickSwipeView_warned", true); + } + if (orientation == Qt::Horizontal) + item->setPosition({index * (contentItem->width() + spacing), 0}); + else + item->setPosition({0, index * (contentItem->height() + spacing)}); + item->setSize(QSizeF(contentItem->width(), contentItem->height())); +} + void QQuickSwipeViewPrivate::resizeItems() { Q_Q(QQuickSwipeView); const int count = q->count(); for (int i = 0; i < count; ++i) { QQuickItem *item = itemAt(i); - if (item) { - QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors; - // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors - if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) { - qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item."; - item->setProperty("_q_QQuickSwipeView_warned", true); - } - if (orientation == Qt::Horizontal) - item->setPosition({i * (contentItem->width() + spacing), 0}); - else - item->setPosition({0, i * (contentItem->height() + spacing)}); - item->setSize(QSizeF(contentItem->width(), contentItem->height())); - } + if (item) + resizeItem(i, item); } } @@ -312,7 +316,7 @@ void QQuickSwipeView::itemAdded(int index, QQuickItem *item) { Q_D(QQuickSwipeView); if (isComponentComplete()) - item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height())); + d->resizeItem(index, item); QQuickSwipeViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item)); if (attached) QQuickSwipeViewAttachedPrivate::get(attached)->update(this, index); diff --git a/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml b/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml index 254c876f08..3c6bbaf91c 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml @@ -703,17 +703,26 @@ TestCase { const control = createTemporaryObject(translucentPages, testCase, {width: 320, height: 200}) verify(control) compare(control.orientation, Qt.Horizontal) - for (var i = 0; i < control.count; ++i) { + for (let i = 0; i < control.count; ++i) { const page = control.itemAt(i) // control.contentItem.width + control.spacing == 310; except Imagine style has contentItem.width == 320 compare(page.x, i * 310) compare(page.y, 0) } control.orientation = Qt.Vertical - for (var i = 0; i < control.count; ++i) { + for (let i = 0; i < control.count; ++i) { const page = control.itemAt(i) compare(page.y, i * (control.contentItem.height + control.spacing)) compare(page.x, 0) } + + // QTBUG-115468: add a page after startup and check that that works too. + control.orientation = Qt.Horizontal + let page4 = page.createObject(control, { text: "page 4", "font.pointSize": 40 }) + control.insertItem(control.count, page4) + compare(page4.x, (control.count - 1) * 310) + compare(page4.y, 0) + compare(page4.width, control.contentItem.width) + compare(page4.height, control.contentItem.height) } } -- cgit v1.2.3 From f714a79dd0ba69227cf04b0db096423f2ca787e6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 24 Oct 2022 13:35:39 +0200 Subject: QQuickPopup: forget touchId when closed on press outside QQuickPopupPrivate::touchId became dangling state when a popup is closed by pressing a touchpoint outside (closePolicy CloseOnPressOutside). The menu doesn't receive the release event, and the overlay filters the release after the menu is already closed, so we need to reset the stored touchId immediately. Fixes: QTBUG-103268 Change-Id: I99ff065b707b4b5f123021368e9d71f2c4b2ca70 Reviewed-by: Volker Hilsheimer (cherry picked from commit 75990e754bf18501583ea38cb0270a52bc2860cf) Reviewed-by: Qt CI Bot --- src/quicktemplates2/qquickpopup.cpp | 1 + .../quickcontrols2/qquickpopup/tst_qquickpopup.cpp | 137 +++++++++++---------- 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index 01f05b4684..826bad727f 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -340,6 +340,7 @@ void QQuickPopupPrivate::closeOrReject() dialog->reject(); else q->close(); + touchId = -1; } bool QQuickPopupPrivate::tryClose(const QPointF &pos, QQuickPopup::ClosePolicy flags) diff --git a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp index d2cfda9375..a587c085a4 100644 --- a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp +++ b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,7 @@ private slots: private: static bool hasWindowActivation(); + QScopedPointer touchScreen = QScopedPointer(QTest::createTouchDevice()); }; tst_QQuickPopup::tst_QQuickPopup() @@ -246,7 +248,6 @@ void tst_QQuickPopup::overlay() QFETCH(bool, modal); QFETCH(bool, dim); - QScopedPointer device(QTest::createTouchDevice()); QQuickControlsApplicationHelper helper(this, source); QVERIFY2(helper.ready, helper.failureMessage()); @@ -337,13 +338,13 @@ void tst_QQuickPopup::overlay() QVERIFY(overlay->isVisible()); - QTest::touchEvent(window, device.data()).press(0, QPoint(1, 1)); + QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(1, 1)); QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount); QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount); QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount); QCOMPARE(overlayAttachedReleasedSignal.count(), overlayReleaseCount); - QTest::touchEvent(window, device.data()).release(0, QPoint(1, 1)); + QTest::touchEvent(window, touchScreen.data()).release(0, QPoint(1, 1)); QCOMPARE(overlayPressedSignal.count(), overlayPressCount); QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount); QCOMPARE(overlayAttachedPressedSignal.count(), overlayPressCount); @@ -358,28 +359,28 @@ void tst_QQuickPopup::overlay() QVERIFY(overlay->isVisible()); QVERIFY(!button->isPressed()); - QTest::touchEvent(window, device.data()).press(0, button->mapToScene(QPointF(1, 1)).toPoint()); + QTest::touchEvent(window, touchScreen.data()).press(0, button->mapToScene(QPointF(1, 1)).toPoint()); QVERIFY(popup->isVisible()); QVERIFY(overlay->isVisible()); QCOMPARE(button->isPressed(), !modal); QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount); QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount); - QTest::touchEvent(window, device.data()).stationary(0).press(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); + QTest::touchEvent(window, touchScreen.data()).stationary(0).press(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); QVERIFY(popup->isVisible()); QVERIFY(overlay->isVisible()); QCOMPARE(button->isPressed(), !modal); QCOMPARE(overlayPressedSignal.count(), ++overlayPressCount); QCOMPARE(overlayReleasedSignal.count(), overlayReleaseCount); - QTest::touchEvent(window, device.data()).release(0, button->mapToScene(QPointF(1, 1)).toPoint()).stationary(1); + QTest::touchEvent(window, touchScreen.data()).release(0, button->mapToScene(QPointF(1, 1)).toPoint()).stationary(1); QTRY_VERIFY(!popup->isVisible()); QVERIFY(!overlay->isVisible()); QVERIFY(!button->isPressed()); QCOMPARE(overlayPressedSignal.count(), overlayPressCount); QCOMPARE(overlayReleasedSignal.count(), ++overlayReleaseCount); - QTest::touchEvent(window, device.data()).release(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); + QTest::touchEvent(window, touchScreen.data()).release(1, button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); QVERIFY(!popup->isVisible()); QVERIFY(!overlay->isVisible()); QVERIFY(!button->isPressed()); @@ -484,21 +485,21 @@ void tst_QQuickPopup::closePolicy_data() QTest::addColumn("source"); QTest::addColumn("closePolicy"); - QTest::newRow("Window:NoAutoClose") << "window.qml"<< static_cast(QQuickPopup::NoAutoClose); - QTest::newRow("Window:CloseOnPressOutside") << "window.qml"<< static_cast(QQuickPopup::CloseOnPressOutside); - QTest::newRow("Window:CloseOnPressOutsideParent") << "window.qml"<< static_cast(QQuickPopup::CloseOnPressOutsideParent); - QTest::newRow("Window:CloseOnPressOutside|Parent") << "window.qml"<< static_cast(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); - QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml"<< static_cast(QQuickPopup::CloseOnReleaseOutside); - QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml"<< static_cast(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); - QTest::newRow("Window:CloseOnEscape") << "window.qml"<< static_cast(QQuickPopup::CloseOnEscape); - - QTest::newRow("ApplicationWindow:NoAutoClose") << "applicationwindow.qml"<< static_cast(QQuickPopup::NoAutoClose); - QTest::newRow("ApplicationWindow:CloseOnPressOutside") << "applicationwindow.qml"<< static_cast(QQuickPopup::CloseOnPressOutside); - QTest::newRow("ApplicationWindow:CloseOnPressOutsideParent") << "applicationwindow.qml"<< static_cast(QQuickPopup::CloseOnPressOutsideParent); - QTest::newRow("ApplicationWindow:CloseOnPressOutside|Parent") << "applicationwindow.qml"<< static_cast(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); - QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml"<< static_cast(QQuickPopup::CloseOnReleaseOutside); - QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml"<< static_cast(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); - QTest::newRow("ApplicationWindow:CloseOnEscape") << "applicationwindow.qml"<< static_cast(QQuickPopup::CloseOnEscape); + QTest::newRow("Window:NoAutoClose") << "window.qml" << static_cast(QQuickPopup::NoAutoClose); + QTest::newRow("Window:CloseOnPressOutside") << "window.qml" << static_cast(QQuickPopup::CloseOnPressOutside); + QTest::newRow("Window:CloseOnPressOutsideParent") << "window.qml" << static_cast(QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("Window:CloseOnPressOutside|Parent") << "window.qml" << static_cast(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml" << static_cast(QQuickPopup::CloseOnReleaseOutside); + QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml" << static_cast(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); + QTest::newRow("Window:CloseOnEscape") << "window.qml" << static_cast(QQuickPopup::CloseOnEscape); + + QTest::newRow("ApplicationWindow:NoAutoClose") << "applicationwindow.qml" << static_cast(QQuickPopup::NoAutoClose); + QTest::newRow("ApplicationWindow:CloseOnPressOutside") << "applicationwindow.qml" << static_cast(QQuickPopup::CloseOnPressOutside); + QTest::newRow("ApplicationWindow:CloseOnPressOutsideParent") << "applicationwindow.qml" << static_cast(QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("ApplicationWindow:CloseOnPressOutside|Parent") << "applicationwindow.qml" << static_cast(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml" << static_cast(QQuickPopup::CloseOnReleaseOutside); + QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml" << static_cast(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); + QTest::newRow("ApplicationWindow:CloseOnEscape") << "applicationwindow.qml" << static_cast(QQuickPopup::CloseOnEscape); } void tst_QQuickPopup::closePolicy() @@ -534,56 +535,64 @@ void tst_QQuickPopup::closePolicy() // wait for dimmer QTest::qWait(50); - // press outside popup and its parent - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); - if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) || closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent)) - QTRY_VERIFY(!popup->isVisible()); - else - QVERIFY(popup->isOpened()); + for (int i = 0; i < 2; ++i) { + // press outside popup and its parent + QTest::touchEvent(window, touchScreen.data()).press(0, {1, 1}); + QQuickTouchUtils::flush(window); + if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) || closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent)) + QTRY_VERIFY(!popup->isVisible()); + else + QVERIFY(popup->isOpened()); - popup->open(); - QVERIFY(popup->isVisible()); - QTRY_VERIFY(popup->isOpened()); + popup->open(); + QVERIFY(popup->isVisible()); + QTRY_VERIFY(popup->isOpened()); - // release outside popup and its parent - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); - if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) || closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent)) - QTRY_VERIFY(!popup->isVisible()); - else - QVERIFY(popup->isOpened()); + // release outside popup and its parent + QTest::touchEvent(window, touchScreen.data()).release(0, {1, 1}); + QQuickTouchUtils::flush(window); + if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) || closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent)) + QTRY_VERIFY(!popup->isVisible()); + else + QVERIFY(popup->isOpened()); - popup->open(); - QVERIFY(popup->isVisible()); - QTRY_VERIFY(popup->isOpened()); + popup->open(); + QVERIFY(popup->isVisible()); + QTRY_VERIFY(popup->isOpened()); - // press outside popup but inside its parent - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + 1, button->y() + 1)); - if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent)) - QTRY_VERIFY(!popup->isVisible()); - else - QVERIFY(popup->isOpened()); + // press outside popup but inside its parent + QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(button->x() + 1, button->y() + 1)); + QQuickTouchUtils::flush(window); + if (closePolicy.testFlag(QQuickPopup::CloseOnPressOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnPressOutsideParent)) + QTRY_VERIFY(!popup->isVisible()); + else + QVERIFY(popup->isOpened()); - popup->open(); - QVERIFY(popup->isVisible()); - QTRY_VERIFY(popup->isOpened()); + popup->open(); + QVERIFY(popup->isVisible()); + QTRY_VERIFY(popup->isOpened()); - // release outside popup but inside its parent - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + 1, button->y() + 1)); - if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent)) - QTRY_VERIFY(!popup->isVisible()); - else - QVERIFY(popup->isOpened()); + // release outside popup but inside its parent + QTest::touchEvent(window, touchScreen.data()).release(0, QPoint(button->x() + 1, button->y() + 1)); + QQuickTouchUtils::flush(window); + if (closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutside) && !closePolicy.testFlag(QQuickPopup::CloseOnReleaseOutsideParent)) + QTRY_VERIFY(!popup->isVisible()); + else + QVERIFY(popup->isOpened()); - popup->open(); - QVERIFY(popup->isVisible()); - QTRY_VERIFY(popup->isOpened()); + popup->open(); + QVERIFY(popup->isVisible()); + QTRY_VERIFY(popup->isOpened()); - // press inside and release outside - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(button->x() + popup->x() + 1, - button->y() + popup->y() + 1)); - QVERIFY(popup->isOpened()); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1)); - QVERIFY(popup->isOpened()); + // press inside and release outside + QTest::touchEvent(window, touchScreen.data()).press(0, QPoint(button->x() + popup->x() + 1, + button->y() + popup->y() + 1)); + QQuickTouchUtils::flush(window); + QVERIFY(popup->isOpened()); + QTest::touchEvent(window, touchScreen.data()).release(0, {1, 1}); + QQuickTouchUtils::flush(window); + QVERIFY(popup->isOpened()); + } // escape QTest::keyClick(window, Qt::Key_Escape); -- cgit v1.2.3 From 609f5c7203f2308a19dea04aebc2dcaac7ff2514 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 27 Apr 2023 15:39:17 +0200 Subject: Remove some references to Q_ENUMS Q_ENUM is the preferred way to expose enums to the metatype system since quite a few years. Tast-number: QTBUG-113229 Change-Id: Ie778d0d4620e73742ca59bb37112e939489ffa4d Reviewed-by: Ulf Hermann (cherry picked from commit 66a200011ea93959349ad7f8d6b34844ccbb418c) Reviewed-by: Qt Cherry-pick Bot --- src/qml/qml/qqmlmetatype.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 4b73984636..c5204d2747 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -282,7 +282,7 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, } } - // Clone Q_ENUMS + // Clone enums registered with the metatype system for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { QMetaEnum enumerator = mo->enumerator(ii); -- cgit v1.2.3 From 1aa66214bf36fd43da9626876c758b8f1a0bb9b2 Mon Sep 17 00:00:00 2001 From: Kai Uwe Broulik Date: Thu, 17 Aug 2023 23:09:21 +0200 Subject: QV4::QObjectWrapper: Use the object's actual meta type Otherwise, a derived type's methods will not be found. In our particular case we tried to call a method of 'B*' on a QML attached property declared as 'A*'. This restores the previous behavior of using the object() for the meta type except for when it's a value type wrapper. Amends commit 63b622d5908ec2960ce5dfa301e9d3fd4d92fdb4. Change-Id: I08b9f4b97a58c15fdc3703dd3c5d927cd1beb3ce Reviewed-by: Sami Shalayel Reviewed-by: Qt CI Bot Reviewed-by: Semih Yavuz Reviewed-by: Ulf Hermann (cherry picked from commit 139d45c062ce6061b96f7ffe206babde4b596a98) --- src/qml/jsruntime/qv4qobjectwrapper.cpp | 6 ++-- tests/auto/qml/qqmllanguage/testtypes.cpp | 2 ++ tests/auto/qml/qqmllanguage/testtypes.h | 44 ++++++++++++++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 23 +++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index c43a2d75cf..5d000e7fe9 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -2102,12 +2102,10 @@ void Heap::QObjectMethod::init(QV4::ExecutionContext *scope, Object *object, int const QMetaObject *Heap::QObjectMethod::metaObject() { Scope scope(internalClass->engine); - if (Scoped objectWrapper(scope, wrapper); objectWrapper) - return objectWrapper->metaObject(); - if (Scoped typeWrapper(scope, wrapper); typeWrapper) - return typeWrapper->metaObject(); if (Scoped valueWrapper(scope, wrapper); valueWrapper) return valueWrapper->metaObject(); + if (QObject *self = object()) + return self->metaObject(); return nullptr; } diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 92e24bc332..46be3766ec 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -125,6 +125,8 @@ void registerTypes() qmlRegisterTypesAndRevisions("Test", 1); qmlRegisterTypesAndRevisions("Test", 1); + + qmlRegisterTypesAndRevisions("Test", 1); } QVariant myCustomVariantTypeConverter(const QString &data) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index e43424c9ca..f06c60f46d 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1866,4 +1866,48 @@ private: void registerTypes(); +class CounterAttachedBaseType: public QObject +{ + Q_OBJECT + QML_ANONYMOUS + Q_PROPERTY (int value READ value NOTIFY valueChanged) + +public: + CounterAttachedBaseType(QObject *parent = nullptr) : QObject(parent) {} + + int value() { return m_value; } + Q_SIGNAL void valueChanged(); + +protected: + int m_value = 98; +}; + + +class CounterAttachedType: public CounterAttachedBaseType +{ + Q_OBJECT + QML_ANONYMOUS + +public: + CounterAttachedType(QObject *parent = nullptr) : CounterAttachedBaseType(parent) {} + + Q_INVOKABLE void increase() { + ++m_value; + Q_EMIT valueChanged(); + } +}; + +class Counter : public QObject +{ + Q_OBJECT + QML_ATTACHED(CounterAttachedBaseType) + QML_ELEMENT + +public: + static CounterAttachedBaseType *qmlAttachedProperties(QObject *o) + { + return new CounterAttachedType(o); + } +}; + #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 047df2f6d0..0d26772c6c 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -388,6 +388,8 @@ private slots: void bindingAliasToComponentUrl(); void signalInlineComponentArg(); + void callMethodOfAttachedDerived(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -6669,6 +6671,27 @@ void tst_qqmllanguage::signalInlineComponentArg() } } +void tst_qqmllanguage::callMethodOfAttachedDerived() +{ + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(R"( + import QtQml + import Test + + QtObject { + Component.onCompleted: Counter.increase() + property int v: Counter.value + } + )", QUrl()); + + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("v").toInt(), 99); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3 From 4a5bfc4649177b2c6fd86bc70b6a4b45563e3aac Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 11 Aug 2023 18:15:15 +0800 Subject: SwipeView: ensure items fill the bounds Taking the translucentPages component from SwipeView::test_initialPositions into a Qt Quick app and running it showed that the Text items weren't actually filling the SwipeView. The documentation states: "SwipeView is populated with a set of pages. One page is visible at a time." The implication here being that each item fills the SwipeView, hence only one being visible at a time. It also states that SwipeView manages the geometry of items: "Note: SwipeView takes over the geometry management of items added to the view. Using anchors on the items is not supported, and any width or height assignment will be overridden by the view. Notice that this only applies to the root of the item. Specifying width and height, or using anchors for its children works as expected." So, this patch corrects the behavior and updates the test. Change-Id: I3425d4d028743f440f003e86ba009a2da7cca9cb Reviewed-by: Richard Moe Gustavsen Reviewed-by: Fabian Kosmale (cherry picked from commit 08b23023109de2cf91c0badf3a2d22a13cb09f40) --- src/quicktemplates2/qquickswipeview.cpp | 7 +++++++ src/quicktemplates2/qquickswipeview_p.h | 1 + tests/auto/quickcontrols2/controls/data/tst_swipeview.qml | 15 ++++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/quicktemplates2/qquickswipeview.cpp b/src/quicktemplates2/qquickswipeview.cpp index 33bf68b091..63655ce452 100644 --- a/src/quicktemplates2/qquickswipeview.cpp +++ b/src/quicktemplates2/qquickswipeview.cpp @@ -305,6 +305,13 @@ QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object) return new QQuickSwipeViewAttached(object); } +void QQuickSwipeView::componentComplete() +{ + Q_D(QQuickSwipeView); + QQuickContainer::componentComplete(); + d->resizeItems(); +} + void QQuickSwipeView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QQuickSwipeView); diff --git a/src/quicktemplates2/qquickswipeview_p.h b/src/quicktemplates2/qquickswipeview_p.h index 84d413755d..f1f8b6fed5 100644 --- a/src/quicktemplates2/qquickswipeview_p.h +++ b/src/quicktemplates2/qquickswipeview_p.h @@ -93,6 +93,7 @@ Q_SIGNALS: Q_REVISION(2, 2) void orientationChanged(); protected: + void componentComplete() override; void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; void itemAdded(int index, QQuickItem *item) override; void itemMoved(int index, QQuickItem *item) override; diff --git a/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml b/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml index 3c6bbaf91c..61dbecd660 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_swipeview.qml @@ -691,7 +691,10 @@ TestCase { id: translucentPages SwipeView { spacing: 10 - padding: 10 + leftPadding: 10 + topPadding: 10 + rightPadding: 10 + bottomPadding: 10 Text { text: "page 0" } Text { text: "page 1"; font.pointSize: 16 } Text { text: "page 2"; font.pointSize: 24 } @@ -702,18 +705,24 @@ TestCase { function test_initialPositions() { // QTBUG-102487 const control = createTemporaryObject(translucentPages, testCase, {width: 320, height: 200}) verify(control) + compare(control.contentItem.width, control.width - control.leftPadding - control.rightPadding) + compare(control.spacing, 10) + compare(control.orientation, Qt.Horizontal) for (let i = 0; i < control.count; ++i) { const page = control.itemAt(i) - // control.contentItem.width + control.spacing == 310; except Imagine style has contentItem.width == 320 - compare(page.x, i * 310) + compare(page.x, i * (control.contentItem.width + control.spacing)) compare(page.y, 0) + compare(page.width, control.contentItem.width) + compare(page.height, control.contentItem.height) } control.orientation = Qt.Vertical for (let i = 0; i < control.count; ++i) { const page = control.itemAt(i) compare(page.y, i * (control.contentItem.height + control.spacing)) compare(page.x, 0) + compare(page.width, control.contentItem.width) + compare(page.height, control.contentItem.height) } // QTBUG-115468: add a page after startup and check that that works too. -- cgit v1.2.3 From ec3d4108476f984ca5572e00be2604fa0cc2363a Mon Sep 17 00:00:00 2001 From: Andreas Eliasson Date: Wed, 28 Jun 2023 10:56:59 +0200 Subject: Doc: Use \qml instead of \code for qml code snippet Change-Id: I0da780e11c8589d0463ab0d4d51d2a146613e98f Reviewed-by: Paul Wicking (cherry picked from commit f4ba1178ccdf35c40870abc4467fb927b5ea61ba) Reviewed-by: Qt Cherry-pick Bot --- src/labs/models/doc/src/qmllabsmodels.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/labs/models/doc/src/qmllabsmodels.qdoc b/src/labs/models/doc/src/qmllabsmodels.qdoc index 598786f1f6..f2b57323dc 100644 --- a/src/labs/models/doc/src/qmllabsmodels.qdoc +++ b/src/labs/models/doc/src/qmllabsmodels.qdoc @@ -36,7 +36,7 @@ To use the types in this module, import the module with the following line: - \code + \qml import Qt.labs.qmlmodels - \endcode + \endqml */ -- cgit v1.2.3 From cb63279f11788e7cb1145ce2a069bba029126097 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 29 Aug 2023 14:51:39 +0800 Subject: Dialogs: fix statically-built applications not running When attemping to run a statically-built Qt Quick app that uses QtQuick.Dialogs, users would get an error: qrc:/qt-project.org/imports/QtQuick/Dialogs/quickimpl/qml/MessageDialog.qml: module "QtQuick.Dialogs.quickimpl" plugin "qtquickdialogs2quickimplplugin" not found This patch fixes these errors by listing QtQuick.Dialogs.quickimpl as a dependency through the qt_internal_add_qml_module command. This does introduce an unconditional tooling (e.g. qmlimportscanner) dependency on the non-native Qt Quick fallback plugin for all users, regardless of whether they are using a statically-built Qt or not. However, this should be fine, and the decision of whether or not to use the fallback is made at runtime anyway, so it may not be possible to conditionally add the dependency at CMake configure time. Fixes: QTBUG-105745 Change-Id: If2e4c4346963e645bd8e5e9cc210c7b38c8616c0 Reviewed-by: Fabian Kosmale Reviewed-by: Oliver Eftevaag (cherry picked from commit 0d3a137254d92644d0193fbdaf996e5e1f88ec9b) Reviewed-by: Qt CI Bot Reviewed-by: Ulf Hermann --- src/quickdialogs2/quickdialogs2/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quickdialogs2/quickdialogs2/CMakeLists.txt b/src/quickdialogs2/quickdialogs2/CMakeLists.txt index f6cf1d8325..f05c4c83a9 100644 --- a/src/quickdialogs2/quickdialogs2/CMakeLists.txt +++ b/src/quickdialogs2/quickdialogs2/CMakeLists.txt @@ -9,6 +9,7 @@ qt_internal_add_qml_module(QuickDialogs2 PLUGIN_TARGET qtquickdialogsplugin DEPENDENCIES QtQuick/auto + QtQuick.Dialogs.quickimpl/auto SOURCES qquickabstractdialog.cpp qquickabstractdialog_p.h -- cgit v1.2.3 From 202b792ed677406ea78d410eef098f7ba16d036b Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 24 Jul 2023 11:50:47 +0200 Subject: Context properties: Don't trigger an assert for numeric names The logic in our IdentifierHash assumes that every entry is a StringOrSymbol; however, IdentifierTable::asProperyKey will convert keys that look like numbers to ArrayIndex instead. This is noramlly what we want, and not an issue, except for setContextPropery where the user can pass an arbitrary string that is not necessarily a valid identifier. In an ideal world, we would just disallow such identifiers, but for backward compatibility change the code to handle this case (avoiding a Qt internal assert). We only need to modify the QString overloads, as those are the only ones that interact with unsanitized user input. A later commit will modify setContextPropery to warn if the key is numeric. Fixes: QTBUG-115319 Change-Id: Ifc4e4d2bc99321836e6976c4cbd0c5ff687b430c Reviewed-by: Ulf Hermann (cherry picked from commit b2b90c7cf5cb5205f2c5b374f7332252205385e8) Reviewed-by: Sami Shalayel --- src/qml/jsruntime/qv4identifierhash.cpp | 4 ++-- src/qml/jsruntime/qv4identifiertable.cpp | 13 ++++++++---- src/qml/jsruntime/qv4identifiertable_p.h | 3 ++- src/qml/jsruntime/qv4string_p.h | 22 +++++++++++++------ tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp | 29 +++++--------------------- 5 files changed, 34 insertions(+), 37 deletions(-) diff --git a/src/qml/jsruntime/qv4identifierhash.cpp b/src/qml/jsruntime/qv4identifierhash.cpp index 830d00926b..52a3e8e948 100644 --- a/src/qml/jsruntime/qv4identifierhash.cpp +++ b/src/qml/jsruntime/qv4identifierhash.cpp @@ -127,7 +127,7 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const if (!d) return nullptr; - PropertyKey id = d->identifierTable->asPropertyKey(str); + PropertyKey id = d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId); return lookup(id); } @@ -146,7 +146,7 @@ inline const PropertyKey IdentifierHash::toIdentifier(const QString &str) const { Q_ASSERT(d); - return d->identifierTable->asPropertyKey(str); + return d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId); } inline diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 32abc831ea..0661a3d32a 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -285,12 +285,17 @@ void IdentifierTable::sweep() size -= freed; } -PropertyKey IdentifierTable::asPropertyKey(const QString &s) +PropertyKey IdentifierTable::asPropertyKey(const QString &s, + IdentifierTable::KeyConversionBehavior conversionBehvior) { uint subtype; - const uint hash = String::createHashValue(s.constData(), s.length(), &subtype); - if (subtype == Heap::String::StringType_ArrayIndex) - return PropertyKey::fromArrayIndex(hash); + uint hash = String::createHashValue(s.constData(), s.size(), &subtype); + if (subtype == Heap::String::StringType_ArrayIndex) { + if (Q_UNLIKELY(conversionBehvior == ForceConversionToId)) + hash = String::createHashValueDisallowingArrayIndex(s.constData(), s.size(), &subtype); + else + return PropertyKey::fromArrayIndex(hash); + } return resolveStringEntry(s, hash, subtype)->identifier; } diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index 7c89b0e300..5ad98ab088 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -91,7 +91,8 @@ public: return asPropertyKey(str->d()); } - PropertyKey asPropertyKey(const QString &s); + enum KeyConversionBehavior { Default, ForceConversionToId }; + PropertyKey asPropertyKey(const QString &s, KeyConversionBehavior conversionBehavior = Default); PropertyKey asPropertyKey(const char *s, int len); PropertyKey asPropertyKeyImpl(const Heap::String *str); diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 6c6d255582..48b074ec3d 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -230,6 +230,12 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { return calculateHashValue(ch, end, subtype); } + static uint createHashValueDisallowingArrayIndex(const QChar *ch, int length, uint *subtype) + { + const QChar *end = ch + length; + return calculateHashValue(ch, end, subtype); + } + static uint createHashValue(const char *ch, int length, uint *subtype) { const char *end = ch + length; @@ -243,15 +249,19 @@ protected: static qint64 virtualGetLength(const Managed *m); public: - template + enum IndicesBehavior {Default, DisallowArrayIndex}; + template static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) { // array indices get their number as hash value - uint h = stringToArrayIndex(ch, end); - if (h != UINT_MAX) { - if (subtype) - *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; - return h; + uint h = UINT_MAX; + if constexpr (Behavior != DisallowArrayIndex) { + h = stringToArrayIndex(ch, end); + if (h != UINT_MAX) { + if (subtype) + *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; + return h; + } } while (ch < end) { diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index c839de05c1..75bf580947 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -75,7 +75,7 @@ private slots: void contextObjectHierarchy(); void destroyContextProperty(); - void gcDeletesContextObject(); + void numericContextProperty(); private: QQmlEngine engine; @@ -1006,31 +1006,12 @@ void tst_qqmlcontext::destroyContextProperty() // TODO: Or are we? } -void tst_qqmlcontext::gcDeletesContextObject() +void tst_qqmlcontext::numericContextProperty() { QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("gcDeletesContextObject.qml")); - - QVERIFY2(c.isReady(), qPrintable(c.errorString())); - QScopedPointer o(c.create()); - - QVERIFY(!o.isNull()); - - QPointer contextObject = o->property("o").value(); - QVERIFY(contextObject != nullptr); - - QQmlData *data = QQmlData::get(contextObject); - QVERIFY(data); - QQmlRefPointer context = data->ownContext; - QVERIFY(context); - QCOMPARE(context->contextObject(), contextObject); - - o->setProperty("o", QVariant::fromValue(nullptr)); - QCOMPARE(o->property("o").value(), nullptr); - engine.collectGarbage(); - - QTRY_VERIFY(contextObject.isNull()); - QCOMPARE(context->contextObject(), nullptr); + auto context = engine.rootContext(); + context->setContextProperty(QLatin1String("11"), 42); + QCOMPARE(context->contextProperty(QLatin1String("11")).toInt(), 42); } QTEST_MAIN(tst_qqmlcontext) -- cgit v1.2.3 From def5567d4ba8181e629cc71f8b988e642690f573 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Tue, 14 Mar 2023 14:33:47 +0100 Subject: Software Adaptation: Don't leak QSGRenderContext resources Subclasses of QSGRenderContext are responsible for releasing the resource tracked by the render context on invalidate. I'm not sure the software context will ever have any other resources than QSGTextures, but it doesn't hurt to try and release everything anyway. Fixes: QTBUG-106968 Change-Id: I58902e1a33c0d3d101e39af3e5f0a18d6820ce98 Reviewed-by: Laszlo Agocs (cherry picked from commit f1b188df132c42da62197055725e5f7eebcc4249) Reviewed-by: Qt Cherry-pick Bot --- .../scenegraph/adaptations/software/qsgsoftwarecontext.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp index f7c2176197..5ccee95597 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -143,6 +143,18 @@ void QSGSoftwareRenderContext::initializeIfNeeded() void QSGSoftwareRenderContext::invalidate() { + qDeleteAll(m_texturesToDelete); + m_texturesToDelete.clear(); + + qDeleteAll(m_textures); + m_textures.clear(); + + qDeleteAll(m_fontEnginesToClean); + m_fontEnginesToClean.clear(); + + qDeleteAll(m_glyphCaches); + m_glyphCaches.clear(); + m_sg->renderContextInvalidated(this); emit invalidated(); } -- cgit v1.2.3 From 2b971646ac23e740b06309aed11ff9f0e28e6743 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 14 Feb 2023 09:27:49 +0100 Subject: Chat tutorial: Fix deprecation warning Adapt to qtbase/14f9f00fdb2dc428610c08e3d9d03e38e9602166, fixing: sqlcontactmodel.cpp:40:13: warning: void QSqlQueryModel::setQuery(const QSqlQuery&) is deprecated: QSqlQuery is not meant to be copied. Pass it by move instead. [-Wdeprecated-declarations] Change-Id: I88d7f30247876f55b0114785efcf4ce580de0ab8 Reviewed-by: Mitch Curtis (cherry picked from commit 3689e9cc4afc810731f67a8deead2b594dbf6b08) Reviewed-by: Ulf Hermann --- examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp | 2 +- examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp b/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp index 6203ebec1e..017e7f4a97 100644 --- a/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp +++ b/examples/quickcontrols2/chattutorial/chapter4/sqlcontactmodel.cpp @@ -84,7 +84,7 @@ SqlContactModel::SqlContactModel(QObject *parent) : if (!query.exec("SELECT * FROM Contacts")) qFatal("Contacts SELECT query failed: %s", qPrintable(query.lastError().text())); - setQuery(query); + setQuery(std::move(query)); if (lastError().isValid()) qFatal("Cannot set query on SqlContactModel: %s", qPrintable(lastError().text())); } diff --git a/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp b/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp index 6203ebec1e..017e7f4a97 100644 --- a/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp +++ b/examples/quickcontrols2/chattutorial/chapter5/sqlcontactmodel.cpp @@ -84,7 +84,7 @@ SqlContactModel::SqlContactModel(QObject *parent) : if (!query.exec("SELECT * FROM Contacts")) qFatal("Contacts SELECT query failed: %s", qPrintable(query.lastError().text())); - setQuery(query); + setQuery(std::move(query)); if (lastError().isValid()) qFatal("Cannot set query on SqlContactModel: %s", qPrintable(lastError().text())); } -- cgit v1.2.3 From d4e821536c3575cddd23806f6a26a312a6c83188 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 16 May 2023 10:16:20 +0200 Subject: doc: Add Window.visibility property snippet; link to Window example Probably we'll keep the Window and Screen example: it has walkthrough docs already. Snippets can be used to illustrate some simplified scenarios, while the example is a bit fancier. Likewise, link to the Window and Screen example from the Screen docs. Change-Id: I20c803affd1f4b6d44b41520782e5ad3c69fc871 Reviewed-by: Oliver Eftevaag (cherry picked from commit 36e3ea45f9df38ee0f2e1e619a8c344357d56896) Reviewed-by: Shawn Rutledge --- src/quick/doc/snippets/qml/windowVisibility.qml | 76 +++++++++++++++++++++++++ src/quick/items/qquickscreen.cpp | 2 + src/quick/items/qquickwindow.cpp | 8 ++- 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 src/quick/doc/snippets/qml/windowVisibility.qml diff --git a/src/quick/doc/snippets/qml/windowVisibility.qml b/src/quick/doc/snippets/qml/windowVisibility.qml new file mode 100644 index 0000000000..b5f89a3894 --- /dev/null +++ b/src/quick/doc/snippets/qml/windowVisibility.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [entire] +import QtQuick +import QtQuick.Controls + +Window { + id: win + flags: Qt.Window | Qt.WindowFullscreenButtonHint + visibility: fullscreenButton.checked ? Window.FullScreen : Window.Windowed + + Button { + id: fullscreenButton + anchors { + right: parent.right + top: parent.top + margins: 6 + } + width: height + checkable: true + Binding on checked { value: win.visibility === Window.FullScreen } + text: "⛶" + ToolTip.visible: hovered + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: win.visibility === Window.FullScreen ? qsTr("restore") : qsTr("fill screen") + } +} +//! [entire] diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp index e4c5dc7eae..8a71f9aea3 100644 --- a/src/quick/items/qquickscreen.cpp +++ b/src/quick/items/qquickscreen.cpp @@ -75,6 +75,8 @@ QT_BEGIN_NAMESPACE Note that the Screen type is not valid at Component.onCompleted, because the Item or Window has not been displayed on a screen by this time. + + \sa {Qt Quick Examples - Window and Screen} */ /*! diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 634561f04e..869143ce07 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3274,7 +3274,7 @@ void QQuickWindow::endExternalCommands() \snippet qml/splashWindow.qml entire - \sa Qt::WindowFlags + \sa Qt::WindowFlags, {Qt Quick Examples - Window and Screen} */ /*! @@ -3359,10 +3359,12 @@ void QQuickWindow::endExternalCommands() visibility property you will always get the actual state, never \c AutomaticVisibility. - When a window is not visible its visibility is Hidden, and setting + When a window is not visible, its visibility is \c Hidden, and setting visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false. - \sa visible + \snippet qml/windowVisibility.qml entire + + \sa visible, {Qt Quick Examples - Window and Screen} \since 5.1 */ -- cgit v1.2.3 From 7cecf2a9da31b0e4697a23e21b806caaef741b20 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Dec 2022 10:48:27 +0800 Subject: Doc: improve grabToImage snippets - Change the code that is quoted so that the indenting looks sane - Make it possible to copy and run the first snippet without modification - Make it easier to copy and run the second snippet without modification - Move itemGrab.qml to item sub-directory - Generally tidy up Change-Id: I608b7a6025b9866c14b84420ae64827965a7519f Reviewed-by: Paul Wicking (cherry picked from commit f0f9bacca8d44e20bd0e21da819737aed0ff62b8) Reviewed-by: Ulf Hermann --- src/quick/doc/snippets/qml/item/itemGrab.qml | 86 ++++++++++++++++++++++++ src/quick/doc/snippets/qml/itemGrab.qml | 97 ---------------------------- src/quick/items/qquickitemgrabresult.cpp | 12 ++-- 3 files changed, 91 insertions(+), 104 deletions(-) create mode 100644 src/quick/doc/snippets/qml/item/itemGrab.qml delete mode 100644 src/quick/doc/snippets/qml/itemGrab.qml diff --git a/src/quick/doc/snippets/qml/item/itemGrab.qml b/src/quick/doc/snippets/qml/item/itemGrab.qml new file mode 100644 index 0000000000..78fc53833b --- /dev/null +++ b/src/quick/doc/snippets/qml/item/itemGrab.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 + +Item { + width: 320 + height: 480 + +//! [grab-to-file] +Rectangle { + id: sourceRectangle + width: 100 + height: 100 + gradient: Gradient { + GradientStop { position: 0; color: "steelblue" } + GradientStop { position: 1; color: "black" } + } + + Component.onCompleted: { + sourceRectangle.grabToImage(function(result) { + result.saveToFile("something.png") + }) + } +} +//! [grab-to-file] + +//! [grab-to-image] +Image { + id: image +} + +Component.onCompleted: { + sourceRectangle.grabToImage(function(result) { + image.source = result.url + }, Qt.size(50, 50)) +} +//! [grab-to-image] +} diff --git a/src/quick/doc/snippets/qml/itemGrab.qml b/src/quick/doc/snippets/qml/itemGrab.qml deleted file mode 100644 index e26e3dc55e..0000000000 --- a/src/quick/doc/snippets/qml/itemGrab.qml +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Jolla Ltd, author: -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.4 - -Item { - width: 320 - height: 480 - -//! [grab-source] -Rectangle { - id: source - width: 100 - height: 100 - gradient: Gradient { - GradientStop { position: 0; color: "steelblue" } - GradientStop { position: 1; color: "black" } - } -} -//! [grab-source] - -//! [grab-image-target] -Image { - id: image -} -//! [grab-image-target] - Timer { - repeat: false - running: true - interval: 1000 - onTriggered: { -//! [grab-to-file] - - // ... - source.grabToImage(function(result) { - result.saveToFile("something.png"); - }); -//! [grab-to-file] - -//! [grab-to-cache] - - // ... - source.grabToImage(function(result) { - image.source = result.url; - }, - Qt.size(50, 50)); -//! [grab-to-cache] - } - } -} diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp index 24edd6d1a7..59ebbcc1ec 100644 --- a/src/quick/items/qquickitemgrabresult.cpp +++ b/src/quick/items/qquickitemgrabresult.cpp @@ -381,17 +381,15 @@ QSharedPointer QQuickItem::grabToImage(const QSize &target * * If the grab could not be initiated, the function returns \c false. * - * The following snippet shows how to grab an item and store the results to - * a file. + * The following snippet shows how to grab an item and store the results in + * a file: * - * \snippet qml/itemGrab.qml grab-source - * \snippet qml/itemGrab.qml grab-to-file + * \snippet qml/item/itemGrab.qml grab-to-file * * The following snippet shows how to grab an item and use the results in - * another image element. + * another image element: * - * \snippet qml/itemGrab.qml grab-image-target - * \snippet qml/itemGrab.qml grab-to-cache + * \snippet qml/item/itemGrab.qml grab-to-image * * \note This function will render the item to an offscreen surface and * copy that surface from the GPU's memory into the CPU's memory, which can -- cgit v1.2.3 From 5c0c5f7fcf9fad5812cce02e471a265d8c6203c2 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 28 Jun 2022 16:05:18 +0800 Subject: tst_QQuickMenu: skip entire popup() test where necessary If the cursor configure feature is not enabled, don't build the code. If the platform is Android or Wayland, skip the test. Change-Id: Ic60771decf7f2e0bd7db77c112925147015af5a6 Reviewed-by: Eskil Abrahamsen Blomfeldt (cherry picked from commit fb22bc65e2730c87dffd779668a1e74aaa3ebf7d) Reviewed-by: Ulf Hermann --- tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp index 9e37541137..0176c2db35 100644 --- a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp +++ b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp @@ -84,7 +84,9 @@ private slots: void menuSeparator(); void repeater(); void order(); +#if QT_CONFIG(cursor) void popup(); +#endif void actions(); #if QT_CONFIG(shortcut) void actionShortcuts(); @@ -819,8 +821,15 @@ void tst_QQuickMenu::order() } } +#if QT_CONFIG(cursor) void tst_QQuickMenu::popup() { +#if defined(Q_OS_ANDROID) + QSKIP("Setting cursor position is not supported on Android"); +#endif + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) + QSKIP("Setting cursor position is not supported on Wayland"); + // Try moving the cursor from the current position // Skip if it fails since the test relies on moving the cursor const QPoint point = QCursor::pos() + QPoint(1, 1); @@ -851,8 +860,6 @@ void tst_QQuickMenu::popup() QQuickItem *button = window->property("button").value(); QVERIFY(button); - // Android does not support settings cursor position -#if QT_CONFIG(cursor) && !defined(Q_OS_ANDROID) QPoint oldCursorPos = QCursor::pos(); QPoint cursorPos = window->mapToGlobal(QPoint(11, 22)); QCursor::setPos(cursorPos); @@ -981,8 +988,8 @@ void tst_QQuickMenu::popup() QCursor::setPos(oldCursorPos); QTRY_COMPARE(QCursor::pos(), oldCursorPos); -#endif } +#endif // QT_CONFIG(cursor) void tst_QQuickMenu::actions() { -- cgit v1.2.3 From d8c58faa292c18effe87c5c6b6ea0efe3bb18f4f Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 5 Jan 2023 08:54:57 +0100 Subject: Deferred properties: avoid spurious binding loop During binding evaluation, it might happen that we do an initial read of a deferred property. If that deferred property is in turn an object property, further binding evaluations might occur. Those bindings should not depend on whatever trigerred the initial binding evaluation. However, with the new C++ properties/bindings, that would happen, as the bindingStatus would still indicate that binding evaluation is active. To remedy this, we temporarily suspend the binding status ind begin and completeDeferred. Fixes: QTBUG-96351 Change-Id: I7e9778aa5339f044ce3082e962a2ede34b5d1e69 Reviewed-by: Ulf Hermann Reviewed-by: Qt CI Bot (cherry picked from commit 9b1d3a86023b47f9c3bff93c1ebbe18f8c7ad657) Reviewed-by: Semih Yavuz --- src/quicktemplates2/qquickdeferredexecute.cpp | 18 ++++ tests/auto/quickcontrols2/CMakeLists.txt | 1 + tests/auto/quickcontrols2/deferred/CMakeLists.txt | 25 ++++++ .../deferred/data/noSpuriousBinding.qml | 15 ++++ .../quickcontrols2/deferred/tst_qquickdeferred.cpp | 100 +++++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 tests/auto/quickcontrols2/deferred/CMakeLists.txt create mode 100644 tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml create mode 100644 tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp diff --git a/src/quicktemplates2/qquickdeferredexecute.cpp b/src/quicktemplates2/qquickdeferredexecute.cpp index c946024c43..09165f0397 100644 --- a/src/quicktemplates2/qquickdeferredexecute.cpp +++ b/src/quicktemplates2/qquickdeferredexecute.cpp @@ -75,6 +75,15 @@ static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &pro int propertyIndex = property.index(); int wasInProgress = enginePriv->inProgressCreations; + /* we don't want deferred properties to suddenly depend on arbitrary + other properties which might have trigerred the construction of + objects as a consequence of a read. + */ + auto bindingStatus = QtPrivate::suspendCurrentBindingStatus(); + auto cleanup = qScopeGuard([&](){ + QtPrivate::restoreBindingStatus(bindingStatus); + }); + for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) { QQmlData::DeferredData *deferData = *dit; @@ -139,6 +148,15 @@ void completeDeferred(QObject *object, const QString &property) QQmlData *data = QQmlData::get(object); QQmlComponentPrivate::DeferredState *state = deferredStates()->take(qHash(object, property)); if (data && state && !data->wasDeleted(object)) { + /* we don't want deferred properties to suddenly depend on arbitrary + other properties which might have trigerred the construction of + objects as a consequence of a read. + */ + auto bindingStatus = QtPrivate::suspendCurrentBindingStatus(); + auto cleanup = qScopeGuard([&](){ + QtPrivate::restoreBindingStatus(bindingStatus); + }); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine()); QQmlComponentPrivate::completeDeferred(ep, state); } diff --git a/tests/auto/quickcontrols2/CMakeLists.txt b/tests/auto/quickcontrols2/CMakeLists.txt index 903732bf79..4004fbecdf 100644 --- a/tests/auto/quickcontrols2/CMakeLists.txt +++ b/tests/auto/quickcontrols2/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(controls) endif() add_subdirectory(cursor) add_subdirectory(customization) +add_subdirectory(deferred) add_subdirectory(designer) if(NOT ANDROID) # QTBUG-100258 add_subdirectory(focus) diff --git a/tests/auto/quickcontrols2/deferred/CMakeLists.txt b/tests/auto/quickcontrols2/deferred/CMakeLists.txt new file mode 100644 index 0000000000..900732ef4e --- /dev/null +++ b/tests/auto/quickcontrols2/deferred/CMakeLists.txt @@ -0,0 +1,25 @@ +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_qquickdeferred + SOURCES + tst_qquickdeferred.cpp + LIBRARIES + Qt::Qml + Qt::QmlPrivate + Qt::QuickTemplates2Private + Qt::QuickTestUtilsPrivate + TESTDATA ${test_data} +) + +qt_internal_extend_target(tst_qquickdeferred CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=\\\":/data\\\" +) + +qt_internal_extend_target(tst_qquickdeferred CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" +) diff --git a/tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml b/tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml new file mode 100644 index 0000000000..9f6a174cf4 --- /dev/null +++ b/tests/auto/quickcontrols2/deferred/data/noSpuriousBinding.qml @@ -0,0 +1,15 @@ +import test +import QtQuick + +Item { + id: root + property bool toggle: true + property int counter: 0 + + x: toggle ? 100 : tester.objectProperty.x + + DeferredPropertyTester { + id: tester + objectProperty: Item { x: { console.log("hi"); return 300; } } + } +} diff --git a/tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp b/tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp new file mode 100644 index 0000000000..4d8096aa46 --- /dev/null +++ b/tests/auto/quickcontrols2/deferred/tst_qquickdeferred.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2023 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +class DeferredPropertyTester : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickItem *objectProperty READ objectProperty WRITE setObjectProperty NOTIFY objectChanged) + Q_CLASSINFO("DeferredPropertyNames", "objectProperty") + +public: + DeferredPropertyTester() {} + + + QQuickItem *objectProperty() { + + if (!m_object.wasExecuted()) { + quickBeginDeferred(this, "objectProperty", m_object); + quickCompleteDeferred(this, "objectProperty", m_object); + } + + return m_object; + } + void setObjectProperty(QQuickItem *obj) { + if (m_object == obj) + return; + m_object = obj; + if (!m_object.isExecuting()) // first read + emit objectChanged(); + } + +signals: + void objectChanged(); + +private: + QQuickDeferredPointer m_object = nullptr; +}; + +class tst_qquickdeferred : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qquickdeferred() : QQmlDataTest(QT_QMLTEST_DATADIR) {} +private slots: + void noSpuriousBinding(); +}; + + + +void tst_qquickdeferred::noSpuriousBinding() { + qmlRegisterType("test", 1, 0, "DeferredPropertyTester"); + + QQmlEngine engine; + QQmlComponent comp(&engine, testFileUrl("noSpuriousBinding.qml")); + std::unique_ptr root(comp.create()); + QVERIFY2(root, qPrintable(comp.errorString())); + root->setProperty("toggle", false); +} + +QTEST_MAIN(tst_qquickdeferred) + +#include "tst_qquickdeferred.moc" -- cgit v1.2.3 From 180051347e837cc5a4a8609ca3bd036c25cab3ee Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Thu, 20 Jul 2023 10:54:27 +0200 Subject: Fix fetching data when reuseItems is true QQmlDelegateModelPrivate::requestMoreIfNecessary() is called in various situations, including at startup, which calls QAIM::fetchMore() on the model if it supports that. But we need to do it in one more case: when delegates are being reused from the cache, the model could provide more rows even though we aren't instantiating new delegates. Amends 1841a9e41d02c9b95a7eb3c47e09f6773da56a85 Fixes: QTBUG-95107 Change-Id: I5b7ff48345ab78977cb03cfcf58ed96a57c831bd Reviewed-by: Santhosh Kumar Reviewed-by: Qt CI Bot Reviewed-by: Shawn Rutledge (cherry picked from commit 589d0ee473d1ea93c55eea1e6881750e5995031c) Reviewed-by: Sami Shalayel --- src/qmlmodels/qqmldelegatemodel.cpp | 3 + .../auto/quick/qquicklistview2/data/fetchMore.qml | 21 ++++++ .../quick/qquicklistview2/tst_qquicklistview2.cpp | 78 ++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 tests/auto/quick/qquicklistview2/data/fetchMore.qml diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index f4c9622f00..75ce47cd95 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -1259,6 +1259,9 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ addCacheItem(cacheItem, it); reuseItem(cacheItem, index, flags); cacheItem->referenceObject(); + + if (index == m_compositor.count(group) - 1) + requestMoreIfNecessary(); return cacheItem->object; } diff --git a/tests/auto/quick/qquicklistview2/data/fetchMore.qml b/tests/auto/quick/qquicklistview2/data/fetchMore.qml new file mode 100644 index 0000000000..4ce53e4d28 --- /dev/null +++ b/tests/auto/quick/qquicklistview2/data/fetchMore.qml @@ -0,0 +1,21 @@ +import QtQuick +import org.qtproject.Test + +ListView { + id: listView + width: 300 + height: 150 + flickDeceleration: 10000 + + model: FetchMoreModel + delegate: Text { + height: 50 + text: model.display + } + + Text { + anchors.right: parent.right + text: "count " + listView.count + color: listView.moving ? "red" : "blue" + } +} diff --git a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp index fb3ad3a93f..a7f64caaf0 100644 --- a/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp +++ b/tests/auto/quick/qquicklistview2/tst_qquicklistview2.cpp @@ -66,6 +66,9 @@ private slots: void isCurrentItem_DelegateModel(); void isCurrentItem_NoRegressionWithDelegateModelGroups(); + void fetchMore_data(); + void fetchMore(); + private: void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); QScopedPointer touchDevice = QScopedPointer(QTest::createTouchDevice()); @@ -475,6 +478,81 @@ void tst_QQuickListView2::isCurrentItem_NoRegressionWithDelegateModelGroups() QCOMPARE(item3->property("isCurrent").toBool(), false); } +class TestFetchMoreModel : public QAbstractListModel +{ + Q_OBJECT + +public: + QVariant data(const QModelIndex& index, int role) const override + { + if (role == Qt::DisplayRole) + return QString::number(index.row()); + return {}; + } + + int columnCount(const QModelIndex&) const override { return 1; } + + int rowCount(const QModelIndex& parent) const override + { + return parent.isValid() ? 0 : m_lines; + } + + QModelIndex parent(const QModelIndex&) const override { return {}; } + + bool canFetchMore(const QModelIndex &) const override { return true; } + + void fetchMore(const QModelIndex & parent) override + { + if (Q_UNLIKELY(parent.isValid())) + return; + beginInsertRows(parent, m_lines, m_lines); + m_lines++; + endInsertRows(); + } + + int m_lines = 3; +}; + +void tst_QQuickListView2::fetchMore_data() +{ + QTest::addColumn("reuseItems"); + QTest::addColumn("cacheBuffer"); + + QTest::newRow("no reuseItems, default buffer") << false << -1; + QTest::newRow("reuseItems, default buffer") << true << -1; + QTest::newRow("no reuseItems, no buffer") << false << 0; + QTest::newRow("reuseItems, no buffer") << true << 0; + QTest::newRow("no reuseItems, buffer 100 px") << false << 100; + QTest::newRow("reuseItems, buffer 100 px") << true << 100; +} + +void tst_QQuickListView2::fetchMore() // QTBUG-95107 +{ + QFETCH(bool, reuseItems); + QFETCH(int, cacheBuffer); + + TestFetchMoreModel model; + qmlRegisterSingletonInstance("org.qtproject.Test", 1, 0, "FetchMoreModel", &model); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("fetchMore.qml"))); + auto *listView = qobject_cast(window.rootObject()); + QVERIFY(listView); + listView->setReuseItems(reuseItems); + if (cacheBuffer >= 0) + listView->setCacheBuffer(cacheBuffer); + + for (int i = 0; i < 3; ++i) { + const int rowCount = listView->count(); + if (lcTests().isDebugEnabled()) QTest::qWait(1000); + listView->flick(0, -5000); + QTRY_VERIFY(!listView->isMoving()); + qCDebug(lcTests) << "after flick: contentY" << listView->contentY() + << "rows" << rowCount << "->" << listView->count(); + QVERIFY(listView->count() > rowCount); + QVERIFY(model.m_lines >= listView->count()); // fetchMore() was called + } +} + QTEST_MAIN(tst_QQuickListView2) #include "tst_qquicklistview2.moc" -- cgit v1.2.3 From d953147816e9647a97c81733c435a116973b027e Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Jun 2023 10:35:36 +0800 Subject: Doc: de-duplicate header view docs Parameterize the variable part of each section and include each section as a snippet. Change-Id: If5251cb2ee40dfb57cf57e0f1cad00b04c582e98 Reviewed-by: Nicholas Bennett (cherry picked from commit d33edd9d36f9692a4dc8f51ef47efc6a1a5aec92) Reviewed-by: Richard Moe Gustavsen --- .../doc/src/includes/qquickheaderview.qdocinc | 72 +++++++++++++++++ src/quicktemplates2/qquickheaderview.cpp | 94 +++------------------- 2 files changed, 82 insertions(+), 84 deletions(-) create mode 100644 src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc diff --git a/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc new file mode 100644 index 0000000000..eda53e4c30 --- /dev/null +++ b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc @@ -0,0 +1,72 @@ +//! [detailed-description] +A \1 provides a styled table header. +It can either be used as an independent view or header for a \l TableView. + +You can add a header for a TableView by assigning a \1 +to the \l {TableView::syncView} property. The header and the table will +then be kept in sync while flicking. + +By default, \1 displays +\l {QAbstractItemModel::headerData()}{header data} +from the \l {TableView::syncView}{sync view's} \l {TableView::model}{model}. +If you don't wish to use this model, you can assign a different model to the +\l {TableView::model}{model} property. If you assign a model that is a +QAbstractItemModel, its header data will be used. Otherwise the data in +the model will be used directly (for example, if you assign a model that +is simply an array of strings). + +The application is responsible for placing the header at the +correct location in the scene. You can add as many headers as you +want to a single TableView, which can be useful if you for example want +to place headers on all four sides of the table. + +The following snippet shows how you can add a horizontal and vertical header +view to a table view: + +\snippet qtquickcontrols-headerview.qml 0 + +A \1 will have +\l {TableView::resizableColumns}{resizableColumns} set to \c true by default. +//! [detailed-description] + +//! [syncView] +This property holds the TableView to synchronize with. + +Once this property is bound to another TableView, both header and table +will synchronize with regard to column widths, column spacing, and flicking +\1. + +If the \l model is not explicitly set, then the header will use the syncView's +model to label the columns. + +\sa model TableView +//! [syncView] + +//! [model] +This property holds the model providing data for the \1 header view. + +When model is not explicitly set, the header will use the syncView's +model once syncView is set. + +If model is a QAbstractTableModel, its \1 headerData() will +be accessed. + +If model is a QAbstractItemModel other than QAbstractTableModel, model's data() +will be accessed. + +Otherwise, the behavior is same as setting TableView::model. + +\sa TableView {TableView::model} {model} QAbstractTableModel +//! [model] + +//! [textRole] +This property holds the model role used to display text in each header cell. + +When the model has multiple roles, textRole can be set to determine which +role should be displayed. + +If model is a QAbstractItemModel then it will default to "display"; otherwise +it is empty. + +\sa QAbstractItemModel::roleNames() +//! [textRole] diff --git a/src/quicktemplates2/qquickheaderview.cpp b/src/quicktemplates2/qquickheaderview.cpp index 03546a5d33..1629259d4b 100644 --- a/src/quicktemplates2/qquickheaderview.cpp +++ b/src/quicktemplates2/qquickheaderview.cpp @@ -47,16 +47,9 @@ \inherits TableView \brief Provides a horizontal header view to accompany a \l TableView. - A HorizontalHeaderView provides labeling of the columns of a \l TableView. - To add a horizontal header to a TableView, bind the - \l {HorizontalHeaderView::syncView} {syncView} property to the TableView: + \include qquickheaderview.qdocinc {detailed-description} {HorizontalHeaderView} - \snippet qtquickcontrols2-headerview-simple.qml horizontal - - The header displays data from the {syncView}'s model by default, but can - also have its own model. If the model is a QAbstractTableModel, then - the header will display the model's horizontal headerData(); otherwise, - the model's data(). + \sa VerticalHeaderView */ /*! @@ -66,112 +59,45 @@ \inherits TableView \brief Provides a vertical header view to accompany a \l TableView. - A VerticalHeaderView provides labeling of the rows of a \l TableView. - To add a vertical header to a TableView, bind the - \l {VerticalHeaderView::syncView} {syncView} property to the TableView: - - \snippet qtquickcontrols2-headerview-simple.qml vertical + \include qquickheaderview.qdocinc {detailed-description} {VerticalHeaderView} - The header displays data from the {syncView}'s model by default, but can - also have its own model. If the model is a QAbstractTableModel, then - the header will display the model's vertical headerData(); otherwise, - the model's data(). + \sa HorizontalHeaderView */ /*! \qmlproperty TableView QtQuick::HorizontalHeaderView::syncView - This property holds the TableView to synchronize with. - - Once this property is bound to another TableView, both header and table - will synchronize with regard to column widths, column spacing, and flicking - horizontally. - - If the \l model is not explicitly set, then the header will use the syncView's - model to label the columns. - - \sa model TableView + \include qquickheaderview.qdocinc {syncView} {horizontally} */ /*! \qmlproperty TableView QtQuick::VerticalHeaderView::syncView - This property holds the TableView to synchronize with. - - Once this property is bound to another TableView, both header and table - will synchronize with regard to row heights, row spacing, and flicking - vertically. - - If the \l model is not explicitly set, then the header will use the syncView's - model to label the rows. - - \sa model TableView + \include qquickheaderview.qdocinc {syncView} {vertically} */ /*! \qmlproperty QVariant QtQuick::HorizontalHeaderView::model - This property holds the model providing data for the horizontal header view. - - When model is not explicitly set, the header will use the syncView's - model once syncView is set. - - If model is a QAbstractTableModel, its horizontal headerData() will - be accessed. - - If model is a QAbstractItemModel other than QAbstractTableModel, model's data() - will be accessed. - - Otherwise, the behavior is same as setting TableView::model. - - \sa TableView {TableView::model} {model} QAbstractTableModel + \include qquickheaderview.qdocinc {model} {horizontal} */ /*! \qmlproperty QVariant QtQuick::VerticalHeaderView::model - This property holds the model providing data for the vertical header view. - - When model is not explicitly set, it will be synchronized with syncView's model - once syncView is set. - - If model is a QAbstractTableModel, its vertical headerData() will - be accessed. - - If model is a QAbstractItemModel other than QAbstractTableModel, model's data() - will be accessed. - - Otherwise, the behavior is same as setting TableView::model. - - \sa TableView {TableView::model} {model} QAbstractTableModel + \include qquickheaderview.qdocinc {model} {vertical} */ /*! \qmlproperty QString QtQuick::HorizontalHeaderView::textRole - This property holds the model role used to display text in each header cell. - - When the model has multiple roles, textRole can be set to determine which - role should be displayed. - - If model is a QAbstractItemModel then it will default to "display"; otherwise - it is empty. - - \sa QAbstractItemModel::roleNames() + \include qquickheaderview.qdocinc {textRole} */ /*! \qmlproperty QString QtQuick::VerticalHeaderView::textRole - This property holds the model role used to display text in each header cell. - - When the model has multiple roles, textRole can be set to determine which - role should be displayed. - - If model is a QAbstractItemModel then it will default to "display"; otherwise - it is empty. - - \sa QAbstractItemModel::roleNames() + \include qquickheaderview.qdocinc {textRole} */ QT_BEGIN_NAMESPACE -- cgit v1.2.3 From 30548c570dda6fcb23e940ef6d60eeed2e484ab9 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Jun 2023 09:29:00 +0800 Subject: Doc: mention textRole in *HeaderView detailed description It may not be clear that Qt::DisplayRole is used by default, as this is only mentioned in textRole's documentation, which isn't mentioned in the detailed description. Change-Id: Ie7e16bbfbedc2de6cc336f3a548bf69d6dbbdf40 Reviewed-by: Nicholas Bennett (cherry picked from commit 9bd72f48600e19743c120035cbbd63fd93f5c8c1) Reviewed-by: Richard Moe Gustavsen --- src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc index eda53e4c30..7e12f53a6d 100644 --- a/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc +++ b/src/quickcontrols2/doc/src/includes/qquickheaderview.qdocinc @@ -15,6 +15,10 @@ QAbstractItemModel, its header data will be used. Otherwise the data in the model will be used directly (for example, if you assign a model that is simply an array of strings). +By default, \l textRole is set to \c "display", meaning that data from the +model's \l {Qt::ItemDataRole}{Qt::DisplayRole} will be used. You can set +this to another role name in order to have that data displayed instead. + The application is responsible for placing the header at the correct location in the scene. You can add as many headers as you want to a single TableView, which can be useful if you for example want -- cgit v1.2.3 From 3017099562d39542029e83f3fbca6caa19de1973 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 13 Mar 2023 14:53:39 +0100 Subject: PropertyAnimation: Handle targets being deleted QQuickPropertyAnimation does not own the targets in its targets list. Thus, we need to be careful to not access deleted objects. Calling QQmlData::wasDeleted is not enoguh, as the object might be fully deleted, but we'd still have a dangling pointer to it. Fix the issue by using QPointer. A similar issue might exists for target, but there we don't normally end up with dangling references - the engine will correctly null the target. And the QPointer isNull check will avoid potentiall nullptr derefences there, too. Ideally, we would use QQmlGuard instead of QPointer, as it would have a lower overhead - however, we run into linker issues on MSVC. Fixing them is deferred to a future commit, to not block this crash fix. Fixes: QTBUG-100392 Change-Id: If084e2e0f22d50dbee8bf5aedfa2e749e79fc105 Reviewed-by: Qt CI Bot Reviewed-by: Mitch Curtis (cherry picked from commit 8a74155411d7a22bb8efa405fe59a681e47e6b45) Reviewed-by: Sami Shalayel --- src/quick/util/qquickanimation.cpp | 41 ++++++++++++++++++++-- src/quick/util/qquickanimation_p_p.h | 2 +- .../data/targetsDeletedWithoutRemoval.qml | 29 +++++++++++++++ .../qquickanimations/tst_qquickanimations.cpp | 21 +++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp index 8ef69e6ca0..0752273da9 100644 --- a/src/quick/util/qquickanimation.cpp +++ b/src/quick/util/qquickanimation.cpp @@ -2567,7 +2567,38 @@ void QQuickPropertyAnimation::setProperties(const QString &prop) QQmlListProperty QQuickPropertyAnimation::targets() { Q_D(QQuickPropertyAnimation); - return QQmlListProperty(this, &(d->targets)); + using ListPtr = QList> *; + using LP = QQmlListProperty; + LP::AppendFunction appendFn = [](LP *prop, QObject *value) + { + static_cast(prop->data)->append(value); + }; + LP::CountFunction countFn = [](LP *prop) + { + return static_cast(prop->data)->size(); + }; + + LP::AtFunction atFn = [](LP *prop, qsizetype index) -> QObject * + { + return static_cast(prop->data)->at(index); + }; + + LP::ClearFunction clearFN = [](LP *prop) + { + return static_cast(prop->data)->clear(); + }; + + LP::ReplaceFunction replaceFn = [](LP *prop, qsizetype index, QObject *value) + { + static_cast(prop->data)->replace(index, value); + }; + + LP::RemoveLastFunction removeLastFn = [](LP *prop) + { + static_cast(prop->data)->removeLast(); + }; + + return QQmlListProperty(this, &(d->targets), appendFn, countFn, atFn, clearFN, replaceFn, removeLastFn); } /*! @@ -2639,7 +2670,7 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA if (!d->propertyName.isEmpty()) props << d->propertyName; - QList targets = d->targets; + QList> targets = d->targets; if (d->target) targets.append(d->target); @@ -2668,10 +2699,14 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA for (int i = 0; i < props.count(); ++i) { for (int j = 0; j < targets.count(); ++j) { + const auto& guarded = targets.at(j); + if (guarded.isNull()) + continue; + QObject *target = guarded.get(); QQuickStateAction myAction; QString errorMessage; const QString &propertyName = props.at(i); - myAction.property = d->createProperty(targets.at(j), propertyName, this, &errorMessage); + myAction.property = d->createProperty(target, propertyName, this, &errorMessage); if (myAction.property.isValid()) { if (usingDefaultProperties) successfullyCreatedDefaultProperty = true; diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h index e0a26bfea9..c348746bca 100644 --- a/src/quick/util/qquickanimation_p_p.h +++ b/src/quick/util/qquickanimation_p_p.h @@ -279,7 +279,7 @@ public: QObject *target; QString propertyName; QString properties; - QList targets; + QList> targets; QList exclude; QString defaultProperties; diff --git a/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml b/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml new file mode 100644 index 0000000000..e31caa1905 --- /dev/null +++ b/tests/auto/quick/qquickanimations/data/targetsDeletedWithoutRemoval.qml @@ -0,0 +1,29 @@ +import QtQuick + +Item { + id: root + + property list targets + property alias animTargets: animation.targets + + Component { + id: trComponent + Translate {} + } + + Component.onCompleted: { + const target = trComponent.createObject(this); + targets.push(target); + target.destroy(); + // give event loop some time to actually stop the animation and destroy the target + Qt.callLater(animation.start); + } + + NumberAnimation { + id: animation + targets: root.targets + property: "x" + running: false + to: 100 + } +} diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index 313f478820..30b934eeec 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -118,6 +118,7 @@ private slots: void alwaysRunToEndInSequentialAnimationBug(); void cleanupWhenRenderThreadStops(); void infiniteLoopsWithoutFrom(); + void targetsDeletedNotRemoved(); }; #define QTIMED_COMPARE(lhs, rhs) do { \ for (int ii = 0; ii < 5; ++ii) { \ @@ -2060,6 +2061,26 @@ void tst_qquickanimations::infiniteLoopsWithoutFrom() animation->stop(); } +void tst_qquickanimations::targetsDeletedNotRemoved() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("targetsDeletedWithoutRemoval.qml")); + QScopedPointer obj(component.create()); + QVERIFY2(obj.get(), qPrintable(component.errorString())); + { + QQmlListReference ref(obj.get(), "targets"); + QVERIFY(ref.isValid()); + QCOMPARE(ref.size(), 1); + QTRY_COMPARE(ref.at(0), nullptr); + } + { + QQmlListReference ref(obj.get(), "animTargets"); + QVERIFY(ref.isValid()); + QCOMPARE(ref.size(), 1); + QCOMPARE(ref.at(0), nullptr); + } +} + QTEST_MAIN(tst_qquickanimations) #include "tst_qquickanimations.moc" -- cgit v1.2.3 From bd6489e5d7172e091e79152cf74399d2f29db8f0 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 2 Sep 2022 15:35:32 +0200 Subject: Engine: Fix handling of attached objects in function calls When we use an attached object as an argument to a function call, we need to properly extract it from the QmlTypeWrapper. Before this change, we simpley left the argument pointer as null, which lead to subsequent crashes when it got dereferenced. Moreover, treat passing namespaces to functions expecting a QObject as a TypeError, by returning false from CallArgument::fromValue (used to crash for the same reason as with the attached object case). Fixes: QTBUG-106119 Change-Id: Ifa6a32e20a29935aff1f265eb0edd3e35ea1c11b Reviewed-by: Ulf Hermann (cherry picked from commit 84483bff4f4d49e5f87abf564021647f2fb2a8d0) Reviewed-by: Semih Yavuz --- src/qml/jsruntime/qv4qobjectwrapper.cpp | 20 ++++++++++++++++---- .../qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml | 8 ++++++++ .../qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml | 9 +++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 20 ++++++++++++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 5d000e7fe9..51d14feca3 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1889,12 +1889,24 @@ bool CallArgument::fromValue(QMetaType metaType, QV4::ExecutionEngine *engine, c } else if (callType == QMetaType::QObjectStar) { qobjectPtr = nullptr; type = callType; - if (const QV4::QObjectWrapper *qobjectWrapper = value.as()) + if (const QV4::QObjectWrapper *qobjectWrapper = value.as()) { qobjectPtr = qobjectWrapper->object(); - else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as()) - queryEngine = qmlTypeWrapper->isSingleton(); - else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr + } else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as()) { + if (qmlTypeWrapper->isSingleton()) { + queryEngine = true; + } else if (QObject *obj = qmlTypeWrapper->object()) { + // attached object case + qobjectPtr = obj; + return true; + } else { + // If this is a plain type wrapper without an instance, + // then we got a namespace, and that's a type error + type = QMetaType::UnknownType; + return false; + } + } else if (!value.isNull() && !value.isUndefined()) { // null and undefined are nullptr return false; + } } else if (callType == qMetaTypeId()) { qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, QMetaType {})); type = callType; diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml new file mode 100644 index 0000000000..7f1b5b0317 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { + id: root + required property QtObject invokableObject + + Component.onCompleted: root.invokableObject.method_QObject(Component) +} diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml new file mode 100644 index 0000000000..1904740b26 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs2.qml @@ -0,0 +1,9 @@ +import QtQml +import QtQml as NS + +QtObject { + id: root + required property QtObject invokableObject + + Component.onCompleted: root.invokableObject.method_QObject(NS) +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index df75729d8e..c468e3213b 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -3075,6 +3075,26 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->actuals().count(), 1); QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr)); + { + o->reset(); + QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs.qml")); + QScopedPointer root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) }; + QVERIFY(root); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 13); + QCOMPARE(o->actuals().count(), 1); + QCOMPARE(o->actuals().at(0).value()->metaObject()->className(), "QQmlComponentAttached"); + } + + { + o->reset(); + QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs2.qml")); + QScopedPointer root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) }; + QVERIFY(root); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), -1); // no function got called due to incompatible arguments + } + o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); -- cgit v1.2.3 From 13760c8d4ead6957eb083499f88f602add36c9a5 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 5 Aug 2022 10:37:24 +0200 Subject: qmlformat: Preserve variable declaration scope type If the user used for(let x;...) we need to preserve the let and must not change it to var. Fixes: QTBUG-105361 Change-Id: I49fc3797505b569cc9b8a9138dd57ec7e70d3eb9 Reviewed-by: Ulf Hermann (cherry picked from commit 84dd339e2eb3385eb143f9d5ab282a135635052d) --- src/qmldom/qqmldomreformatter.cpp | 27 +++++++++++++++------- .../qml/qmlformat/data/forWithLet.formatted.qml | 8 +++++++ tests/auto/qml/qmlformat/data/forWithLet.qml | 8 +++++++ tests/auto/qml/qmlformat/tst_qmlformat.cpp | 2 ++ 4 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 tests/auto/qml/qmlformat/data/forWithLet.formatted.qml create mode 100644 tests/auto/qml/qmlformat/data/forWithLet.qml diff --git a/src/qmldom/qqmldomreformatter.cpp b/src/qmldom/qqmldomreformatter.cpp index 7a5b15cdcf..fa8372e2ca 100644 --- a/src/qmldom/qqmldomreformatter.cpp +++ b/src/qmldom/qqmldomreformatter.cpp @@ -585,16 +585,27 @@ protected: return false; } + + void outputScope(VariableScope scope) { + switch (scope) { + case VariableScope::Const: + out("const "); + break; + case VariableScope::Let: + out("let "); + break; + case VariableScope::Var: + out("var "); + break; + default: + break; + } + } + bool visit(PatternElement *ast) override { if (ast->isForDeclaration) { - if (ast->scope == VariableScope::Var) { - out("var "); - } else if (ast->scope == VariableScope::Let) { - out("let "); - } else if (ast->scope == VariableScope::Const) { - out("const "); - } + outputScope(ast->scope); } accept(ast->bindingTarget); switch (ast->type) { @@ -678,7 +689,7 @@ protected: if (ast->initialiser) { accept(ast->initialiser); } else if (ast->declarations) { - out("var "); + outputScope(ast->declarations->declaration->scope); accept(ast->declarations); } out("; "); // ast->firstSemicolonToken diff --git a/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml b/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml new file mode 100644 index 0000000000..5fecc1d180 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/forWithLet.formatted.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { + function foo() { + for (let i = 0; i < 5; ++i) + console.log(i); + } +} diff --git a/tests/auto/qml/qmlformat/data/forWithLet.qml b/tests/auto/qml/qmlformat/data/forWithLet.qml new file mode 100644 index 0000000000..afed76ebce --- /dev/null +++ b/tests/auto/qml/qmlformat/data/forWithLet.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { +function foo() { +for (let i = 0; i < 5; ++i) +console.log(i); +} +} diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp index aa8ed2e9da..29a14900d2 100644 --- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp +++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp @@ -260,6 +260,8 @@ void TestQmlformat::testFormat_data() QTest::newRow("arrowFunctionWithBinding") << "arrowFunctionWithBinding.qml" << "arrowFunctionWithBinding.formatted.qml" << QStringList{}; + QTest::newRow("forWithLet") << "forWithLet.qml" + << "forWithLet.formatted.qml" << QStringList {}; } void TestQmlformat::testFormat() -- cgit v1.2.3 From 94888ad5893999786a28631bd8d8ebba57fec87c Mon Sep 17 00:00:00 2001 From: Seokha Ko Date: Thu, 27 Apr 2023 18:03:02 +0900 Subject: Blacklist failing tests in tst_flickableinterop pinchHandlerOnFlickable fail with QTBUG-112924. Blacklist it for now. It will be handled in QTBUG-113226 Task-number: QTBUG-113226 Task-number: QTBUG-112924 Change-Id: I58c646f957fb6f397405dc477d00eee770602e80 Reviewed-by: Richard Moe Gustavsen (cherry picked from commit 407675b1913a5138c636a730cf3f6a65ffed87fb) Reviewed-by: Ulf Hermann --- tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST index 4bf1b58b9a..d2a270279b 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST @@ -15,4 +15,6 @@ android android [touchAndDragHandlerOnFlickable] android - +# QTBUG-113226 +[pinchHandlerOnFlickable] +* -- cgit v1.2.3 From 9b00d83b11a7a1f54839281fe4d4f568ffae600b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 2 Sep 2022 21:39:03 +0200 Subject: Flickable: don't initiate grab if there are multiple touchpoints Flickable is meant to be flicked with one finger, not two or more. If the UI depends on using the pinch gesture on some child of the contentItem, Flickable shouldn't interfere with that. The user might accidentally touch the screen with extra fingers while trying to flick; Flickable should not care, as long as it has grabbed the one touchpoint that it's tracking before the second point appears. Removes QEXPECT_FAILs added in d7623d79ef4bc9533fced027bf1d173d68b4eba6 handleMoveEvent() can be called from filterPointerEvent(), or from mouseMoveEvent() in case of direct delivery; and filterPointerEvent() will now avoid stealing the grab during multi-touch press or release as well. The "ignoring multi-touch" categorized log message is emitted twice while filtering a move event, but only once in the other cases. Fixes: QTBUG-77629 Task-number: QTBUG-85278 Task-number: QTBUG-106223 Fixes: QTBUG-109655 Change-Id: I7a19deaed3b2255de2b686f0d96bd08fe5ab1636 Reviewed-by: Doris Verria (cherry picked from commit 5171bcd9ce99e794c1091069810314fb5457703b) Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 20 ++++-- .../flickableinterop/data/pinchOnFlickable.qml | 49 +++++++++++++++ .../flickableinterop/tst_flickableinterop.cpp | 72 ++++++++++++++++++++++ .../qquickmultipointtoucharea/data/inFlickable.qml | 4 +- .../tst_qquickmultipointtoucharea.cpp | 17 +++-- 5 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 64e2ebc7f7..44b0f4dd47 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1393,10 +1393,14 @@ void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event) const QVector2D velocity = firstPointLocalVelocity(event); bool overThreshold = false; - if (q->yflick()) - overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint); - if (q->xflick()) - overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint); + if (event->pointCount() == 1) { + if (q->yflick()) + overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint); + if (q->xflick()) + overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint); + } else { + qCDebug(lcFilter) << q->objectName() << "ignoring multi-touch" << event; + } drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity); } @@ -2567,11 +2571,17 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev QQuickDeliveryAgentPrivate::isTabletEvent(event))) return false; // don't filter hover events or wheel events, for example Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself"); - qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver; Q_D(QQuickFlickable); // If a touch event contains a new press point, don't steal right away: watch the movements for a while if (isTouch && static_cast(event)->touchPointStates().testFlag(QEventPoint::State::Pressed)) d->stealMouse = false; + // If multiple touchpoints are within bounds, don't grab: it's probably meant for multi-touch interaction in some child + if (event->pointCount() > 1) { + qCDebug(lcFilter) << objectName() << "ignoring multi-touch" << event << "for" << receiver; + d->stealMouse = false; + } else { + qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver; + } const auto &firstPoint = event->points().first(); if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) == this) { diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml new file mode 100644 index 0000000000..a4867d502c --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickable.qml @@ -0,0 +1,49 @@ +// 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 + +Flickable { + id: root + width: 800 + height: 480 + contentWidth: 1000 + contentHeight: 600 + + Rectangle { + id: pinchable + objectName: "pinchable" + border.color: "black" + color: pinch.active ? "salmon" : "peachpuff" + x: 100 + y: 100 + width: 200 + height: 200 + radius: 80 + PinchHandler { + id: pinch + } + PointHandler { + id: p1 + target: Rectangle { + parent: pinchable + color: "green" + visible: p1.active + x: p1.point.position.x - width / 2 + y: p1.point.position.y - height / 2 + width: 9; height: width; radius: width / 2 + } + } + PointHandler { + id: p0 + target: Rectangle { + parent: pinchable + color: "red" + visible: p0.active + x: p0.point.position.x - width / 2 + y: p0.point.position.y - height / 2 + width: 9; height: width; radius: width / 2 + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp index 5925965c55..0bf6b3fdcc 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp +++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp @@ -80,6 +80,7 @@ private slots: void touchDragSliderAndFlickable(); void touchAndDragHandlerOnFlickable_data(); void touchAndDragHandlerOnFlickable(); + void pinchHandlerOnFlickable(); private: void createView(QScopedPointer &window, const char *fileName); @@ -807,6 +808,77 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable() touchSeq.release(1, p1, window).commit(); } +void tst_FlickableInterop::pinchHandlerOnFlickable() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pinchOnFlickable.qml"))); + QQuickFlickable *flickable = qmlobject_cast(window.rootObject()); + QVERIFY(flickable); + QQuickPointerHandler *pinchHandler = flickable->findChild(); + QVERIFY(pinchHandler); + QQuickItem *pinchable = pinchHandler->target(); + QVERIFY(pinchable); + + QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted); + QSignalSpy grabChangedSpy(touchDevice, &QPointingDevice::grabChanged); + + QObject *grabber = nullptr; + connect(touchDevice, &QPointingDevice::grabChanged, + [&grabber](QObject *g, QPointingDevice::GrabTransition transition, const QPointerEvent *, const QEventPoint &) { + if (transition == QPointingDevice::GrabTransition::GrabExclusive) + grabber = g; + }); + + QPoint p0 = pinchable->mapToScene({50, 100}).toPoint(); + QPoint p1 = pinchable->mapToScene({150, 100}).toPoint(); + QTest::QTouchEventSequence touch = QTest::touchEvent(&window, touchDevice); + + touch.press(0, p0, &window).press(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + int activeStep = -1; + int grabTransitionCount = 0; + // drag two fingers down: PinchHandler moves the item; Flickable doesn't grab, because there are 2 points + for (int i = 0; i < 4; ++i) { + p0 += QPoint(0, dragThreshold); + p1 += QPoint(0, dragThreshold); + touch.move(0, p0, &window).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (pinchHandler->active() && activeStep < 0) { + qCDebug(lcPointerTests) << "pinch began at step" << i; + activeStep = i; + QCOMPARE(grabber, pinchHandler); + grabTransitionCount = grabChangedSpy.count(); + } + } + QVERIFY(pinchHandler->active()); + QCOMPARE(grabChangedSpy.count(), grabTransitionCount); + QCOMPARE(grabber, pinchHandler); + qreal scale = pinchable->scale(); + QCOMPARE(scale, 1); + qreal rot = pinchable->rotation(); + QCOMPARE(rot, 0); + // start expanding and rotating + for (int i = 0; i < 4; ++i) { + p0 += QPoint(-5, 10); + p1 += QPoint(5, -10); + touch.move(0, p0, &window).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + QVERIFY(pinchHandler->active()); + // PinchHandler keeps grab: no more transitions + QCOMPARE(grabChangedSpy.count(), grabTransitionCount); + QCOMPARE(grabber, pinchHandler); + QVERIFY(pinchable->scale() > scale); + scale = pinchable->scale(); + QVERIFY(pinchable->rotation() < rot); + rot = pinchable->rotation(); + } + touch.release(0, p0, &window).release(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(pinchHandler->active(), false); + QCOMPARE(flickMoveSpy.count(), 0); // Flickable never moved +} + QTEST_MAIN(tst_FlickableInterop) #include "tst_flickableinterop.moc" diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml index 32733613b3..fada2b3370 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml +++ b/tests/auto/quick/qquickmultipointtoucharea/data/inFlickable.qml @@ -43,8 +43,8 @@ Rectangle { TouchPoint { id: point2; objectName: "point2" } ] - onCanceled: root.cancelCount = touchPoints.length - onTouchUpdated: root.touchCount = touchPoints.length + onCanceled: (touchPoints) => root.cancelCount = touchPoints.length + onTouchUpdated: (touchPoints) => root.touchCount = touchPoints.length Text { text: "①" diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 44b5920cdf..707f34989d 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -595,7 +595,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable() QPoint p1(20,100); QPoint p2(40,100); - // moving one point vertically + // moving one point vertically: flickable gets the grab QTest::touchEvent(window.data(), device).press(0, p1); QQuickTouchUtils::flush(window.data()); @@ -619,7 +619,8 @@ void tst_QQuickMultiPointTouchArea::inFlickable() QTRY_VERIFY(!flickable->isMoving()); - // moving two points vertically + // moving two points vertically: MPTAs handle them, Flickable ignores multi-touch. + // The stray mouse events simulate OS-level synth-from-touch, and should not interfere. p1 = QPoint(20,100); QTest::touchEvent(window.data(), device).press(0, p1).press(1, p2); QTest::mousePress(window.data(), Qt::LeftButton, Qt::NoModifier, p1); @@ -641,11 +642,11 @@ void tst_QQuickMultiPointTouchArea::inFlickable() i, p1.x(), p1.y(), p2.x(), p2.y(), flickable->contentY()); } - QVERIFY(flickable->contentY() < 0); - QCOMPARE(point11->pressed(), false); - QCOMPARE(point12->pressed(), false); - QCOMPARE(window->rootObject()->property("cancelCount").toInt(), 2); - QCOMPARE(window->rootObject()->property("touchCount").toInt(), 0); + QCOMPARE(flickable->contentY(), 0); + QCOMPARE(point11->pressed(), true); + QCOMPARE(point12->pressed(), true); + QCOMPARE(window->rootObject()->property("cancelCount").toInt(), 0); + QCOMPARE(window->rootObject()->property("touchCount").toInt(), 2); QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2); QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, p1); @@ -680,10 +681,8 @@ void tst_QQuickMultiPointTouchArea::inFlickable() i, p1.x(), p1.y(), p2.x(), p2.y(), flickable->contentY()); } - QEXPECT_FAIL("", "currently flickable does grab the actual mouse", Continue); QCOMPARE(flickable->contentY(), qreal(0)); QCOMPARE(point11->pressed(), true); - QEXPECT_FAIL("", "currently flickable does grab the actual mouse", Continue); QCOMPARE(point12->pressed(), true); QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2); -- cgit v1.2.3 From 89871b69da134282bdb31b548bf7518da29fbdd2 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 18 Oct 2022 13:28:52 +0200 Subject: Add QQuickTest::pointerPress/Move/Release convenience functions More autotests should test mouse, especially touch and maybe tablet events, with similar expectations, instead of testing mainly mouse and mostly neglecting the other device types. This makes it easier to work with data-driven tests that take specific QPointingDevice instances as test data. Started using them in tst_TapHandler::gesturePolicyDragWithinBounds() which was already data-driven by device type. Clearly, this code is now easier to read and less repetitious. In tst_qquickwindow::subclassWithPointerEventVirtualOverrides() the improvement is somewhat smaller, but this test validates that tablet events are working. Change-Id: I74d0fcc2f082af3737a0754c58205fa2b18c1a2d Reviewed-by: Richard Moe Gustavsen (cherry picked from commit 59d967a8fb60bb764654a857cac20196358865eb) Reviewed-by: Ulf Hermann --- src/quicktestutils/quick/viewtestutils.cpp | 87 ++++++++++++++++++++++ src/quicktestutils/quick/viewtestutils_p.h | 11 +++ tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 45 +++++------ 3 files changed, 118 insertions(+), 25 deletions(-) diff --git a/src/quicktestutils/quick/viewtestutils.cpp b/src/quicktestutils/quick/viewtestutils.cpp index b31bbf47e2..007008b627 100644 --- a/src/quicktestutils/quick/viewtestutils.cpp +++ b/src/quicktestutils/quick/viewtestutils.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -471,6 +472,10 @@ namespace QQuickTouchUtils { } +namespace QTest { + int Q_TESTLIB_EXPORT defaultMouseDelay(); +} + namespace QQuickTest { /*! \internal @@ -528,6 +533,88 @@ namespace QQuickTest { return false; return true; } + + // TODO maybe move the generic pointerPress/Move/Release functions to QTestLib later on + + static Qt::MouseButton pressedTabletButton = Qt::NoButton; + static Qt::KeyboardModifiers pressedTabletModifiers = Qt::NoModifier; + + void pointerPress(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p, + Qt::MouseButton button, Qt::KeyboardModifiers modifiers) + { + switch (dev->type()) { + case QPointingDevice::DeviceType::Mouse: + case QPointingDevice::DeviceType::TouchPad: + QTest::mousePress(window, button, modifiers, p); + break; + case QPointingDevice::DeviceType::TouchScreen: + QTest::touchEvent(window, const_cast(dev)).press(pointId, p, window); + QQuickTouchUtils::flush(window); + break; + case QPointingDevice::DeviceType::Puck: + case QPointingDevice::DeviceType::Stylus: + case QPointingDevice::DeviceType::Airbrush: + QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); + pressedTabletButton = button; + pressedTabletModifiers = modifiers; + QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p), + button, 0.8, 0, 0, 0, 0, 0, modifiers); + break; + default: + qWarning() << "can't send a press event from" << dev; + break; + } + } + + void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p) + { + switch (dev->type()) { + case QPointingDevice::DeviceType::Mouse: + case QPointingDevice::DeviceType::TouchPad: + QTest::mouseMove(window, p); + break; + case QPointingDevice::DeviceType::TouchScreen: + QTest::touchEvent(window, const_cast(dev)).move(pointId, p, window); + QQuickTouchUtils::flush(window); + break; + case QPointingDevice::DeviceType::Puck: + case QPointingDevice::DeviceType::Stylus: + case QPointingDevice::DeviceType::Airbrush: + QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); + QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p), + pressedTabletButton, 0, 0, 0, 0, 0, 0, pressedTabletModifiers); + break; + default: + qWarning() << "can't send a move event from" << dev; + break; + } + } + + void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p, + Qt::MouseButton button, Qt::KeyboardModifiers modifiers) + { + switch (dev->type()) { + case QPointingDevice::DeviceType::Mouse: + case QPointingDevice::DeviceType::TouchPad: + QTest::mouseRelease(window, button, modifiers, p); + break; + case QPointingDevice::DeviceType::TouchScreen: + QTest::touchEvent(window, const_cast(dev)).release(pointId, p, window); + QQuickTouchUtils::flush(window); + break; + case QPointingDevice::DeviceType::Puck: + case QPointingDevice::DeviceType::Stylus: + case QPointingDevice::DeviceType::Airbrush: + QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); + QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p), + Qt::NoButton, 0, 0, 0, 0, 0, 0, modifiers); + break; + default: + qWarning() << "can't send a press event from" << dev; + break; + } + } + } QT_END_NAMESPACE diff --git a/src/quicktestutils/quick/viewtestutils_p.h b/src/quicktestutils/quick/viewtestutils_p.h index d2fb639230..50edb96718 100644 --- a/src/quicktestutils/quick/viewtestutils_p.h +++ b/src/quicktestutils/quick/viewtestutils_p.h @@ -203,6 +203,17 @@ namespace QQuickTest { [[nodiscard]] bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut = true, QByteArray *errorMessage = nullptr); [[nodiscard]] bool showView(QQuickView &v, const QUrl &url); + + void pointerPress(const QPointingDevice *dev, QQuickWindow *window, + int pointId, const QPoint &p, Qt::MouseButton button = Qt::LeftButton, + Qt::KeyboardModifiers modifiers = Qt::NoModifier); + + void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, + const QPoint &p); + + void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId, + const QPoint &p, Qt::MouseButton button = Qt::LeftButton, + Qt::KeyboardModifiers modifiers = Qt::NoModifier); } QT_END_NAMESPACE diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 773ccdac99..fc17457a3c 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -445,6 +445,9 @@ public: , touchDevice(QTest::createTouchDevice()) , touchDeviceWithVelocity(QTest::createTouchDevice(QInputDevice::DeviceType::TouchScreen, QInputDevice::Capability::Position | QPointingDevice::Capability::Velocity)) + , tabletStylusDevice(QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType::Stylus, + QPointingDevice::PointerType::Pen, + QPointingDeviceUniqueId::fromNumericId(1234567890))) { QQuickWindow::setDefaultAlphaBuffer(true); } @@ -568,6 +571,7 @@ private slots: private: QPointingDevice *touchDevice; QPointingDevice *touchDeviceWithVelocity; + const QPointingDevice *tabletStylusDevice; }; #if QT_CONFIG(opengl) @@ -3637,16 +3641,16 @@ void tst_qquickwindow::cleanupGrabsOnRelease() void tst_qquickwindow::subclassWithPointerEventVirtualOverrides_data() { - QTest::addColumn("deviceType"); + QTest::addColumn("device"); - QTest::newRow("mouse click") << QPointingDevice::DeviceType::Mouse; - QTest::newRow("touch tap") << QPointingDevice::DeviceType::TouchScreen; - QTest::newRow("stylus tap") << QPointingDevice::DeviceType::Stylus; + QTest::newRow("mouse click") << QPointingDevice::primaryPointingDevice(); + QTest::newRow("touch tap") << touchDevice; + QTest::newRow("stylus tap") << tabletStylusDevice; } void tst_qquickwindow::subclassWithPointerEventVirtualOverrides() // QTBUG-97859 { - QFETCH(QPointingDevice::DeviceType, deviceType); + QFETCH(const QPointingDevice *, device); PointerRecordingWindow window; window.resize(250, 250); @@ -3654,32 +3658,23 @@ void tst_qquickwindow::subclassWithPointerEventVirtualOverrides() // QTBUG-97859 window.setTitle(QTest::currentTestFunction()); window.show(); QVERIFY(QTest::qWaitForWindowActive(&window)); - const qint64 stylusId = 1234567890; - const QPoint pos(120, 120); - switch (static_cast(deviceType)) { + + QQuickTest::pointerPress(device, &window, 0, pos); + QQuickTest::pointerRelease(device, &window, 0, pos); + + switch (device->type()) { case QPointingDevice::DeviceType::Mouse: - QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, pos); - QTRY_COMPARE(window.m_mouseEvents.count(), 3); // separate move before press - QCOMPARE(window.m_events.count(), 3); + QTRY_COMPARE(window.m_mouseEvents.size(), 3); // separate move before press + QCOMPARE(window.m_events.size(), 3); break; case QPointingDevice::DeviceType::TouchScreen: - QTest::touchEvent(&window, touchDevice).press(0, pos, &window); - QTest::touchEvent(&window, touchDevice).release(0, pos, &window); - QTRY_COMPARE(window.m_touchEvents.count(), 2); - QCOMPARE(window.m_events.count(), 2); + QTRY_COMPARE(window.m_touchEvents.size(), 2); + QCOMPARE(window.m_events.size(), 2); break; case QPointingDevice::DeviceType::Stylus: - // press (pressure is 0.8) - QWindowSystemInterface::handleTabletEvent(&window, pos, window.mapToGlobal(pos), - int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen), - Qt::LeftButton, 0.8, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier); - // release (pressure is 0) - QWindowSystemInterface::handleTabletEvent(&window, pos, window.mapToGlobal(pos), - int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen), - Qt::NoButton, 0, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier); - QTRY_COMPARE(window.m_tabletEvents.count(), 2); - QVERIFY(window.m_events.count() >= window.m_tabletEvents.count()); // tablet + synth-mouse events + QTRY_COMPARE(window.m_tabletEvents.size(), 2); + QVERIFY(window.m_events.size() >= window.m_tabletEvents.size()); // tablet + synth-mouse events break; default: break; -- cgit v1.2.3 From dac2d5f28cee2fb77d2f58e61cf745c229065eaf Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Wed, 13 Sep 2023 04:05:37 +0000 Subject: Update dependencies on 'tqtc/lts-6.2' in qt/tqtc-qtdeclarative Change-Id: I056b1fe67120dae4908d277793439603f848addb Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index 281a53180e..fa82379f05 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 3fe5ac02a2d8b3ce034c4119d9ec1365b9372b41 + ref: 80ff8c87cee26a07e3160bf947346ace34c3396d required: true ../tqtc-qtimageformats: - ref: e18a7eb356353287b84c3db4d9669eae7abd7f0e + ref: 2f7a579c00d432b21f3e78da55eff57b013bda99 required: false ../tqtc-qtshadertools: - ref: 651dfc1a305d8cf0bbf2fc841befe68184a11d6d + ref: 693ae168c393b14fc3e76cec302653c7b81b1f63 required: false ../tqtc-qtsvg: - ref: fdd1dae7cdb4e442ca5056daf79e2eeebbf9fe1d + ref: 44e82754308b91e85ec3e5ed6ff0210c6cd63c0c required: false -- cgit v1.2.3 From 003836d02bc0f93912d0feb9fe92ae82e4eef3b0 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 27 Jul 2022 17:09:34 +0200 Subject: Integrate property binding evaluation fix from qtbase This is basically the same as QTBUG-105204, only with the QML engine being involved. Fixes: QTBUG-104982 Change-Id: I5afaadedcd7af41198702a8f2331703b4f6ef2e7 Reviewed-by: Sami Shalayel Reviewed-by: Ulf Hermann (cherry picked from commit 2879c7c3411955b1d4f795436f803abd960c176d) Reviewed-by: Qt Cherry-pick Bot --- src/qml/qml/qqmlpropertybinding.cpp | 8 +++++--- .../data/qpropertyBindingNoQPropertyCapture.qml | 19 ++++++++++++++++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 23 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml diff --git a/src/qml/qml/qqmlpropertybinding.cpp b/src/qml/qml/qqmlpropertybinding.cpp index be37b33c81..9482bc7b43 100644 --- a/src/qml/qml/qqmlpropertybinding.cpp +++ b/src/qml/qml/qqmlpropertybinding.cpp @@ -163,7 +163,8 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP void QQmlPropertyBindingJS::expressionChanged() { - if (!asBinding()->propertyDataPtr) + auto binding = asBinding(); + if (!binding->propertyDataPtr) return; if (QQmlData::wasDeleted(asBinding()->target())) return; @@ -185,8 +186,9 @@ void QQmlPropertyBindingJS::expressionChanged() return; } m_error.setTag(InEvaluationLoop); - asBinding()->evaluateRecursive(); - asBinding()->notifyRecursive(); + PendingBindingObserverList bindingObservers; + binding->evaluateRecursive(bindingObservers); + binding->notifyNonRecursive(bindingObservers); m_error.setTag(NoTag); } diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml new file mode 100644 index 0000000000..d3a151efe3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qpropertyBindingNoQPropertyCapture.qml @@ -0,0 +1,19 @@ +import QtQuick + +Item { + objectName: "redRectangle" + id: redRectangle + + property bool b: false + function toggle() { b = !b } + width: b ? 600 : 500 + + Item { + id: blueRectangle + objectName: "blueRectangle" + // width: b ? (100 + redRectangle.width / 2) : 25 + width: b ? redRectangle.width : 25 + } + + property int blueRectangleWidth: blueRectangle.width +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index c468e3213b..e976ec46a3 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -314,6 +314,7 @@ private slots: void bindingBoundFunctions(); void qpropertyAndQtBinding(); void qpropertyBindingReplacement(); + void qpropertyBindingNoQPropertyCapture(); void deleteRootObjectInCreation(); void onDestruction(); void onDestructionViaGC(); @@ -7664,6 +7665,28 @@ void tst_qqmlecmascript::qpropertyBindingReplacement() QCOMPARE(root->objectName(), u"overwritten"_qs); } +void tst_qqmlecmascript::qpropertyBindingNoQPropertyCapture() +{ + + QQmlEngine engine; + QQmlComponent comp(&engine, testFileUrl("qpropertyBindingNoQPropertyCapture.qml")); + std::unique_ptr root(comp.create()); + QVERIFY2(root, qPrintable(comp.errorString())); + auto redRectangle = root.get(); + + QQmlProperty blueRectangleWidth(redRectangle, "blueRectangleWidth", &engine); + + auto toggle = [&](){ + QMetaObject::invokeMethod(root.get(), "toggle"); + }; + + QCOMPARE(blueRectangleWidth.read().toInt(), 25); + toggle(); + QCOMPARE(blueRectangleWidth.read().toInt(), 600); + toggle(); + QCOMPARE(blueRectangleWidth.read().toInt(), 25); +} + void tst_qqmlecmascript::deleteRootObjectInCreation() { QQmlEngine engine; -- cgit v1.2.3 From 47d0f16da6df49ea9a8633f8397046799d23024d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Sun, 23 Oct 2022 10:09:55 +0200 Subject: Use const QPointingDevices in tests When a data-driven test has QTest::addColumn("device"); QTestData::append() does a type check with QTEST_ASSERT if it's not an exact match. That can result in a crash, apparently, although it's not clear then how 59d967a8fb60bb764654a857cac20196358865eb got through CI the first time. Amends 59d967a8fb60bb764654a857cac20196358865eb Task-number: QTBUG-107863 Task-number: QTBUG-107864 Task-number: QTBUG-117079 Change-Id: I383191b604eb504d49b0d51a23ecc45160f3383b Reviewed-by: Shawn Rutledge (cherry picked from commit 1a8a47d574438c2e550647fb66c2cbf175b19a1f) Reviewed-by: Fabian Kosmale (cherry picked from commit 54227bc58abaa3ffd84150dfc80863035201d09f) --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index fc17457a3c..992709b865 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -569,8 +569,8 @@ private slots: void visibleVsVisibility(); private: - QPointingDevice *touchDevice; - QPointingDevice *touchDeviceWithVelocity; + QPointingDevice *touchDevice; // TODO make const after fixing QTBUG-107864 + const QPointingDevice *touchDeviceWithVelocity; const QPointingDevice *tabletStylusDevice; }; @@ -3644,7 +3644,7 @@ void tst_qquickwindow::subclassWithPointerEventVirtualOverrides_data() QTest::addColumn("device"); QTest::newRow("mouse click") << QPointingDevice::primaryPointingDevice(); - QTest::newRow("touch tap") << touchDevice; + QTest::newRow("touch tap") << static_cast(touchDevice); // TODO QTBUG-107864 QTest::newRow("stylus tap") << tabletStylusDevice; } -- cgit v1.2.3 From 6d1d0e616737f3def2c558ec52b13204e20d1e11 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 27 Sep 2023 14:47:42 +0200 Subject: Skip tst_QQuickMultiPointTouchArea::inFlickable on Android It crashes because of an assert: QPointingDevicePrivate::queryPointById() fails to find the expected eventpoint. Task-number: QTBUG-117079 Change-Id: I85dcd7615cddc26d3c3adfe51473bb4074a1c925 Reviewed-by: Shawn Rutledge (cherry picked from commit 467563f33545d8cdc526b3f62a0a19bf49756896) --- .../quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 707f34989d..3e6a7fce16 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -577,6 +577,10 @@ void tst_QQuickMultiPointTouchArea::nested() void tst_QQuickMultiPointTouchArea::inFlickable() { +#ifdef Q_OS_ANDROID + QSKIP("On Android, QPointingDevicePrivate::queryPointById() sometimes fails to find a point 'in play' (QTBUG-117079)"); +#endif + QScopedPointer window(createAndShowView("inFlickable.qml")); QVERIFY(window->rootObject() != nullptr); -- cgit v1.2.3 From 33e04685b27fc887dea2f71dbdf23850febc54dd Mon Sep 17 00:00:00 2001 From: Qt Submodule Update Bot Date: Thu, 28 Sep 2023 20:40:49 +0000 Subject: Update dependencies on 'tqtc/lts-6.2.10' in qt/tqtc-qtdeclarative Change-Id: I553081d7166160c7cdca78076d884aea336a8c29 Reviewed-by: Qt Submodule Update Bot --- dependencies.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dependencies.yaml b/dependencies.yaml index fa82379f05..6e518f3447 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../tqtc-qtbase: - ref: 80ff8c87cee26a07e3160bf947346ace34c3396d + ref: 8d7d8b9bbc72e612c18815cca0fdeb0539c9c1a7 required: true ../tqtc-qtimageformats: - ref: 2f7a579c00d432b21f3e78da55eff57b013bda99 + ref: e14870c7268408e4cbffafcbe4eb34f97d5295c2 required: false ../tqtc-qtshadertools: - ref: 693ae168c393b14fc3e76cec302653c7b81b1f63 + ref: 219736208b92b3f12749bc1edf10cc75639842e4 required: false ../tqtc-qtsvg: - ref: 44e82754308b91e85ec3e5ed6ff0210c6cd63c0c + ref: 908aa16c002af8f9762e2239171923f8d2bbe38f required: false -- cgit v1.2.3