diff options
Diffstat (limited to 'tests/auto/quick/pointerhandlers')
87 files changed, 4026 insertions, 2058 deletions
diff --git a/tests/auto/quick/pointerhandlers/CMakeLists.txt b/tests/auto/quick/pointerhandlers/CMakeLists.txt index f8ac22d531..fcd00cddc6 100644 --- a/tests/auto/quick/pointerhandlers/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from pointerhandlers.pro. if(QT_FEATURE_private_tests) diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST index 47c11f14d8..f5259a9e64 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST @@ -5,7 +5,23 @@ opensuse-leap windows gcc [touchDragFlickableBehindButton] windows gcc +# QTBUG-95887 +[mouseDragSlider] +opensuse-leap +# QTBUG-95887 +[touchDragFlickableBehindButton] +opensuse-leap +# QTBUG-95887 +[mouseClickButton] +opensuse-leap +# QTBUG-95887 +[mouseDragFlickableBehindSlider] +opensuse-leap +# QTBUG-103061 +[touchDragFlickableBehindItemWithHandlers] +android +[mouseDragFlickableBehindItemWithHandlers] +android [touchAndDragHandlerOnFlickable] -macos # QTBUG-86729 -[touchDragSliderAndFlickable] -* # QTBUG-86729 +android + diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt index f67f9a0a59..3836aefbb2 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/flickableinterop/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from flickableinterop.pro. ##################################################################### ## tst_flickableinterop Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_flickableinterop LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_flickableinterop SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_flickableinterop.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_flickableinterop qt_internal_extend_target(tst_flickableinterop CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_flickableinterop CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml index 4b2935b52e..9483a12d1c 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/FlashAnimation.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the manual tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml index 7a32170bed..ec790c9b99 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/GrooveDragSlider.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml index b3d621c447..cba135269f 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/KnobDragSlider.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml index 2c9fa30a70..99b53e6afb 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/TapHandlerButton.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml index 7bdae0e76b..f6748da19c 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnFlickable.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml index 00f3d66049..562dc156f9 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnList.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml index ee63c1aa79..bbd5f5b278 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/dragOnTable.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml index 5693a6acd2..740b698fd4 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/flickableWithHandlers.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 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..e594f165b2 --- /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 + +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/data/pinchOnFlickableWithParentTapHandler.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml new file mode 100644 index 0000000000..2660952f16 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/pinchOnFlickableWithParentTapHandler.qml @@ -0,0 +1,24 @@ +import QtQuick + +Rectangle { + width: 320 + height: 320 + + TapHandler { + onTapped: color = "tomato" + } + + Flickable { + anchors.fill: parent + contentWidth: content.width + contentHeight: content.height + Rectangle { + id: content + objectName: "pinchable" + width: 150 + height: 150 + color: "wheat" + PinchHandler {} + } + } +} diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml index 56ae514b96..dc32b1d82c 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnFlickable.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml index 2ad92e8b71..f4c0e5daaa 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnList.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml index 46b01ee577..0293ad03b0 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml +++ b/tests/auto/quick/pointerhandlers/flickableinterop/data/tapOnTable.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp index 869ba80658..790c7c771e 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp +++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -36,6 +11,7 @@ #include <QtQuick/private/qquickitemview_p.h> #include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/private/qquickdraghandler_p.h> +#include <QtQuick/private/qquickpinchhandler_p.h> #include <QtQuick/private/qquicktaphandler_p.h> #include <QtQuick/private/qquicktableview_p.h> #include <qpa/qwindowsysteminterface.h> @@ -45,8 +21,8 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -55,7 +31,7 @@ class tst_FlickableInterop : public QQmlDataTest Q_OBJECT public: tst_FlickableInterop() - :touchDevice(QTest::createTouchDevice()) + : QQmlDataTest(QT_QMLTEST_DATADIR) {} private slots: @@ -79,10 +55,15 @@ private slots: void touchDragSliderAndFlickable(); void touchAndDragHandlerOnFlickable_data(); void touchAndDragHandlerOnFlickable(); + void pinchHandlerOnFlickable(); + void nativeGesturePinchOnFlickableWithParentTapHandler_data(); + void nativeGesturePinchOnFlickableWithParentTapHandler(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); - QPointingDevice *touchDevice; + QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); + QScopedPointer<QPointingDevice> touchpad = QScopedPointer<QPointingDevice>( + QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad)); }; void tst_FlickableInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName) @@ -90,8 +71,8 @@ void tst_FlickableInterop::createView(QScopedPointer<QQuickView> &window, const window.reset(new QQuickView); window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickViewTestUtils::centerOnScreen(window.data()); + QQuickViewTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); @@ -121,27 +102,27 @@ void tst_FlickableInterop::touchTapButton() // Button changes pressed state and emits tapped on release QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(button->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 1); + QCOMPARE(tappedSpy.size(), 1); // We can drag <= dragThreshold and the button still acts normal, Flickable doesn't grab p1 = button->mapToScene(QPointF(20, 20)).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(button->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(button->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 2); + QCOMPARE(tappedSpy.size(), 2); } void tst_FlickableInterop::touchDragFlickableBehindButton_data() @@ -169,11 +150,11 @@ void tst_FlickableInterop::touchDragFlickableBehindButton() tappedSpy.clear(); QPoint p1 = button->mapToScene(QPointF(20, 20)).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(button->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(button->property("pressed").toBool()); int i = 0; @@ -181,17 +162,17 @@ void tst_FlickableInterop::touchDragFlickableBehindButton() // Button is no longer pressed because Flickable steals the grab for (; i < 100 && !flickable->isMoving(); ++i) { p1 += QPoint(1, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); } qCDebug(lcPointerTests) << "flickable started moving after" << i << "moves, when we got to" << p1; QVERIFY(flickable->isMoving()); QCOMPARE(i, 2); QVERIFY(!button->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(tappedSpy.size(), 0); } void tst_FlickableInterop::mouseClickButton_data() @@ -221,7 +202,7 @@ void tst_FlickableInterop::mouseClickButton() QTRY_VERIFY(button->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 1); + QCOMPARE(tappedSpy.size(), 1); // We can drag <= dragThreshold and the button still acts normal, Flickable doesn't grab p1 = button->mapToScene(QPointF(20, 20)).toPoint(); @@ -232,7 +213,7 @@ void tst_FlickableInterop::mouseClickButton() QVERIFY(button->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 2); + QCOMPARE(tappedSpy.size(), 2); } void tst_FlickableInterop::mouseDragFlickableBehindButton_data() @@ -265,7 +246,7 @@ void tst_FlickableInterop::mouseDragFlickableBehindButton() QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(button->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QVERIFY(button->property("pressed").toBool()); int i = 0; for (; i < 100 && !flickable->isMoving(); ++i) { @@ -278,7 +259,7 @@ void tst_FlickableInterop::mouseDragFlickableBehindButton() QVERIFY(!button->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QVERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(tappedSpy.size(), 0); } void tst_FlickableInterop::touchDragSlider() @@ -297,24 +278,24 @@ void tst_FlickableInterop::touchDragSlider() QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); QVERIFY(flickable); QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); - QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged); // Drag the slider in the allowed (vertical) direction tappedSpy.clear(); QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint() - QPoint(0, 8); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(slider->property("pressed").toBool()); p1 += QPoint(0, dragThreshold); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(slider->property("pressed").toBool()); QCOMPARE(slider->property("value").toInt(), 49); p1 += QPoint(0, 1); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); p1 += QPoint(0, 10); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(slider->property("value").toInt() < 49); QVERIFY(!flickable->isMoving()); @@ -323,15 +304,15 @@ void tst_FlickableInterop::touchDragSlider() // Now that the DragHandler is active, the Flickable will not steal the grab // even if we move a large distance horizontally p1 += QPoint(dragThreshold * 2, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!flickable->isMoving()); // Release, and do not expect the tapped signal - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); - QCOMPARE(tappedSpy.count(), 0); - QCOMPARE(translationChangedSpy.count(), 1); + QCOMPARE(tappedSpy.size(), 0); + QCOMPARE(translationChangedSpy.size(), 1); } void tst_FlickableInterop::mouseDragSlider_data() @@ -377,7 +358,7 @@ void tst_FlickableInterop::mouseDragSlider() QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); QVERIFY(flickable); QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); - QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged); // Drag the slider tappedSpy.clear(); @@ -415,8 +396,8 @@ void tst_FlickableInterop::mouseDragSlider() // Release, and do not expect the tapped signal QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QCOMPARE(tappedSpy.count(), 0); - QCOMPARE(translationChangedSpy.count(), expectedDragHandlerActive ? 1 : 0); + QCOMPARE(tappedSpy.size(), 0); + QCOMPARE(translationChangedSpy.size(), expectedDragHandlerActive ? 1 : 0); } void tst_FlickableInterop::touchDragFlickableBehindSlider() @@ -435,34 +416,34 @@ void tst_FlickableInterop::touchDragFlickableBehindSlider() QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); QVERIFY(flickable); QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); - QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged); // Button is no longer pressed if touchpoint goes beyond dragThreshold, // because Flickable steals the grab tappedSpy.clear(); QPoint p1 = knob->mapToScene(knob->clipRect().center()).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(slider->property("pressed").toBool()); p1 += QPoint(dragThreshold, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(slider->property("pressed").toBool()); int i = 0; for (; i < 100 && !flickable->isMoving(); ++i) { p1 += QPoint(1, 0); - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); } qCDebug(lcPointerTests) << "flickable started moving after" << i << "moves, when we got to" << p1; QVERIFY(flickable->isMoving()); QCOMPARE(i, 2); QVERIFY(!slider->property("pressed").toBool()); - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!slider->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 0); - QCOMPARE(translationChangedSpy.count(), 0); + QCOMPARE(tappedSpy.size(), 0); + QCOMPARE(translationChangedSpy.size(), 0); } void tst_FlickableInterop::mouseDragFlickableBehindSlider() @@ -481,7 +462,7 @@ void tst_FlickableInterop::mouseDragFlickableBehindSlider() QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); QVERIFY(flickable); QSignalSpy tappedSpy(knob->parent(), SIGNAL(tapped())); - QSignalSpy translationChangedSpy(drag, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy(drag, &QQuickDragHandler::translationChanged); // Button is no longer pressed if touchpoint goes beyond dragThreshold, // because Flickable steals the grab @@ -503,8 +484,8 @@ void tst_FlickableInterop::mouseDragFlickableBehindSlider() QCOMPARE(i, 2); QVERIFY(!slider->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QCOMPARE(tappedSpy.count(), 0); - QCOMPARE(translationChangedSpy.count(), 0); + QCOMPARE(tappedSpy.size(), 0); + QCOMPARE(translationChangedSpy.size(), 0); } void tst_FlickableInterop::touchDragFlickableBehindItemWithHandlers_data() @@ -532,19 +513,19 @@ void tst_FlickableInterop::touchDragFlickableBehindItemWithHandlers() QPoint p1 = rect->mapToScene(rect->clipRect().center()).toPoint(); QPoint originP1 = p1; - QTest::touchEvent(window, touchDevice).press(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).press(1, p1, window); QQuickTouchUtils::flush(window); for (int i = 0; i < dragThreshold * 3; ++i) { p1 = originP1; p1.rx() += i; - QTest::touchEvent(window, touchDevice).move(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).move(1, p1, window); QQuickTouchUtils::flush(window); } QCOMPARE(flickable->isMoving(), expectedFlickableMoving); if (!expectedFlickableMoving) { QVERIFY(rect->mapToScene(rect->clipRect().center()).toPoint().x() > originP1.x()); } - QTest::touchEvent(window, touchDevice).release(1, p1, window); + QTest::touchEvent(window, touchDevice.get()).release(1, p1, window); QQuickTouchUtils::flush(window); } @@ -609,7 +590,7 @@ void tst_FlickableInterop::touchDragSliderAndFlickable() QVERIFY(knob); QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>(); QVERIFY(flickable); - QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice, false); + QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice.get(), false); // The knob is initially centered over the slider's "groove" qreal initialXOffset = qAbs(knob->mapToScene(knob->clipRect().center()).x() - slider->mapToScene @@ -647,7 +628,10 @@ void tst_FlickableInterop::touchDragSliderAndFlickable() QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "step" << i << ": fingers @" << p1 << p2 << "is Flickable moving yet?" << flickable->isMoving(); } - QVERIFY(flickable->isMoving()); + // In Qt 6, Flickable doesn't see the second touchpoint, so it doesn't move. + // One way to see this is that Flickable is more immune to stray touches than it otherwise would be. + // But time will tell if we are missing out on something useful, which was possible in Qt 5 (QTBUG-123490). + QCOMPARE(flickable->isMoving(), false); qreal knobSliderXOffset = qAbs(knob->mapToScene(knob->clipRect().center()).toPoint().x() - slider->mapToScene(slider->clipRect().center()).toPoint().x()) - initialXOffset; if (knobSliderXOffset > 1) @@ -739,7 +723,7 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable() } // Drag one finger on the Flickable (between delegates) and make sure it flicks - QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice, false); + QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice.get(), false); QPoint p1(780, 460); if (delegate) p1 = delegate->mapToScene(delegate->clipRect().bottomRight()).toPoint() + QPoint(-1, 1); @@ -806,6 +790,187 @@ 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<QQuickFlickable*>(window.rootObject()); + QVERIFY(flickable); + QQuickPointerHandler *pinchHandler = flickable->findChild<QQuickPointerHandler*>(); + QVERIFY(pinchHandler); + QQuickItem *pinchable = pinchHandler->target(); + QVERIFY(pinchable); + + QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted); + QSignalSpy grabChangedSpy(touchDevice.get(), &QPointingDevice::grabChanged); + + QObject *grabber = nullptr; + connect(touchDevice.get(), &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.get()); + + 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); + QTRY_COMPARE_GT(pinchable->scale(), scale); + scale = pinchable->scale(); + QCOMPARE_LT(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 +} + +void tst_FlickableInterop::nativeGesturePinchOnFlickableWithParentTapHandler_data() +{ + QTest::addColumn<const QPointingDevice*>("device"); + QTest::addColumn<Qt::MouseButton>("button"); + QTest::addColumn<Qt::NativeGestureType>("gesture"); + QTest::addColumn<qreal>("value"); + QTest::addColumn<qreal>("expectedPropertyValue"); + + const QPointingDevice *constTouchPad = touchpad.data(); + + QTest::newRow("touchpad: left and rotate") << constTouchPad << Qt::LeftButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("touchpad: right and rotate") << constTouchPad << Qt::RightButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("touchpad: left and scale") << constTouchPad << Qt::LeftButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + QTest::newRow("touchpad: right and scale") << constTouchPad << Qt::RightButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + + const auto *mouse = QPointingDevice::primaryPointingDevice(); + if (mouse->type() == QInputDevice::DeviceType::Mouse) { + QTest::newRow("mouse: left and rotate") << mouse << Qt::LeftButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("mouse: right and rotate") << mouse << Qt::RightButton << Qt::RotateNativeGesture << 5.0 << 10.0; + QTest::newRow("mouse: left and scale") << mouse << Qt::LeftButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + QTest::newRow("mouse: right and scale") << mouse << Qt::RightButton << Qt::ZoomNativeGesture << 0.1 << 1.21; + } else { + qCWarning(lcPointerTests) << "skipping mouse tests: primary device is not a mouse" << mouse; + } +} + +void tst_FlickableInterop::nativeGesturePinchOnFlickableWithParentTapHandler() +{ + QFETCH(const QPointingDevice*, device); + QFETCH(Qt::MouseButton, button); + QFETCH(Qt::NativeGestureType, gesture); + QFETCH(qreal, value); + QFETCH(qreal, expectedPropertyValue); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pinchOnFlickableWithParentTapHandler.qml"))); + QQuickFlickable *flickable = window.rootObject()->findChild<QQuickFlickable*>(); + QVERIFY(flickable); + QQuickPointerHandler *pinchHandler = flickable->findChild<QQuickPinchHandler*>(); + QVERIFY(pinchHandler); + QQuickItem *pinchable = pinchHandler->target(); + QVERIFY(pinchable); + QQuickTapHandler *tapHandler = window.rootObject()->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandler); + const bool expectTap = button & tapHandler->acceptedButtons(); + + QSignalSpy flickMoveSpy(flickable, &QQuickFlickable::movementStarted); + QSignalSpy grabChangedSpy(touchDevice.get(), &QPointingDevice::grabChanged); + QSignalSpy tapActiveSpy(tapHandler, &QQuickTapHandler::activeChanged); + QSignalSpy tapSpy(tapHandler, &QQuickTapHandler::tapped); + + QObject *grabber = nullptr; + connect(device, &QPointingDevice::grabChanged, + [&grabber](QObject *g, QPointingDevice::GrabTransition transition, const QPointerEvent *, const QEventPoint &) { + if (transition == QPointingDevice::GrabTransition::GrabExclusive) + grabber = g; + }); + + const QPoint pinchPos(75, 75); + const QPoint outsidePos(200, 200); + + // move to position + QTest::mouseMove(&window, pinchPos); + + // pinch via native gesture + ulong ts = 502; // after the mouse move, which is at time 501 in practice + QWindowSystemInterface::handleGestureEvent(&window, ts++, touchpad.get(), + Qt::BeginNativeGesture, pinchPos, pinchPos); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + for (int i = 0; i < 2; ++i) { + QWindowSystemInterface::handleGestureEventWithRealValue(&window, ts++, touchpad.get(), + gesture, value, pinchPos, pinchPos); + } + if (gesture == Qt::RotateNativeGesture) + QTRY_COMPARE(pinchHandler->parentItem()->rotation(), expectedPropertyValue); + else if (gesture == Qt::ZoomNativeGesture) + QTRY_COMPARE(pinchHandler->parentItem()->scale(), expectedPropertyValue); + QVERIFY(pinchHandler->active()); + QCOMPARE(grabChangedSpy.count(), 0); + QCOMPARE(grabber, nullptr); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QWindowSystemInterface::handleGestureEvent(&window, ts++, touchpad.get(), + Qt::EndNativeGesture, pinchPos, pinchPos); + + // tap in square: TapHandler detects tap iff acceptedButtons permits + // TODO delay; unfortunately this also begins at timestamp 502 because we don't have testlib + // functions to send gesture events, and QQuickTest::pointerPress() doesn't take a delay value + QQuickTest::pointerPress(device, &window, 0, pinchPos, button); + QCOMPARE(tapHandler->point().id(), expectTap ? 0 : -1); + QQuickTest::pointerRelease(device, &window, 0, pinchPos, button); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(tapSpy.size() != 0, expectTap); + QCOMPARE(tapActiveSpy.size(), 0); + QCOMPARE(tapHandler->point().id(), -1); // does not keep tracking after release + + // move outside: nothing should happen; + // but QTBUG-108896 happened because TapHandler was setting pointInfo to track this moving point + QQuickTest::pointerMove(device, &window, 0, outsidePos); + QCOMPARE(tapHandler->point().id(), -1); // does not track after mouse move + + // tap outside: nothing happens + tapSpy.clear(); + tapActiveSpy.clear(); + QQuickTest::pointerPress(device, &window, 0, outsidePos, button); + QQuickTest::pointerRelease(device, &window, 0, outsidePos, button); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(tapSpy.size(), 0); + QCOMPARE(tapActiveSpy.size(), 0); +} + QTEST_MAIN(tst_FlickableInterop) #include "tst_flickableinterop.moc" diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST b/tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST new file mode 100644 index 0000000000..74d29b4ac3 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/BLACKLIST @@ -0,0 +1,7 @@ +# QTBUG-95938 +[dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch] +opensuse-leap + +# QTBUG-95938 +[dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse] +opensuse-leap diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt index f799de97b8..22123e6385 100644 --- a/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from mousearea_interop.pro. ##################################################################### ## tst_mousearea_interop Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_mousearea_interop LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_mousearea_interop SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_mousearea_interop.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_mousearea_interop qt_internal_extend_target(tst_mousearea_interop CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_mousearea_interop CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml new file mode 100644 index 0000000000..7adb205a1f --- /dev/null +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragHandlerInMouseAreaGrandparent.qml @@ -0,0 +1,20 @@ +import QtQuick 2.12 + +Rectangle { + width: 200; height: 200 + + DragHandler { } + + Rectangle { + objectName: "button" + width: 100; height: 40; x: 10; y: 10 + border.color: "orange" + color: ma.pressed ? "lightsteelblue" : "beige" + + MouseArea { + id: ma + anchors.fill: parent + onDoubleClicked: console.log("__") + } + } +} diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml index 48b1dc86f0..1bde433dba 100644 --- a/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/dragTakeOverFromSibling.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/data/hoverHandlerInGrandparentOfHoverableItem.qml b/tests/auto/quick/pointerhandlers/mousearea_interop/data/hoverHandlerInGrandparentOfHoverableItem.qml new file mode 100644 index 0000000000..a3b957a1f7 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/data/hoverHandlerInGrandparentOfHoverableItem.qml @@ -0,0 +1,33 @@ +import QtQuick 2.12 + +Item { + width: 320 + height: 240 + + Rectangle { + color: hh.hovered ? "orange" : "gray" + anchors.fill: container + } + + Item { + id: container + anchors.fill: parent + anchors.margins: 40 + + Rectangle { + width: parent.width + height: 40 + color: ma.pressed ? "blue" : ma.containsMouse ? "aquamarine" : "beige" + + MouseArea { + id: ma + anchors.fill: parent + hoverEnabled: true + } + } + + HoverHandler { + id: hh + } + } +} diff --git a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp index 4709622245..b1a480b9cf 100644 --- a/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp @@ -1,43 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> +#include <QtGui/qstylehints.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> #include <QtQuick/private/qquickdraghandler_p.h> +#include <QtQuick/private/qquickhoverhandler_p.h> #include <QtQuick/private/qquickmousearea_p.h> #include <QtQuick/qquickitem.h> #include <QtQuick/qquickview.h> #include <QtGui/private/qpointingdevice_p.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> + +#include <QtCore/qpointer.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -46,12 +25,15 @@ class tst_MouseAreaInterop : public QQmlDataTest Q_OBJECT public: tst_MouseAreaInterop() + : QQmlDataTest(QT_QMLTEST_DATADIR) {} private slots: void dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse(); void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch_data(); void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch(); + void hoverHandlerDoesntHoverOnPress(); + void doubleClickInMouseAreaWithDragHandlerInGrandparent(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); @@ -63,8 +45,8 @@ void tst_MouseAreaInterop::createView(QScopedPointer<QQuickView> &window, const window.reset(new QQuickView); window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickViewTestUtils::centerOnScreen(window.data()); + QQuickViewTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); @@ -86,7 +68,7 @@ void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse QPoint p1(150, 150); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QCOMPARE(window->mouseGrabberItem(), ma); - QCOMPARE(ma->pressed(), true); + QCOMPARE(ma->isPressed(), true); // Start dragging // DragHandler keeps monitoring, due to its passive grab, @@ -104,7 +86,7 @@ void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab); QVERIFY(dragStoleGrab > 1); QCOMPARE(handler->active(), true); - QCOMPARE(ma->pressed(), false); + QCOMPARE(ma->isPressed(), false); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QCOMPARE(handler->active(), false); @@ -145,7 +127,7 @@ void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch QVERIFY(epd); QVERIFY(epd->passiveGrabbers.contains(handler.data())); QCOMPARE(epd->exclusiveGrabber, ma); - QCOMPARE(ma->pressed(), true); + QCOMPARE(ma->isPressed(), true); // Start dragging // DragHandler keeps monitoring, due to its passive grab, @@ -163,11 +145,11 @@ void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch if (preventStealing) { QCOMPARE(dragStoleGrab, 0); QCOMPARE(handler->active(), false); - QCOMPARE(ma->pressed(), true); + QCOMPARE(ma->isPressed(), true); } else { QVERIFY(dragStoleGrab > 1); QCOMPARE(handler->active(), true); - QCOMPARE(ma->pressed(), false); + QCOMPARE(ma->isPressed(), false); } touch.release(1, p1).commit(); @@ -175,6 +157,55 @@ void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch QCOMPARE(handler->active(), false); } +void tst_MouseAreaInterop::hoverHandlerDoesntHoverOnPress() // QTBUG-72843 +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("hoverHandlerInGrandparentOfHoverableItem.qml"))); + + QPointer<QQuickHoverHandler> handler = window.rootObject()->findChild<QQuickHoverHandler*>(); + QVERIFY(handler); + QQuickMouseArea *ma = window.rootObject()->findChild<QQuickMouseArea*>(); + QVERIFY(ma); + QPoint p = ma->mapToScene(ma->boundingRect().center()).toPoint(); + + // move the mouse below the "button" but within HoverHandler's region of interest + QTest::mouseMove(&window, p + QPoint(0, 50)); + QTRY_COMPARE(handler->isHovered(), true); + // move the mouse into the "button" + QTest::mouseMove(&window, p); + // both the hoverhandler and the mouse area should now be hovered! + QTRY_COMPARE(handler->isHovered(), true); + QCOMPARE(ma->hovered(), true); + + QSignalSpy hoveredChangedSpy(handler, SIGNAL(hoveredChanged())); + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p); + QTRY_COMPARE(ma->isPressed(), true); + QCOMPARE(handler->isHovered(), true); + QCOMPARE(hoveredChangedSpy.size(), 0); + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p); + QTRY_COMPARE(ma->isPressed(), false); + QCOMPARE(handler->isHovered(), true); + QCOMPARE(hoveredChangedSpy.size(), 0); +} + +void tst_MouseAreaInterop::doubleClickInMouseAreaWithDragHandlerInGrandparent() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("dragHandlerInMouseAreaGrandparent.qml"))); + + QQuickDragHandler *handler = window.rootObject()->findChild<QQuickDragHandler*>(); + QVERIFY(handler); + QSignalSpy dragActiveSpy(handler, &QQuickDragHandler::activeChanged); + QQuickMouseArea *ma = window.rootObject()->findChild<QQuickMouseArea*>(); + QVERIFY(ma); + QSignalSpy dClickSpy(ma, &QQuickMouseArea::doubleClicked); + QPoint p = ma->mapToScene(ma->boundingRect().center()).toPoint(); + + QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, p); + QCOMPARE(dClickSpy.size(), 1); + QCOMPARE(dragActiveSpy.size(), 0); +} + QTEST_MAIN(tst_MouseAreaInterop) #include "tst_mousearea_interop.moc" diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST index 3f01d3a7d4..80d6dd0ee8 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/BLACKLIST @@ -1,2 +1,3 @@ -[touchesThenPinch] -* # QTBUG-86729 +# QTBUG-118062 +[touchDrag] +opensuse-leap diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt index 4aac9440f9..9f19e8b427 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from multipointtoucharea_interop.pro. ##################################################################### ## tst_multipointtoucharea_interop Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_multipointtoucharea_interop LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_multipointtoucharea_interop SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_multipointtoucharea_interop.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_multipointtoucharea_interop qt_internal_extend_target(tst_multipointtoucharea_interop CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_multipointtoucharea_interop CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml index dc7e5f6411..ad4494eb25 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml index f1591a412e..199292e015 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 @@ -60,8 +35,8 @@ Rectangle { Item { id: crosshairs property TouchPoint touchPoint - x: touchPoint.x - width / 2 - y: touchPoint.y - height / 2 + x: touchPoint?.x - width / 2 + y: touchPoint?.y - height / 2 width: 300; height: 300 visible: touchPoint.pressed rotation: touchPoint.rotation diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml index 1bd20c6bcb..868a5265a6 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/unloadHandlerOnPress.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp index 0312ed0141..a4cc182422 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -37,8 +12,10 @@ #include <QtQuick/qquickview.h> #include <QtGui/private/qpointingdevice_p.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> + +#include <QtCore/qpointer.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -47,6 +24,7 @@ class tst_MptaInterop : public QQmlDataTest Q_OBJECT public: tst_MptaInterop() + : QQmlDataTest(QT_QMLTEST_DATADIR) {} private slots: @@ -67,8 +45,8 @@ void tst_MptaInterop::createView(QScopedPointer<QQuickView> &window, const char window.reset(new QQuickView); window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickViewTestUtils::centerOnScreen(window.data()); + QQuickViewTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); @@ -169,7 +147,7 @@ void tst_MptaInterop::touchesThenPinch() QQuickTouchUtils::flush(window); QVERIFY(tp.at(0)->property("pressed").toBool()); QTRY_VERIFY(tp.at(1)->property("pressed").toBool()); - QCOMPARE(mptaPressedSpy.count(), 2); + QCOMPARE(mptaPressedSpy.size(), 2); // Press a third touchpoint: MPTA grabs it too QPoint p3 = mpta->mapToScene(QPointF(110, 200)).toPoint(); @@ -178,8 +156,8 @@ void tst_MptaInterop::touchesThenPinch() QCOMPARE(tp.at(0)->property("pressed").toBool(), true); QCOMPARE(tp.at(1)->property("pressed").toBool(), true); QCOMPARE(tp.at(2)->property("pressed").toBool(), true); - QCOMPARE(mptaPressedSpy.count(), 3); - QCOMPARE(mptaCanceledSpy.count(), 0); + QCOMPARE(mptaPressedSpy.size(), 3); + QCOMPARE(mptaCanceledSpy.size(), 0); QCOMPARE(devPriv->pointById(1)->exclusiveGrabber, mpta); QCOMPARE(devPriv->pointById(2)->exclusiveGrabber, mpta); QCOMPARE(devPriv->pointById(3)->exclusiveGrabber, mpta); @@ -272,13 +250,14 @@ void tst_MptaInterop::touchesThenPinch() if (!dragTookGrab && devPriv->pointById(2)->exclusiveGrabber == drag) dragTookGrab = i; } - qCDebug(lcPointerTests) << "drag started after" << dragTookGrab << "moves; ended with translation" << drag->translation(); - QCOMPARE(devPriv->pointById(1)->exclusiveGrabber, drag); - QTRY_VERIFY(drag->translation().x() > 0); + qCDebug(lcPointerTests) << "drag started after" << dragTookGrab + << "moves; ended with translation" << drag->activeTranslation(); + QCOMPARE(devPriv->pointById(2)->exclusiveGrabber, drag); + QTRY_VERIFY(drag->activeTranslation().x() > 0); touch.release(2, p2).commit(); QQuickTouchUtils::flush(window); - QTRY_COMPARE(mptaReleasedSpy.count(), 1); + QTRY_COMPARE(mptaReleasedSpy.size(), 1); } void tst_MptaInterop::unloadHandlerWithPassiveGrab() diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquickdraghandler/BLACKLIST new file mode 100644 index 0000000000..e555a4bb61 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/BLACKLIST @@ -0,0 +1,9 @@ +# QTBUG-103064 +[touchDragMulti] +android +[touchDrag] +android +[touchDragMultiSliders] +android +[touchPassiveGrabbers] +android diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt index 85c664f8ca..23087c1d48 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from qquickdraghandler.pro. ##################################################################### ## tst_qquickdraghandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickdraghandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qquickdraghandler SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_qquickdraghandler.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_qquickdraghandler qt_internal_extend_target(tst_qquickdraghandler CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_qquickdraghandler CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml index 778a799d70..065b0aaed8 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/DragAnywhereSlider.qml @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.12 +import QtQuick Item { id: root @@ -38,7 +13,7 @@ Item { property alias pressed: tap.pressed signal tapped width: 140 - height: 400 + height: 260 DragHandler { id: dragHandler diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml index 158a02b7a6..9483a12d1c 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/FlashAnimation.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml index 782750b783..200e846207 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/Slider.qml @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.12 +import QtQuick Item { id: root @@ -38,7 +13,7 @@ Item { property alias pressed: tap.pressed signal tapped width: 140 - height: 400 + height: 260 Rectangle { id: slot diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragAndWheel.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragAndWheel.qml new file mode 100644 index 0000000000..811326aaba --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragAndWheel.qml @@ -0,0 +1,32 @@ +import QtQuick 2.15 + +Item { + id: root + + property bool gotWheel: false + property int changeCount: 0 + property alias wheelHandlerEnabled: wheelHandler.enabled + + width: 640 + height: 480 + + Rectangle { + color: "blue" + width: 200 + height: 200 + + DragHandler { + id: dragHandler + } + + WheelHandler { + id: wheelHandler + + enabled: !dragHandler.active + onEnabledChanged: root.changeCount++ + onWheel: root.gotWheel = true + } + + } + +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml new file mode 100644 index 0000000000..b24812c914 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragHandlerUnderModalLayer.qml @@ -0,0 +1,34 @@ +import QtQuick 2.15 + +import Test 1.0 + +Item { + width: 640 + height: 480 + + Rectangle { + anchors.fill: parent + color: "grey" + + Rectangle { + x: 200 + y: 200 + width: 100 + height: 100 + color: "orange" + DragHandler { + grabPermissions: DragHandler.CanTakeOverFromAnything // but not anything with keepMouseGrab! + } + } + } + + ModalLayer { + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "red" + opacity: 0.4 + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml index 81fa20f3bb..47be6052ad 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draggables.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the manual tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.15 @@ -54,7 +29,9 @@ Item { color: "white" anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter - text: ball.objectName + "\n" + dragHandler.centroid.position.x.toFixed(1) + "," + dragHandler.centroid.position.y.toFixed(1) + text: ball.objectName + "\n" + + dragHandler.centroid.position.x.toFixed(1) + "," + dragHandler.centroid.position.y.toFixed(1) + "\n" + + ball.x.toFixed(1) + "," + ball.y.toFixed(1) } } } diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml index 08b85aef50..5d700cdd08 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/draghandler_and_pinchhandler.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the manual tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml index f6042f4461..ea71da5623 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/multipleSliders.qml @@ -1,40 +1,35 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.12 +import QtQuick Rectangle { id: root - width: 900 - height: 850 + width: 600 + height: 540 objectName: "root" color: "#222222" + Instantiator { + model: 3 + // non-interfering, just for visual monitoring of points + delegate: PointHandler { + id: ph + required property int index + objectName: "ph" + index + parent: root + + target: Rectangle { + parent: root + visible: ph.active + x: ph.point.position.x - width / 2 + y: ph.point.position.y - height / 2 + width: 10; height: width; radius: width / 2 + color: Qt.rgba(1, 0.33 * ph.index, 1 - 0.3 * ph.index) + } + } + } + Grid { objectName: "grid" anchors.fill: parent diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml index 6e5574787c..6c7a25c148 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml @@ -1,37 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the manual tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.12 +import QtQuick Rectangle { id: root - width: 900 - height: 850 + width: 320 + height: 240 objectName: "root" color: "#222222" @@ -105,7 +80,5 @@ Rectangle { } } } - - } } diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml index d6eb791700..30b28ac9e8 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 @@ -34,6 +9,26 @@ Item { width: 640 height: 480 + Instantiator { + model: 3 + // non-interfering, just for visual monitoring of points + delegate: PointHandler { + id: ph + required property int index + objectName: "ph" + index + parent: root + + target: Rectangle { + parent: root + visible: ph.active + x: ph.point.position.x - width / 2 + y: ph.point.position.y - height / 2 + width: 10; height: width; radius: width / 2 + color: Qt.rgba(1, 0.33 * ph.index, 1 - 0.3 * ph.index) + } + } + } + Rectangle { id: rect1 objectName: "rect1" diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index c33c70b9e9..15df656b93 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -1,33 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> +#include <QtGui/qstylehints.h> +#include <QtQml/private/qqmlglobal_p.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> #include <QtQuick/private/qquickdraghandler_p.h> @@ -37,8 +14,10 @@ #include <QtQuick/qquickview.h> #include <QtGui/private/qpointingdevice_p.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> + +#include <QtCore/qpointer.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -47,7 +26,8 @@ class tst_DragHandler : public QQmlDataTest Q_OBJECT public: tst_DragHandler() - :touchDevice(QTest::createTouchDevice()) + : QQmlDataTest(QT_QMLTEST_DATADIR) + , touchDevice(QTest::createTouchDevice()) {} private slots: @@ -69,8 +49,12 @@ private slots: void touchPassiveGrabbers_data(); void touchPassiveGrabbers(); void touchPinchAndMouseMove(); + void unsuitableEventDuringDrag(); + void underModalLayer(); + void interruptedByIrrelevantButton(); private: + void sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, QPoint pixelDelta, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, bool inverted); void createView(QScopedPointer<QQuickView> &window, const char *fileName); QSet<QQuickPointerHandler *> passiveGrabbers(QQuickWindow *window, int pointId = 0); QPointingDevice *touchDevice; @@ -81,8 +65,8 @@ void tst_DragHandler::createView(QScopedPointer<QQuickView> &window, const char window.reset(new QQuickView); window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickViewTestUtils::centerOnScreen(window.data()); + QQuickViewTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); @@ -124,7 +108,8 @@ void tst_DragHandler::defaultPropertyValues() QVERIFY(dragHandler); QCOMPARE(dragHandler->acceptedButtons(), Qt::LeftButton); - QCOMPARE(dragHandler->translation(), QVector2D()); + QCOMPARE(dragHandler->persistentTranslation(), QVector2D()); + QCOMPARE(dragHandler->activeTranslation(), QVector2D()); QCOMPARE(dragHandler->centroid().position(), QPointF()); QCOMPARE(dragHandler->centroid().scenePosition(), QPointF()); QCOMPARE(dragHandler->centroid().pressPosition(), QPointF()); @@ -159,8 +144,9 @@ void tst_DragHandler::touchDrag() dragHandler->setDragThreshold(dragThreshold); } - QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy(dragHandler, &QQuickDragHandler::translationChanged); QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); + QSignalSpy xDeltaSpy(dragHandler->xAxis(), &QQuickDragAxis::activeValueChanged); QPointF ballCenter = ball->clipRect().center(); QPointF scenePressPos = ball->mapToScene(ballCenter); @@ -173,22 +159,24 @@ void tst_DragHandler::touchDrag() QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); - QCOMPARE(centroidChangedSpy.count(), 1); + QCOMPARE(centroidChangedSpy.size(), 1); p1 += QPoint(dragThreshold, 0); QTest::touchEvent(window, touchDevice).move(1, p1, window); QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "velocity after drag" << dragHandler->centroid().velocity(); if (dragThreshold > 0) QTRY_VERIFY(!qFuzzyIsNull(dragHandler->centroid().velocity().x())); - QCOMPARE(centroidChangedSpy.count(), 2); + QCOMPARE(centroidChangedSpy.size(), 2); QVERIFY(!dragHandler->active()); p1 += QPoint(1, 0); QTest::touchEvent(window, touchDevice).move(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(dragHandler->active()); - QCOMPARE(translationChangedSpy.count(), 0); - QCOMPARE(centroidChangedSpy.count(), 3); - QCOMPARE(dragHandler->translation().x(), 0.0); + QCOMPARE(translationChangedSpy.size(), 0); + QCOMPARE(xDeltaSpy.size(), 0); + QCOMPARE(centroidChangedSpy.size(), 3); + QCOMPARE(dragHandler->persistentTranslation().x(), 0); + QCOMPARE(dragHandler->activeTranslation().x(), 0); QPointF sceneGrabPos = p1; QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); p1 += QPoint(19, 0); @@ -200,18 +188,79 @@ void tst_DragHandler::touchDrag() QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); - QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); - QCOMPARE(dragHandler->translation().y(), 0.0); + QCOMPARE(dragHandler->persistentTranslation().x(), dragThreshold + 20); + QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold + 20); + QCOMPARE(dragHandler->persistentTranslation().y(), 0); + QCOMPARE(dragHandler->activeTranslation().y(), 0); + QCOMPARE(translationChangedSpy.size(), 1); + QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(dragThreshold + 20, 0)); QVERIFY(dragHandler->centroid().velocity().x() > 0); - QCOMPARE(centroidChangedSpy.count(), 4); + QCOMPARE(centroidChangedSpy.size(), 4); QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!dragHandler->active()); QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); - QCOMPARE(translationChangedSpy.count(), 1); - QCOMPARE(centroidChangedSpy.count(), 5); + QCOMPARE(translationChangedSpy.size(), 1); + QCOMPARE(xDeltaSpy.size(), 1); + QCOMPARE(xDeltaSpy.first().first().toReal(), dragThreshold + 20); + QCOMPARE(centroidChangedSpy.size(), 5); + QCOMPARE(dragHandler->persistentTranslation().x(), dragThreshold + 20); + + // Drag again: activeTranslation starts over, while persistentTranslation accumulates + p1 = ball->mapToScene(ballCenter).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(dragHandler->persistentTranslation().x(), dragThreshold + 20); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + p1 += QPoint(1, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(dragHandler->active()); + p1 += QPoint(9, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(xDeltaSpy.size(), 2); + QCOMPARE(xDeltaSpy.last().first().toReal(), dragThreshold + 10); + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold + 20); + QCOMPARE(dragHandler->persistentTranslation().x(), dragThreshold * 2 + 40); + QCOMPARE(xDeltaSpy.size(), 3); + QCOMPARE(xDeltaSpy.last().first().toReal(), 10); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + + // Call setPersistentTranslation and drag yet again: + // activeTranslation starts over, while persistentTranslation adds the drags onto the new basis + dragHandler->setPersistentTranslation({10, 10}); + p1 = ball->mapToScene(ballCenter).toPoint(); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(dragHandler->persistentTranslation().x(), 10); + p1 += QPoint(dragThreshold, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + p1 += QPoint(1, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(dragHandler->active()); + p1 += QPoint(9, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + p1 += QPoint(10, 0); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold + 20); + QCOMPARE(dragHandler->persistentTranslation().x(), dragThreshold + 30); + QCOMPARE(xDeltaSpy.size(), 6); + QCOMPARE(xDeltaSpy.last().first().toReal(), 10); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); } void tst_DragHandler::mouseDrag_data() @@ -241,8 +290,9 @@ void tst_DragHandler::mouseDrag() QVERIFY(dragHandler); dragHandler->setAcceptedButtons(acceptedButtons); // QTBUG-76875 - QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy(dragHandler, &QQuickDragHandler::translationChanged); QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); + QSignalSpy xDeltaSpy(dragHandler->xAxis(), &QQuickDragAxis::activeValueChanged); QPointF ballCenter = ball->clipRect().center(); QPointF scenePressPos = ball->mapToScene(ballCenter); @@ -258,13 +308,13 @@ void tst_DragHandler::mouseDrag() QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); - QCOMPARE(centroidChangedSpy.count(), 1); + QCOMPARE(centroidChangedSpy.size(), 1); } p1 += QPoint(dragThreshold, 0); QTest::mouseMove(window, p1); if (shouldDrag) { -// QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); // TODO QTBUG-33891 - QCOMPARE(centroidChangedSpy.count(), 2); + QVERIFY(dragHandler->centroid().velocity().x() > 0); + QCOMPARE(centroidChangedSpy.size(), 2); QVERIFY(!dragHandler->active()); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); @@ -276,10 +326,12 @@ void tst_DragHandler::mouseDrag() QTRY_VERIFY(dragHandler->active()); else QVERIFY(!dragHandler->active()); - QCOMPARE(translationChangedSpy.count(), 0); + QCOMPARE(translationChangedSpy.size(), 0); + QCOMPARE(xDeltaSpy.size(), 0); if (shouldDrag) - QCOMPARE(centroidChangedSpy.count(), 3); - QCOMPARE(dragHandler->translation().x(), 0.0); + QCOMPARE(centroidChangedSpy.size(), 3); + QCOMPARE(dragHandler->persistentTranslation().x(), 0.0); + QCOMPARE(dragHandler->activeTranslation().x(), 0.0); QPointF sceneGrabPos = p1; if (shouldDrag) QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); @@ -292,10 +344,12 @@ void tst_DragHandler::mouseDrag() QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); - QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); - QCOMPARE(dragHandler->translation().y(), 0.0); -// QVERIFY(dragHandler->centroid().velocity().x() > 0); // TODO QTBUG-33891 - QCOMPARE(centroidChangedSpy.count(), 4); + QCOMPARE(dragHandler->persistentTranslation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler->persistentTranslation().y(), 0.0); + QCOMPARE(dragHandler->activeTranslation().y(), 0.0); + QVERIFY(dragHandler->centroid().velocity().x() > 0); + QCOMPARE(centroidChangedSpy.size(), 4); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ClosedHandCursor); #endif @@ -303,10 +357,13 @@ void tst_DragHandler::mouseDrag() QTest::mouseRelease(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1); QTRY_VERIFY(!dragHandler->active()); QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); - if (shouldDrag) + QCOMPARE(translationChangedSpy.size(), shouldDrag ? 1 : 0); + if (shouldDrag) { QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); - QCOMPARE(translationChangedSpy.count(), shouldDrag ? 1 : 0); - QCOMPARE(centroidChangedSpy.count(), shouldDrag ? 5 : 0); + QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(dragThreshold + 20, 0)); + } + QCOMPARE(xDeltaSpy.size(), shouldDrag ? 1 : 0); + QCOMPARE(centroidChangedSpy.size(), shouldDrag ? 5 : 0); #if QT_CONFIG(cursor) QTest::mouseMove(window, p1 + QPoint(1, 0)); // TODO after fixing QTBUG-53987, don't send mouseMove QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); @@ -340,8 +397,9 @@ void tst_DragHandler::mouseDragThreshold() dragHandler->setDragThreshold(dragThreshold); } - QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy(dragHandler, &QQuickDragHandler::translationChanged); QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); + QSignalSpy xDeltaSpy(dragHandler->xAxis(), &QQuickDragAxis::activeValueChanged); QPointF ballCenter = ball->clipRect().center(); QPointF scenePressPos = ball->mapToScene(ballCenter); @@ -353,20 +411,28 @@ void tst_DragHandler::mouseDragThreshold() QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); - QCOMPARE(centroidChangedSpy.count(), 1); + QCOMPARE(centroidChangedSpy.size(), 1); p1 += QPoint(qMax(1, dragThreshold), 0); // QTBUG-85431: zero-distance mouse moves are not delivered QTest::mouseMove(window, p1); if (dragThreshold > 0) QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); - QCOMPARE(centroidChangedSpy.count(), 2); + QCOMPARE(centroidChangedSpy.size(), 2); // the handler is not yet active, unless the drag threshold was already exceeded QCOMPARE(dragHandler->active(), dragThreshold == 0); p1 += QPoint(1, 0); QTest::mouseMove(window, p1); QTRY_VERIFY(dragHandler->active()); - QCOMPARE(translationChangedSpy.count(), dragThreshold ? 0 : 1); - QCOMPARE(centroidChangedSpy.count(), 3); + QCOMPARE(translationChangedSpy.size(), dragThreshold ? 0 : 1); + if (!dragThreshold) + QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(2, 0)); + QCOMPARE(xDeltaSpy.size(), dragThreshold ? 0 : 1); + QCOMPARE(centroidChangedSpy.size(), 3); +#if QT_DEPRECATED_SINCE(6, 2) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(dragHandler->translation().x(), dragThreshold ? 0 : 2); +QT_WARNING_POP +#endif + QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold ? 0 : 2); QPointF sceneGrabPos = dragThreshold ? p1 : p1 - QPoint(1, 0); QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); p1 += QPoint(19, 0); @@ -377,16 +443,26 @@ void tst_DragHandler::mouseDragThreshold() QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); +#if QT_DEPRECATED_SINCE(6, 2) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(dragHandler->translation().x(), dragThreshold + (dragThreshold ? 20 : 21)); QCOMPARE(dragHandler->translation().y(), 0.0); +QT_WARNING_POP +#endif + QCOMPARE(dragHandler->activeTranslation().x(), dragThreshold + (dragThreshold ? 20 : 21)); + QCOMPARE(dragHandler->activeTranslation().y(), 0.0); + QCOMPARE(translationChangedSpy.size(), dragThreshold ? 1 : 2); + QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), + QVector2D(dragThreshold ? dragThreshold + 20 : 2, 0)); QVERIFY(dragHandler->centroid().velocity().x() > 0); - QCOMPARE(centroidChangedSpy.count(), 4); + QCOMPARE(centroidChangedSpy.size(), 4); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!dragHandler->active()); QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); - QCOMPARE(translationChangedSpy.count(), dragThreshold ? 1 : 2); - QCOMPARE(centroidChangedSpy.count(), 5); + QCOMPARE(translationChangedSpy.size(), dragThreshold ? 1 : 2); + QCOMPARE(xDeltaSpy.size(), dragThreshold ? 1 : 2); + QCOMPARE(centroidChangedSpy.size(), 5); } void tst_DragHandler::dragFromMargin() // QTBUG-74966 @@ -416,8 +492,14 @@ void tst_DragHandler::dragFromMargin() // QTBUG-74966 QTRY_VERIFY(dragHandler->active()); QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); QCOMPARE(dragHandler->centroid().sceneGrabPosition(), p1); +#if QT_DEPRECATED_SINCE(6, 2) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(dragHandler->translation().x(), 0.0); // hmm that's odd QCOMPARE(dragHandler->translation().y(), 0.0); +QT_WARNING_POP +#endif + QCOMPARE(dragHandler->activeTranslation().x(), 0.0); // hmm that's odd + QCOMPARE(dragHandler->activeTranslation().y(), 0.0); QCOMPARE(draggableItem->position(), originalPos + QPointF(dragThreshold * 2, 0)); #if QT_CONFIG(cursor) // The cursor doesn't change until the next event after the handler becomes active. @@ -516,15 +598,17 @@ void tst_DragHandler::touchDragMulti() QVERIFY(ball1); QQuickDragHandler *dragHandler1 = ball1->findChild<QQuickDragHandler*>(); QVERIFY(dragHandler1); - QSignalSpy translationChangedSpy1(dragHandler1, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy1(dragHandler1, &QQuickDragHandler::translationChanged); QSignalSpy centroidChangedSpy1(dragHandler1, SIGNAL(centroidChanged())); + QSignalSpy xDeltaSpy1(dragHandler1->xAxis(), &QQuickDragAxis::activeValueChanged); QQuickItem *ball2 = window->rootObject()->childItems().at(1); QVERIFY(ball2); QQuickDragHandler *dragHandler2 = ball2->findChild<QQuickDragHandler*>(); QVERIFY(dragHandler2); - QSignalSpy translationChangedSpy2(dragHandler2, SIGNAL(translationChanged())); + QSignalSpy translationChangedSpy2(dragHandler2, &QQuickDragHandler::translationChanged); QSignalSpy centroidChangedSpy2(dragHandler1, SIGNAL(centroidChanged())); + QSignalSpy yDeltaSpy2(dragHandler2->yAxis(), &QQuickDragAxis::activeValueChanged); QPointF ball1Center = ball1->clipRect().center(); QPointF scenePressPos1 = ball1->mapToScene(ball1Center); @@ -534,16 +618,18 @@ void tst_DragHandler::touchDragMulti() QPoint p2 = scenePressPos2.toPoint(); QTest::QTouchEventSequence touchSeq = QTest::touchEvent(window, touchDevice, false); - touchSeq.press(1, p1, window).press(2, p2, window).commit(); + touchSeq.press(1, p1, window).commit(); + QQuickTouchUtils::flush(window); + touchSeq.stationary(1).press(2, p2, window).commit(); QQuickTouchUtils::flush(window); QVERIFY(!dragHandler1->active()); - QCOMPARE(centroidChangedSpy1.count(), 1); + QCOMPARE(centroidChangedSpy1.size(), 2); QCOMPARE(dragHandler1->centroid().position(), ball1Center); QCOMPARE(dragHandler1->centroid().pressPosition(), ball1Center); QCOMPARE(dragHandler1->centroid().scenePosition(), scenePressPos1); QCOMPARE(dragHandler1->centroid().scenePressPosition(), scenePressPos1); QVERIFY(!dragHandler2->active()); - QCOMPARE(centroidChangedSpy2.count(), 1); + QCOMPARE(centroidChangedSpy2.size(), 2); QCOMPARE(dragHandler2->centroid().position(), ball2Center); QCOMPARE(dragHandler2->centroid().pressPosition(), ball2Center); QCOMPARE(dragHandler2->centroid().scenePosition(), scenePressPos2); @@ -553,13 +639,13 @@ void tst_DragHandler::touchDragMulti() touchSeq.move(1, p1, window).move(2, p2, window).commit(); QQuickTouchUtils::flush(window); QVERIFY(!dragHandler1->active()); - QCOMPARE(centroidChangedSpy1.count(), 2); + QCOMPARE(centroidChangedSpy1.size(), 3); QCOMPARE(dragHandler1->centroid().position(), ball1Center + QPointF(dragThreshold, 0)); QCOMPARE(dragHandler1->centroid().pressPosition(), ball1Center); QCOMPARE(dragHandler1->centroid().scenePosition().toPoint(), p1); QCOMPARE(dragHandler1->centroid().scenePressPosition(), scenePressPos1); QVERIFY(!dragHandler2->active()); - QCOMPARE(centroidChangedSpy2.count(), 2); + QCOMPARE(centroidChangedSpy2.size(), 3); QCOMPARE(dragHandler2->centroid().position(), ball2Center + QPointF(0, dragThreshold)); QCOMPARE(dragHandler2->centroid().pressPosition(), ball2Center); QCOMPARE(dragHandler2->centroid().scenePosition().toPoint(), p2); @@ -570,8 +656,14 @@ void tst_DragHandler::touchDragMulti() QQuickTouchUtils::flush(window); QTRY_VERIFY(dragHandler1->active()); QVERIFY(dragHandler2->active()); - QCOMPARE(translationChangedSpy1.count(), 0); + QCOMPARE(translationChangedSpy1.size(), 0); + QCOMPARE(xDeltaSpy1.size(), 0); +#if QT_DEPRECATED_SINCE(6, 2) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(dragHandler1->translation().x(), 0.0); +QT_WARNING_POP +#endif + QCOMPARE(dragHandler1->activeTranslation().x(), 0.0); QPointF sceneGrabPos1 = p1; QPointF sceneGrabPos2 = p2; QCOMPARE(dragHandler1->centroid().sceneGrabPosition(), sceneGrabPos1); @@ -579,8 +671,14 @@ void tst_DragHandler::touchDragMulti() p1 += QPoint(19, 0); p2 += QPoint(0, 19); QVERIFY(dragHandler2->active()); - QCOMPARE(translationChangedSpy2.count(), 0); + QCOMPARE(translationChangedSpy2.size(), 0); + QCOMPARE(yDeltaSpy2.size(), 0); +#if QT_DEPRECATED_SINCE(6, 2) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(dragHandler2->translation().x(), 0.0); +QT_WARNING_POP +#endif + QCOMPARE(dragHandler2->activeTranslation().x(), 0.0); QCOMPARE(dragHandler2->centroid().sceneGrabPosition(), sceneGrabPos2); touchSeq.move(1, p1, window).move(2, p2, window).commit(); QQuickTouchUtils::flush(window); @@ -591,27 +689,47 @@ void tst_DragHandler::touchDragMulti() QCOMPARE(dragHandler1->centroid().scenePosition(), ball1->mapToScene(ball1Center)); QCOMPARE(dragHandler1->centroid().scenePressPosition(), scenePressPos1); QCOMPARE(dragHandler1->centroid().sceneGrabPosition(), sceneGrabPos1); +#if QT_DEPRECATED_SINCE(6, 2) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(dragHandler1->translation().x(), dragThreshold + 20.0); QCOMPARE(dragHandler1->translation().y(), 0.0); +QT_WARNING_POP +#endif + QCOMPARE(dragHandler1->activeTranslation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler1->activeTranslation().y(), 0.0); QCOMPARE(dragHandler2->centroid().position(), ball2Center); QCOMPARE(dragHandler2->centroid().pressPosition(), ball2Center); QCOMPARE(dragHandler2->centroid().scenePosition(), ball2->mapToScene(ball2Center)); QCOMPARE(dragHandler2->centroid().scenePressPosition(), scenePressPos2); QCOMPARE(dragHandler2->centroid().sceneGrabPosition(), sceneGrabPos2); +#if QT_DEPRECATED_SINCE(6, 2) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(dragHandler2->translation().x(), 0.0); QCOMPARE(dragHandler2->translation().y(), dragThreshold + 20.0); +QT_WARNING_POP +#endif + QCOMPARE(dragHandler2->activeTranslation().x(), 0.0); + QCOMPARE(dragHandler2->activeTranslation().y(), dragThreshold + 20.0); + QCOMPARE(xDeltaSpy1.size(), 1); + QCOMPARE(xDeltaSpy1.first().first().toReal(), dragThreshold + 20); + QCOMPARE(yDeltaSpy2.size(), 1); + QCOMPARE(yDeltaSpy2.first().first().toReal(), dragThreshold + 20); + QCOMPARE(translationChangedSpy1.size(), 1); + QCOMPARE(translationChangedSpy1.first().first().value<QVector2D>(), QVector2D(dragThreshold + 20, 0)); + QCOMPARE(translationChangedSpy2.size(), 1); + QCOMPARE(translationChangedSpy2.first().first().value<QVector2D>(), QVector2D(0, dragThreshold + 20)); touchSeq.release(1, p1, window).stationary(2).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(!dragHandler1->active()); QVERIFY(dragHandler2->active()); QCOMPARE(dragHandler1->centroid().pressedButtons(), Qt::NoButton); QCOMPARE(ball1->mapToScene(ball1Center).toPoint(), p1); - QCOMPARE(translationChangedSpy1.count(), 1); + QCOMPARE(translationChangedSpy1.size(), 1); touchSeq.release(2, p2, window).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(!dragHandler2->active()); QCOMPARE(ball2->mapToScene(ball2Center).toPoint(), p2); - QCOMPARE(translationChangedSpy2.count(), 1); + QCOMPARE(translationChangedSpy2.size(), 1); } void tst_DragHandler::touchDragMultiSliders_data() @@ -700,7 +818,7 @@ void tst_DragHandler::touchDragMultiSliders() expectedEndPosition = startPoints[sli].toPoint(); qCDebug(lcPointerTests) << "slider " << knobs[sli]->objectName() << "started @" << startPoints[sli] << "tried to move by" << movements[sli] << "ended up @" << endPosition << "expected" << expectedEndPosition; - QTRY_COMPARE(endPosition, expectedEndPosition); + QCOMPARE(endPosition, expectedEndPosition); } // Release @@ -817,10 +935,198 @@ void tst_DragHandler::touchPinchAndMouseMove() for (int i = 0; i < 10; ++i) { p1 += delta; QTest::mouseMove(window, p1); - QCOMPARE(rectMovedSpy.count(), 0); + QCOMPARE(rectMovedSpy.size(), 0); } } +void tst_DragHandler::unsuitableEventDuringDrag() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "dragAndWheel.qml"); + QQuickView *window = windowPtr.data(); + auto root = window->rootObject(); + QQmlProperty changeCount(root, "changeCount"); + QQmlProperty wheelHandlerEnabled(root, "wheelHandlerEnabled"); + bool ok = false; + QCOMPARE(changeCount.read().toInt(&ok), 0); + QVERIFY(ok); + QCOMPARE(wheelHandlerEnabled.read().toBool(), true); + + QPoint p1(100, 100); + QPoint p2(150, 150); + + QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice); + // When we start dragging... + touch.press(3,p1).commit(); + touch.move(3, p2).commit(); + QQuickTouchUtils::flush(window); + // the DragHandler becomes active + ok = false; + QCOMPARE(changeCount.read().toInt(&ok), 1); + QVERIFY(ok); + QCOMPARE(wheelHandlerEnabled.read().toBool(), false); + + // When a scroll event arrives while we are dragging + sendWheelEvent(*window, p2, QPoint(160, 120), QPoint(-360, 120), Qt::NoModifier, Qt::ScrollBegin, false); + // nothing changes because the DragHandler is still active, and the wheel handler stays disabled + ok = false; + QCOMPARE(changeCount.read().toInt(&ok), 1); + QVERIFY(ok); + QCOMPARE(wheelHandlerEnabled.read().toBool(), false); + + // When we stop dragging... + touch.release(3, p2).commit(); + QQuickTouchUtils::flush(window); + + // the wheel handler becomes active again + ok = false; + QCOMPARE(changeCount.read().toInt(&ok), 2); + QVERIFY(ok); + QCOMPARE(wheelHandlerEnabled.read().toBool(), true); + + // During the whole sequence the wheel handler never got a wheel event + // as it was disabled: + QQmlProperty gotWheel(root, "gotWheel"); + QVERIFY(!gotWheel.read().toBool()); + + // If the WheelHandler is unconditionally enabled... + wheelHandlerEnabled.write(true); + // it receives scroll events during drags. + touch.press(4,p2).commit(); + touch.move(4, p1).commit(); + QQuickTouchUtils::flush(window); + sendWheelEvent(*window, p2, QPoint(160, 120), QPoint(-360, 120), Qt::NoModifier, Qt::ScrollBegin, false); + touch.release(4, p2).commit(); + QQuickTouchUtils::flush(window); + QVERIFY(gotWheel.read().toBool()); +} + +void tst_DragHandler::sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, QPoint pixelDelta, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, bool inverted) +{ + QWheelEvent wheelEvent(pos, window.mapToGlobal(pos), pixelDelta, angleDelta, + Qt::NoButton, modifiers, phase, inverted); + QGuiApplication::sendEvent(&window, &wheelEvent); + qApp->processEvents(); + QQuickTouchUtils::flush(&window); +} + +class ModalLayer : public QQuickItem { +public: + explicit ModalLayer(QQuickItem* parent = nullptr) : QQuickItem(parent) { + this->setAcceptedMouseButtons(Qt::AllButtons); + this->setAcceptTouchEvents(true); + this->setKeepMouseGrab(true); + this->setKeepTouchGrab(true); + } + + bool event(QEvent* event) override { + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseTrackingChange: + case QEvent::MouseButtonDblClick: + case QEvent::Wheel: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchCancel: + case QEvent::TouchEnd: { + qCDebug(lcPointerTests) << "BLOCK!" << event->type(); + return true; + } + default: break; + } + return QQuickItem::event(event); + } +}; + +void tst_DragHandler::underModalLayer() // QTBUG-78258 +{ + qmlRegisterType<ModalLayer>("Test", 1, 0, "ModalLayer"); + + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "dragHandlerUnderModalLayer.qml"); + QQuickView * window = windowPtr.data(); + QPointer<QQuickDragHandler> dragHandler = window->rootObject()->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + + QPoint p1(250, 250); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mouseMove(window, p1); + QVERIFY(!dragHandler->active()); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::mouseMove(window, p1); + QVERIFY(!dragHandler->active()); + QTest::mouseRelease(window, Qt::LeftButton); +} + +void tst_DragHandler::interruptedByIrrelevantButton() // QTBUG-102201 +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("draggables.qml"))); + + QQuickItem *ball = window.rootObject()->childItems().first(); + QVERIFY(ball); + QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + + QCOMPARE(dragHandler->acceptedButtons(), Qt::LeftButton); // the default + + QSignalSpy translationChangedSpy(dragHandler, &QQuickDragHandler::translationChanged); + QSignalSpy cancelSpy(dragHandler, &QQuickDragHandler::canceled); + QSignalSpy activeSpy(dragHandler, &QQuickDragHandler::activeChanged); + QSignalSpy grabSpy(dragHandler, &QQuickDragHandler::grabChanged); + + QPoint p1 = ball->mapToScene(ball->clipRect().center()).toPoint(); + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(grabSpy.size(), 1); // passive grab + p1 += QPoint(dragThreshold + 1, 0); + QTest::mouseMove(&window, p1); + QVERIFY(dragHandler->active()); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(grabSpy.size(), 2); // exclusive grab + QCOMPARE(translationChangedSpy.size(), 0); + p1 += QPoint(1, 0); + QTest::mouseMove(&window, p1); + QCOMPARE(translationChangedSpy.size(), 1); + + // Left button is already held, now press right button too (chording) + QTest::mousePress(&window, Qt::RightButton, Qt::NoModifier, p1); + // DragHandler will ungrab and deactivate, but not cancel + QCOMPARE(dragHandler->active(), false); + QCOMPARE(translationChangedSpy.size(), 1); + QCOMPARE(cancelSpy.size(), 0); + QCOMPARE(activeSpy.size(), 2); + QCOMPARE_GT(grabSpy.size(), 2); // lost grabs + + // Release right button: no change in state + QTest::mouseRelease(&window, Qt::RightButton, Qt::NoModifier, p1); + QCOMPARE(dragHandler->active(), false); + QCOMPARE(translationChangedSpy.size(), 1); + QCOMPARE(cancelSpy.size(), 0); + QCOMPARE(activeSpy.size(), 2); + auto grabChangedCount = grabSpy.size(); + + // But the left button is still held, and it's possible to resume dragging + p1 += QPoint(dragThreshold + 1, 0); + QTest::mouseMove(&window, p1); + QCOMPARE_GT(grabSpy.size(), grabChangedCount); // re-grabbed + p1 += QPoint(1, 0); + QTest::mouseMove(&window, p1); + QVERIFY(dragHandler->active()); + QCOMPARE(activeSpy.size(), 3); + QCOMPARE(translationChangedSpy.size(), 2); + + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!dragHandler->active()); + QCOMPARE(activeSpy.size(), 4); + QCOMPARE(cancelSpy.size(), 0); // none of this caused a canceled() signal +} + QTEST_MAIN(tst_DragHandler) #include "tst_qquickdraghandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST index 9bb35c4770..c0d73ff05f 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/BLACKLIST @@ -1,3 +1,8 @@ [movingItemWithHoverHandler] macos # Can't move cursor (QTBUG-76312) +# QTBUG-103065 +[movingItemWithHoverHandler] +android +[window] +opensuse-leap # QTBUG-122405 diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt index 654bf1c366..a2e7d640d5 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from qquickhoverhandler.pro. ##################################################################### ## tst_qquickhoverhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickhoverhandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qquickhoverhandler SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_qquickhoverhandler.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_qquickhoverhandler qt_internal_extend_target(tst_qquickhoverhandler CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_qquickhoverhandler CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml new file mode 100644 index 0000000000..42b658a4d4 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/changingCursor.qml @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.15 + + +Rectangle { + id: brownRect + objectName: "brownRect" + + width: 400 + height: 400 + + HoverHandler { + id: hh + cursorShape: parent.colorIndex == 0 ? + Qt.CrossCursor : + Qt.OpenHandCursor + } + + property list<color> colors: ["beige", "brown"] + property int colorIndex: 0 + + color: colors[colorIndex] + + Timer { + id: colorTimer + interval: 200 + running: true + repeat: true + + onTriggered: { + parent.colorIndex = (parent.colorIndex + 1) % parent.colors.length; + parent.color = parent.colors[parent.colorIndex]; + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml new file mode 100644 index 0000000000..48e130a35e --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverDeviceCursors.qml @@ -0,0 +1,55 @@ +import QtQuick + +Item { + width: 200; height: 200 + + Rectangle { + width: 100; height: 100 + anchors.centerIn: parent + border.color: "black" + + HoverHandler { + objectName: "stylus" + acceptedDevices: PointerDevice.Stylus + acceptedPointerTypes: PointerDevice.Pen + cursorShape: Qt.CrossCursor + } + + HoverHandler { + objectName: "stylus eraser" + acceptedDevices: PointerDevice.Stylus + acceptedPointerTypes: PointerDevice.Eraser + cursorShape: Qt.PointingHandCursor + } + + HoverHandler { + objectName: "airbrush" + acceptedDevices: PointerDevice.Airbrush + acceptedPointerTypes: PointerDevice.Pen + cursorShape: Qt.BusyCursor + } + + HoverHandler { + objectName: "airbrush eraser" + acceptedDevices: PointerDevice.Airbrush + acceptedPointerTypes: PointerDevice.Eraser + cursorShape: Qt.OpenHandCursor + } + + HoverHandler { + objectName: "mouse" + acceptedDevices: PointerDevice.Mouse + // acceptedPointerTypes can be omitted because Mouse is not ambiguous. + // When a genuine mouse move is sent, there's a conflict, and this one should win. + cursorShape: Qt.IBeamCursor + } + + HoverHandler { + objectName: "conflictingMouse" + acceptedDevices: PointerDevice.Mouse + // acceptedPointerTypes can be omitted because Mouse is not ambiguous. + // When a genuine mouse move is sent, there's a conflict, and this one should lose. + cursorShape: Qt.ClosedHandCursor + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml new file mode 100644 index 0000000000..60dfc53c40 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/hoverHandler.qml @@ -0,0 +1,17 @@ +import QtQuick + +Item { + width: 320 + height: 240 + + Rectangle { + width: 100 + height: 100 + anchors.centerIn: parent + color: hh.hovered ? "lightsteelblue" : "beige" + + HoverHandler { + id: hh + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml index 38a19c57c5..ca30a7fe99 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.15 @@ -34,59 +9,96 @@ Rectangle { height: 480 color: "#444" - Component { - id: buttonsAndStuff - Column { - anchors.fill: parent - anchors.margins: 8 - spacing: 8 - - Rectangle { - objectName: "buttonWithMA" - width: parent.width - height: 30 - color: buttonMA.pressed ? "lightsteelblue" : "#999" - border.color: buttonMA.containsMouse ? "cyan" : "transparent" - - MouseArea { - id: buttonMA - objectName: "buttonMA" - hoverEnabled: true - cursorShape: Qt.UpArrowCursor - anchors.fill: parent - onClicked: console.log("clicked MA") - } + component CheckBox: Row { + id: cbRoot + property bool checked : true + property string label : "CheckBox" + spacing: 4 + Rectangle { + width: 16; height: 16 + // comment out this color change to test whether we rely on "dirty" items to + // trigger QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents() to update hover state + color: cbRoot.checked ? "cadetblue" : "transparent" + border.color: "black" + TapHandler { onTapped: cbRoot.checked = !cbRoot.checked } + } + Text { text: cbRoot.label } + } - Text { - anchors.centerIn: parent - text: "MouseArea" - } + component ButtonsAndStuff: Column { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 + function toggleMAEnabled() { maButtonCB.checked = !maButtonCB.checked } + function toggleMAHover() { maButtonHoverCB.checked = !maButtonHoverCB.checked } + function toggleHHEnabled() { hhButtonHoverCB.checked = !hhButtonHoverCB.checked } + + CheckBox { + id: maButtonCB + label: "enabled" + } + + CheckBox { + id: maButtonHoverCB + label: "hover enabled" + } + + Rectangle { + objectName: "buttonWithMA" + width: parent.width + height: 30 + color: buttonMA.pressed ? "lightsteelblue" : "#999" + border.color: buttonMA.containsMouse ? "cyan" : "transparent" + + MouseArea { + id: buttonMA + objectName: "buttonMA" + enabled: maButtonCB.checked + hoverEnabled: maButtonHoverCB.checked + cursorShape: Qt.UpArrowCursor + anchors.fill: parent } - Rectangle { - objectName: "buttonWithHH" - width: parent.width - height: 30 - color: flash ? "#999" : "white" - border.color: buttonHH.hovered ? "cyan" : "transparent" - property bool flash: true - - HoverHandler { - id: buttonHH - objectName: "buttonHH" - acceptedDevices: PointerDevice.AllDevices - cursorShape: tapHandler.pressed ? Qt.BusyCursor : Qt.PointingHandCursor - } + Text { + anchors.centerIn: parent + text: "MouseArea" + } + } - TapHandler { - id: tapHandler - } + CheckBox { + id: hhButtonHoverCB + label: "hover enabled" + } + + Rectangle { + id: buttonRoot + objectName: "buttonWithHH" + width: parent.width + height: 30 + color: flash ? "#999" : "white" + border.color: buttonHH.hovered ? "cyan" : "transparent" + property bool flash: true + + HoverHandler { + id: buttonHH + objectName: "buttonHH" + acceptedDevices: PointerDevice.AllDevices + enabled: hhButtonHoverCB.checked + cursorShape: tapHandler.pressed ? Qt.BusyCursor : Qt.PointingHandCursor + } - Text { - anchors.centerIn: parent - text: "HoverHandler" + TapHandler { + id: tapHandler + onTapped: { + console.log("buttonRoot tapped") + buttonHH.enabled = !buttonHH.enabled } } + + Text { + anchors.centerIn: parent + text: "HoverHandler" + } } } @@ -134,10 +146,22 @@ Rectangle { cursorShape: Qt.OpenHandCursor } - Loader { - objectName: "topSidebarLoader" - sourceComponent: buttonsAndStuff + ButtonsAndStuff { + id: tbs + objectName: "topSidebarContents" anchors.fill: parent + Shortcut { + sequence: "Ctrl+E" + onActivated: tbs.toggleMAEnabled() + } + Shortcut { + sequence: "Ctrl+M" + onActivated: tbs.toggleMAHover() + } + Shortcut { + sequence: "Ctrl+H" + onActivated: tbs.toggleHHEnabled() + } } } @@ -161,9 +185,8 @@ Rectangle { anchors.fill: parent } - Loader { - objectName: "bottomSidebarLoader" - sourceComponent: buttonsAndStuff + ButtonsAndStuff { + objectName: "bottomSidebarContents" anchors.fill: parent } } diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/nohandler.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/nohandler.qml new file mode 100644 index 0000000000..ed3728e278 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/nohandler.qml @@ -0,0 +1,15 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + width: 320 + height: 240 + visible: true + + Rectangle { + objectName: "childItem" + width: 100 + height: 100 + color: "lightblue" + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/windowCursorShape.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/windowCursorShape.qml new file mode 100644 index 0000000000..b5c1f3d2ef --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/windowCursorShape.qml @@ -0,0 +1,13 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + width: 320 + height: 240 + visible: true + color: hh.hovered ? "lightsteelblue" : "beige" + HoverHandler { + id: hh + cursorShape: Qt.OpenHandCursor + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp index 11a5393390..0569fed472 100644 --- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp @@ -1,36 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> #include <QtQuick/private/qquickhoverhandler_p.h> +#include <QtQuick/private/qquickpointerhandler_p_p.h> #include <QtQuick/private/qquickmousearea_p.h> #include <qpa/qwindowsysteminterface.h> @@ -38,9 +14,10 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> +#include <QQmlComponent> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -54,17 +31,30 @@ class tst_HoverHandler : public QQmlDataTest Q_OBJECT public: tst_HoverHandler() + : QQmlDataTest(QT_QMLTEST_DATADIR) {} private slots: + void hoverHandlerAndUnderlyingHoverHandler_data(); void hoverHandlerAndUnderlyingHoverHandler(); void mouseAreaAndUnderlyingHoverHandler(); void hoverHandlerAndUnderlyingMouseArea(); + void disabledHoverHandlerAndUnderlyingMouseArea(); + void hoverHandlerOnDisabledItem(); void movingItemWithHoverHandler(); void margin(); + void window(); + void deviceCursor_data(); + void deviceCursor(); + void addHandlerFromCpp(); + void ensureHoverHandlerWorksWhenItemHasHoverDisabled(); + void changeCursor(); + void touchDrag(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); + + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); }; void tst_HoverHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) @@ -72,16 +62,26 @@ void tst_HoverHandler::createView(QScopedPointer<QQuickView> &window, const char window.reset(new QQuickView); window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickViewTestUtils::centerOnScreen(window.data()); + QQuickViewTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != nullptr); } +void tst_HoverHandler::hoverHandlerAndUnderlyingHoverHandler_data() +{ + QTest::addColumn<bool>("blocking"); + + QTest::newRow("default: nonblocking") << false; + QTest::newRow("blocking") << true; +} + void tst_HoverHandler::hoverHandlerAndUnderlyingHoverHandler() { + QFETCH(bool, blocking); + QScopedPointer<QQuickView> windowPtr; createView(windowPtr, "lesHoverables.qml"); QQuickView * window = windowPtr.data(); @@ -94,6 +94,9 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingHoverHandler() QQuickHoverHandler *buttonHH = button->findChild<QQuickHoverHandler *>("buttonHH"); QVERIFY(buttonHH); + QCOMPARE(buttonHH->isBlocking(), false); // default property value + buttonHH->setBlocking(blocking); + QPoint buttonCenter(button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); QPoint rightOfButton(button->mapToScene(QPointF(button->width() + 2, button->height() / 2)).toPoint()); QPoint outOfSidebar(topSidebar->mapToScene(QPointF(topSidebar->width() + 2, topSidebar->height() / 2)).toPoint()); @@ -102,45 +105,45 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingHoverHandler() QTest::mouseMove(window, outOfSidebar); QCOMPARE(topSidebarHH->isHovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 0); + QCOMPARE(sidebarHoveredSpy.size(), 0); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 0); + QCOMPARE(buttonHoveredSpy.size(), 0); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); #endif QTest::mouseMove(window, rightOfButton); QCOMPARE(topSidebarHH->isHovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(sidebarHoveredSpy.size(), 1); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 0); + QCOMPARE(buttonHoveredSpy.size(), 0); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::OpenHandCursor); #endif QTest::mouseMove(window, buttonCenter); - QCOMPARE(topSidebarHH->isHovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(topSidebarHH->isHovered(), !blocking); + QCOMPARE(sidebarHoveredSpy.size(), blocking ? 2 : 1); QCOMPARE(buttonHH->isHovered(), true); - QCOMPARE(buttonHoveredSpy.count(), 1); + QCOMPARE(buttonHoveredSpy.size(), 1); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::PointingHandCursor); #endif QTest::mouseMove(window, rightOfButton); QCOMPARE(topSidebarHH->isHovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(sidebarHoveredSpy.size(), blocking ? 3 : 1); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 2); + QCOMPARE(buttonHoveredSpy.size(), 2); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::OpenHandCursor); #endif QTest::mouseMove(window, outOfSidebar); QCOMPARE(topSidebarHH->isHovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 2); + QCOMPARE(sidebarHoveredSpy.size(), blocking ? 4 : 2); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 2); + QCOMPARE(buttonHoveredSpy.size(), 2); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); #endif @@ -158,6 +161,13 @@ void tst_HoverHandler::mouseAreaAndUnderlyingHoverHandler() QQuickHoverHandler *topSidebarHH = topSidebar->findChild<QQuickHoverHandler *>("topSidebarHH"); QVERIFY(topSidebarHH); + // Ensure that we don't get extra hover events delivered on the + // side, since it can affect the number of hover move events we receive below. + QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->frameSynchronousHoverEnabled = false; + // And flush out any mouse events that might be queued up + // in QPA, since QTest::mouseMove() calls processEvents. + qGuiApp->processEvents(); + QPoint buttonCenter(buttonMA->mapToScene(QPointF(buttonMA->width() / 2, buttonMA->height() / 2)).toPoint()); QPoint rightOfButton(buttonMA->mapToScene(QPointF(buttonMA->width() + 2, buttonMA->height() / 2)).toPoint()); QPoint outOfSidebar(topSidebar->mapToScene(QPointF(topSidebar->width() + 2, topSidebar->height() / 2)).toPoint()); @@ -166,45 +176,45 @@ void tst_HoverHandler::mouseAreaAndUnderlyingHoverHandler() QTest::mouseMove(window, outOfSidebar); QCOMPARE(topSidebarHH->isHovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 0); + QCOMPARE(sidebarHoveredSpy.size(), 0); QCOMPARE(buttonMA->hovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 0); + QCOMPARE(buttonHoveredSpy.size(), 0); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); #endif QTest::mouseMove(window, rightOfButton); QCOMPARE(topSidebarHH->isHovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(sidebarHoveredSpy.size(), 1); QCOMPARE(buttonMA->hovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 0); + QCOMPARE(buttonHoveredSpy.size(), 0); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::OpenHandCursor); #endif QTest::mouseMove(window, buttonCenter); QCOMPARE(topSidebarHH->isHovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(sidebarHoveredSpy.size(), 1); QCOMPARE(buttonMA->hovered(), true); - QCOMPARE(buttonHoveredSpy.count(), 1); + QCOMPARE(buttonHoveredSpy.size(), 1); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::UpArrowCursor); #endif QTest::mouseMove(window, rightOfButton); QCOMPARE(topSidebarHH->isHovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(sidebarHoveredSpy.size(), 1); QCOMPARE(buttonMA->hovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 2); + QCOMPARE(buttonHoveredSpy.size(), 2); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::OpenHandCursor); #endif QTest::mouseMove(window, outOfSidebar); QCOMPARE(topSidebarHH->isHovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 2); + QCOMPARE(sidebarHoveredSpy.size(), 2); QCOMPARE(buttonMA->hovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 2); + QCOMPARE(buttonHoveredSpy.size(), 2); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); #endif @@ -232,50 +242,131 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea() QTest::mouseMove(window, outOfSidebar); QCOMPARE(bottomSidebarMA->hovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 0); + QCOMPARE(sidebarHoveredSpy.size(), 0); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 0); + QCOMPARE(buttonHoveredSpy.size(), 0); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); #endif QTest::mouseMove(window, rightOfButton); QCOMPARE(bottomSidebarMA->hovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(sidebarHoveredSpy.size(), 1); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 0); + QCOMPARE(buttonHoveredSpy.size(), 0); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ClosedHandCursor); #endif QTest::mouseMove(window, buttonCenter); QCOMPARE(bottomSidebarMA->hovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 2); + QCOMPARE(sidebarHoveredSpy.size(), 2); QCOMPARE(buttonHH->isHovered(), true); - QCOMPARE(buttonHoveredSpy.count(), 1); + QCOMPARE(buttonHoveredSpy.size(), 1); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::PointingHandCursor); #endif QTest::mouseMove(window, rightOfButton); QCOMPARE(bottomSidebarMA->hovered(), true); - QCOMPARE(sidebarHoveredSpy.count(), 3); + QCOMPARE(sidebarHoveredSpy.size(), 3); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 2); + QCOMPARE(buttonHoveredSpy.size(), 2); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ClosedHandCursor); #endif QTest::mouseMove(window, outOfSidebar); QCOMPARE(bottomSidebarMA->hovered(), false); - QCOMPARE(sidebarHoveredSpy.count(), 4); + QCOMPARE(sidebarHoveredSpy.size(), 4); QCOMPARE(buttonHH->isHovered(), false); - QCOMPARE(buttonHoveredSpy.count(), 2); + QCOMPARE(buttonHoveredSpy.size(), 2); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); #endif } +void tst_HoverHandler::disabledHoverHandlerAndUnderlyingMouseArea() +{ + // Check that if a disabled HoverHandler is installed on an item, it + // will not participate in hover event delivery, and as such, also + // not block propagation to siblings. + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "lesHoverables.qml"); + QQuickView * window = windowPtr.data(); + QQuickItem * bottomSidebar = window->rootObject()->findChild<QQuickItem *>("bottomSidebar"); + QVERIFY(bottomSidebar); + QQuickMouseArea *bottomSidebarMA = bottomSidebar->findChild<QQuickMouseArea *>("bottomSidebarMA"); + QVERIFY(bottomSidebarMA); + QQuickItem * button = bottomSidebar->findChild<QQuickItem *>("buttonWithHH"); + QVERIFY(button); + QQuickHoverHandler *buttonHH = button->findChild<QQuickHoverHandler *>("buttonHH"); + QVERIFY(buttonHH); + + // By disabling the HoverHandler, it should no longer + // block the sibling MouseArea underneath from receiving hover events. + buttonHH->setEnabled(false); + + QPoint buttonCenter(button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); + QPoint rightOfButton(button->mapToScene(QPointF(button->width() + 2, button->height() / 2)).toPoint()); + QPoint outOfSidebar(bottomSidebar->mapToScene(QPointF(bottomSidebar->width() + 2, bottomSidebar->height() / 2)).toPoint()); + QSignalSpy sidebarHoveredSpy(bottomSidebarMA, SIGNAL(hoveredChanged())); + QSignalSpy buttonHoveredSpy(buttonHH, SIGNAL(hoveredChanged())); + + QTest::mouseMove(window, outOfSidebar); + QCOMPARE(bottomSidebarMA->hovered(), false); + QCOMPARE(sidebarHoveredSpy.size(), 0); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.size(), 0); + + QTest::mouseMove(window, buttonCenter); + QCOMPARE(bottomSidebarMA->hovered(), true); + QCOMPARE(sidebarHoveredSpy.size(), 1); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.size(), 0); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(bottomSidebarMA->hovered(), true); + QCOMPARE(sidebarHoveredSpy.size(), 1); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.size(), 0); +} + +void tst_HoverHandler::hoverHandlerOnDisabledItem() +{ + // Check that if HoverHandler on a disabled item will + // continue to receive hover events (QTBUG-30801) + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "lesHoverables.qml"); + QQuickView * window = windowPtr.data(); + QQuickItem * bottomSidebar = window->rootObject()->findChild<QQuickItem *>("bottomSidebar"); + QVERIFY(bottomSidebar); + QQuickItem * button = bottomSidebar->findChild<QQuickItem *>("buttonWithHH"); + QVERIFY(button); + QQuickHoverHandler *buttonHH = button->findChild<QQuickHoverHandler *>("buttonHH"); + QVERIFY(buttonHH); + + // Disable the button/rectangle item. This should not + // block its HoverHandler from being hovered + button->setEnabled(false); + + QPoint buttonCenter(button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); + QPoint rightOfButton(button->mapToScene(QPointF(button->width() + 2, button->height() / 2)).toPoint()); + QSignalSpy buttonHoveredSpy(buttonHH, SIGNAL(hoveredChanged())); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.size(), 0); + + QTest::mouseMove(window, buttonCenter); + QCOMPARE(buttonHH->isHovered(), true); + QCOMPARE(buttonHoveredSpy.size(), 1); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.size(), 2); +} + void tst_HoverHandler::movingItemWithHoverHandler() { if (isPlatformWayland()) @@ -306,7 +397,7 @@ void tst_HoverHandler::movingItemWithHoverHandler() paddle->setX(100); QTRY_COMPARE(paddleHH->isHovered(), false); - paddle->setX(p.x()); + paddle->setX(p.x() - paddle->width() / 2); QTRY_COMPARE(paddleHH->isHovered(), true); paddle->setX(540); @@ -329,21 +420,21 @@ void tst_HoverHandler::margin() // QTBUG-85303 QTest::mouseMove(window, {10, 10}); QCOMPARE(hh->isHovered(), false); - QCOMPARE(hoveredSpy.count(), 0); + QCOMPARE(hoveredSpy.size(), 0); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::ArrowCursor); #endif QTest::mouseMove(window, leftMargin); QCOMPARE(hh->isHovered(), true); - QCOMPARE(hoveredSpy.count(), 1); + QCOMPARE(hoveredSpy.size(), 1); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::OpenHandCursor); #endif QTest::mouseMove(window, itemCenter); QCOMPARE(hh->isHovered(), true); - QCOMPARE(hoveredSpy.count(), 1); + QCOMPARE(hoveredSpy.size(), 1); #if QT_CONFIG(cursor) QCOMPARE(window->cursor().shape(), Qt::OpenHandCursor); #endif @@ -363,6 +454,306 @@ void tst_HoverHandler::margin() // QTBUG-85303 #endif } +void tst_HoverHandler::window() // QTBUG-98717 +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("windowCursorShape.qml")); + QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create())); + QVERIFY(!window.isNull()); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); +#if QT_CONFIG(cursor) + if (isPlatformWayland()) + QSKIP("Wayland: QCursor::setPos() doesn't work."); + auto cursorPos = window->mapToGlobal(QPoint(100, 100)); + qCDebug(lcPointerTests) << "in window @" << window->position() << "setting cursor pos" << cursorPos; + QCursor::setPos(cursorPos); + if (!QTest::qWaitFor([cursorPos]{ return QCursor::pos() == cursorPos; })) + QSKIP("QCursor::setPos() doesn't work (QTBUG-76312)."); + QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor); +#endif +} + +void tst_HoverHandler::deviceCursor_data() +{ + QTest::addColumn<bool>("synthMouseForTabletEvents"); + QTest::addColumn<bool>("earlierTabletBeforeMouse"); + + QTest::newRow("nosynth, tablet wins") << false << false; + QTest::newRow("synth, tablet wins") << true << false; + QTest::newRow("synth, mouse wins") << true << true; +} + +void tst_HoverHandler::deviceCursor() +{ +#if !QT_CONFIG(tabletevent) + QSKIP("This test depends on QTabletEvent delivery."); +#endif + QFETCH(bool, synthMouseForTabletEvents); + QFETCH(bool, earlierTabletBeforeMouse); + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents, synthMouseForTabletEvents); + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("hoverDeviceCursors.qml"))); + // Ensure that we don't get extra hover events delivered on the side + QQuickWindowPrivate::get(&window)->deliveryAgentPrivate()->frameSynchronousHoverEnabled = false; + // And flush out any mouse events that might be queued up in QPA, since QTest::mouseMove() calls processEvents. + qGuiApp->processEvents(); + const QQuickItem *root = window.rootObject(); + QQuickHoverHandler *stylusHandler = root->findChild<QQuickHoverHandler *>("stylus"); + QVERIFY(stylusHandler); + QQuickHoverHandler *eraserHandler = root->findChild<QQuickHoverHandler *>("stylus eraser"); + QVERIFY(eraserHandler); + QQuickHoverHandler *aibrushHandler = root->findChild<QQuickHoverHandler *>("airbrush"); + QVERIFY(aibrushHandler); + QQuickHoverHandler *airbrushEraserHandler = root->findChild<QQuickHoverHandler *>("airbrush eraser"); + QVERIFY(airbrushEraserHandler); + QQuickHoverHandler *mouseHandler = root->findChild<QQuickHoverHandler *>("mouse"); + QVERIFY(mouseHandler); + + QPoint point(100, 100); + + const qint64 stylusId = 1234567890; + QElapsedTimer timer; + timer.start(); + auto testStylusDevice = [&](QInputDevice::DeviceType dt, QPointingDevice::PointerType pt, + Qt::CursorShape expectedCursor, QQuickHoverHandler* expectedActiveHandler) { + // We will follow up with a mouse event afterwards, and we want to simulate that the tablet events occur + // either slightly before (earlierTabletBeforeMouse == true) or some time before. + // It turns out that the first mouse move happens at timestamp 501 (simulated). + const ulong timestamp = (earlierTabletBeforeMouse ? 0 : 400) + timer.elapsed(); + qCDebug(lcPointerTests) << "@" << timestamp << "sending" << dt << pt << "expecting" << expectedCursor << expectedActiveHandler->objectName(); + QWindowSystemInterface::handleTabletEvent(&window, timestamp, point, window.mapToGlobal(point), + int(dt), int(pt), Qt::NoButton, 0, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier); + point += QPoint(1, 0); +#if QT_CONFIG(cursor) + // QQuickItem::setCursor() doesn't get called: we only have HoverHandlers in this test + QCOMPARE(root->cursor().shape(), Qt::ArrowCursor); + QTRY_COMPARE(window.cursor().shape(), expectedCursor); +#endif + QCOMPARE(stylusHandler->isHovered(), stylusHandler == expectedActiveHandler); + QCOMPARE(eraserHandler->isHovered(), eraserHandler == expectedActiveHandler); + QCOMPARE(aibrushHandler->isHovered(), aibrushHandler == expectedActiveHandler); + QCOMPARE(airbrushEraserHandler->isHovered(), airbrushEraserHandler == expectedActiveHandler); + }; + + // simulate move events from various tablet stylus types + testStylusDevice(QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Pen, + Qt::CrossCursor, stylusHandler); + testStylusDevice(QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Eraser, + Qt::PointingHandCursor, eraserHandler); + testStylusDevice(QInputDevice::DeviceType::Airbrush, QPointingDevice::PointerType::Pen, + Qt::BusyCursor, aibrushHandler); + testStylusDevice(QInputDevice::DeviceType::Airbrush, QPointingDevice::PointerType::Eraser, + Qt::OpenHandCursor, airbrushEraserHandler); + + qCDebug(lcPointerTests) << "---- no more tablet events, now we send a mouse move"; + + // move the mouse: the mouse-specific HoverHandler gets to set the cursor only if + // more than kCursorOverrideTimeout ms have elapsed (100ms) + QTest::mouseMove(&window, point, 100); + QTRY_IMPL(mouseHandler->isHovered() == true, 500); + const bool afterTimeout = + QQuickPointerHandlerPrivate::get(airbrushEraserHandler)->lastEventTime + 100 < + QQuickPointerHandlerPrivate::get(mouseHandler)->lastEventTime; + qCDebug(lcPointerTests) << "airbrush handler reacted last time:" << QQuickPointerHandlerPrivate::get(airbrushEraserHandler)->lastEventTime + << "and the mouse handler reacted at time:" << QQuickPointerHandlerPrivate::get(mouseHandler)->lastEventTime + << "so > 100 ms have elapsed?" << afterTimeout; + if (afterTimeout) + QCOMPARE(mouseHandler->isHovered(), true); + else + QSKIP("Failed to delay mouse move 100ms after the previous tablet event"); + +#if QT_CONFIG(cursor) + QCOMPARE(window.cursor().shape(), afterTimeout ? Qt::IBeamCursor : Qt::OpenHandCursor); +#endif + QCOMPARE(stylusHandler->isHovered(), false); + QCOMPARE(eraserHandler->isHovered(), false); + QCOMPARE(aibrushHandler->isHovered(), false); + QCOMPARE(airbrushEraserHandler->isHovered(), true); // there was no fresh QTabletEvent to tell it not to be hovered + + // hover with the stylus again, then move the mouse outside the handlers' parent item + testStylusDevice(QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Pen, + Qt::CrossCursor, stylusHandler); + QTest::mouseMove(&window, QPoint(180, 180)); + // the mouse has left the item: all its HoverHandlers should be unhovered (QTBUG-116505) + QCOMPARE(stylusHandler->isHovered(), false); + QCOMPARE(eraserHandler->isHovered(), false); + QCOMPARE(aibrushHandler->isHovered(), false); + QCOMPARE(airbrushEraserHandler->isHovered(), false); + QCOMPARE(mouseHandler->isHovered(), false); +} + +void tst_HoverHandler::addHandlerFromCpp() +{ + // Check that you can create a hover handler from c++, and add it + // as a child of an existing item. Continue to check that you can + // also change the parent item at runtime. + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("nohandler.qml")); + QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create())); + QVERIFY(!window.isNull()); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickItem *childItem = window->findChild<QQuickItem *>("childItem"); + QVERIFY(childItem); + + // Move mouse outside child + const QPoint outside(200, 200); + const QPoint inside(50, 50); + QTest::mouseMove(window.data(), outside); + + QQuickHoverHandler *handler = new QQuickHoverHandler(childItem); + QSignalSpy spy(handler, &QQuickHoverHandler::hoveredChanged); + + // Move mouse inside child + QTest::mouseMove(window.data(), inside); + QVERIFY(handler->isHovered()); + QCOMPARE(spy.size(), 1); + + // Move mouse outside child + QTest::mouseMove(window.data(), outside); + QVERIFY(!handler->isHovered()); + QCOMPARE(spy.size(), 2); + + // Remove the parent item from the handler + spy.clear(); + handler->setParentItem(nullptr); + + // Move mouse inside child + QTest::mouseMove(window.data(), inside); + QVERIFY(!handler->isHovered()); + QCOMPARE(spy.size(), 0); + + // Move mouse outside child + QTest::mouseMove(window.data(), outside); + QVERIFY(!handler->isHovered()); + QCOMPARE(spy.size(), 0); + + // Reparent back the item to the handler + spy.clear(); + handler->setParentItem(childItem); + + // Move mouse inside child + QTest::mouseMove(window.data(), inside); + QVERIFY(handler->isHovered()); + QCOMPARE(spy.size(), 1); + + // Move mouse outside child + QTest::mouseMove(window.data(), outside); + QVERIFY(!handler->isHovered()); + QCOMPARE(spy.size(), 2); +} + +void tst_HoverHandler::ensureHoverHandlerWorksWhenItemHasHoverDisabled() +{ + // Check that a hover handler with a leaf item as parent, continues to + // receive hover, even if the item itself stops listening for hover. + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("nohandler.qml")); + QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create())); + QVERIFY(!window.isNull()); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickItem *childItem = window->findChild<QQuickItem *>("childItem"); + QVERIFY(childItem); + + // Move mouse outside child + const QPoint outside(200, 200); + const QPoint inside(50, 50); + QTest::mouseMove(window.data(), outside); + + QQuickHoverHandler *handler = new QQuickHoverHandler(childItem); + + // Toggle hover on the item. This should not clear subtreeHoverEnabled + // on the item as a whole, since it still has a hover handler. + childItem->setAcceptHoverEvents(true); + childItem->setAcceptHoverEvents(false); + QSignalSpy spy(handler, &QQuickHoverHandler::hoveredChanged); + + // Move mouse inside child + QTest::mouseMove(window.data(), inside); + QVERIFY(handler->isHovered()); + QCOMPARE(spy.size(), 1); + + // Move mouse outside child + QTest::mouseMove(window.data(), outside); + QVERIFY(!handler->isHovered()); + QCOMPARE(spy.size(), 2); +} + +void tst_HoverHandler::changeCursor() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "changingCursor.qml"); + QQuickView * window = windowPtr.data(); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickItem *item = window->findChild<QQuickItem *>("brownRect"); + QVERIFY(item); + QQuickHoverHandler *hh = item->findChild<QQuickHoverHandler *>(); + QVERIFY(hh); + + QPoint itemCenter(item->mapToScene(QPointF(item->width() / 2, item->height() / 2)).toPoint()); + QSignalSpy hoveredSpy(hh, SIGNAL(hoveredChanged())); + + QTest::mouseMove(window, itemCenter); + + QTRY_COMPARE(hoveredSpy.size(), 1); + +#if QT_CONFIG(cursor) + QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor); + QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor); + QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor); + QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor); +#endif +} + +void tst_HoverHandler::touchDrag() +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("hoverHandler.qml"))); + const QQuickItem *root = window.rootObject(); + QQuickHoverHandler *handler = root->findChild<QQuickHoverHandler *>(); + QVERIFY(handler); + + // polishAndSync() calls flushFrameSynchronousEvents() before emitting afterAnimating() + QSignalSpy frameSyncSpy(&window, &QQuickWindow::afterAnimating); + + const QPoint out(root->width() - 1, root->height() / 2); + QPoint in(root->width() / 2, root->height() / 2); + + QTest::touchEvent(&window, touchscreen.get()).press(0, out, &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(handler->isHovered(), false); + + frameSyncSpy.clear(); + QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(handler->isHovered(), true); + QCOMPARE(handler->point().scenePosition(), in); + + in += {10, 10}; + QTest::touchEvent(&window, touchscreen.get()).move(0, in, &window); + QQuickTouchUtils::flush(&window); + // ensure that the color change is visible + QTRY_COMPARE_GE(frameSyncSpy.size(), 1); + QCOMPARE(handler->isHovered(), true); + QCOMPARE(handler->point().scenePosition(), in); + + QTest::touchEvent(&window, touchscreen.get()).move(0, out, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE_GE(frameSyncSpy.size(), 2); + QCOMPARE(handler->isHovered(), false); + + QTest::touchEvent(&window, touchscreen.get()).release(0, out, &window); +} + QTEST_MAIN(tst_HoverHandler) #include "tst_qquickhoverhandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquickpinchhandler/BLACKLIST new file mode 100644 index 0000000000..701cdae344 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/BLACKLIST @@ -0,0 +1,3 @@ +# QTBUG-103066 +[scaleNativeGesture] +android diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt index 09550b143d..1334607ab2 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from qquickpinchhandler.pro. ##################################################################### ## tst_qquickpinchhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickpinchhandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qquickpinchhandler SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_qquickpinchhandler.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_qquickpinchhandler qt_internal_extend_target(tst_qquickpinchhandler CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_qquickpinchhandler CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml new file mode 100644 index 0000000000..9d9903fc0e --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/nullTarget.qml @@ -0,0 +1,33 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.15 + +Item { + width: 320; height: 320 + property alias pinchScale: pinch.scale + + Rectangle { + objectName: "blackrect" + width: 200; height: 200 + color: "black" + antialiasing: true + scale: pinch.scale + rotation: pinch.rotation + x: pinch.translation.x + y: pinch.translation.y + + PinchHandler { + id: pinch + target: null + minimumScale: 0.5 + maximumScale: 4 + } + + Text { + color: "cyan" + anchors.centerIn: parent + text: "scale " + pinch.scale.toFixed(2) + " activeScale " + pinch.activeScale.toFixed(2) + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml new file mode 100644 index 0000000000..ddd63ec720 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchAndDrag.qml @@ -0,0 +1,37 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Rectangle { + id: whiteRect + property real scale: -1.0 + property int activeCount : 0 + property int deactiveCount : 0 + width: 320; height: 320 + color: "white" + Rectangle { + id: blackRect + objectName: "blackrect" + color: "black" + y: 50 + x: 50 + width: 100 + height: 100 + Text { color: "white"; text: "opacity: " + blackRect.opacity + "\nscale: " + blackRect.scale} + Rectangle { + color: "red" + width: 6; height: 6; radius: 3 + visible: pincharea.active + x: pincharea.centroid.position.x - radius + y: pincharea.centroid.position.y - radius + } + + DragHandler { } + + PinchHandler { + id: pincharea + objectName: "pinchHandler" + } + } + } diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml index 3cabde5f59..2b9b3eb156 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/pinchproperties.qml @@ -1,40 +1,49 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 Rectangle { id: whiteRect - property real scale: -1.0 + property real pinchScale: -1.0 property int activeCount : 0 property int deactiveCount : 0 width: 320; height: 320 color: "white" + + PointHandler { + id: ph1 + acceptedDevices: PointerDevice.TouchScreen + target: Rectangle { + parent: whiteRect + color: "cyan" + x: ph1.point.position.x - 3 + y: ph1.point.position.y - 3 + width: 6; height: 6; radius: 3 + } + } + + PointHandler { + id: ph2 + acceptedDevices: PointerDevice.TouchScreen + target: Rectangle { + parent: whiteRect + color: "lightgreen" + x: ph2.point.position.x - 3 + y: ph2.point.position.y - 3 + width: 6; height: 6; radius: 3 + } + } + + Text { + color: "magenta" + z: 1 + text: "scale: " + blackRect.scale + + "\npos: " + blackRect.x.toFixed(2) + ", " + blackRect.y.toFixed(2) + + "\ntranslation: active " + pincharea.activeTranslation.x.toFixed(2) + ", " + pincharea.activeTranslation.y.toFixed(2) + + "\n persistent " + pincharea.persistentTranslation.x.toFixed(2) + ", " + pincharea.persistentTranslation.y.toFixed(2) + } + Rectangle { id: blackRect objectName: "blackrect" @@ -43,8 +52,7 @@ Rectangle { x: 50 width: 100 height: 100 - opacity: (whiteRect.width-blackRect.x+whiteRect.height-blackRect.y-199)/200 - Text { color: "white"; text: "opacity: " + blackRect.opacity + "\nscale: " + blackRect.scale} + Rectangle { color: "red" width: 6; height: 6; radius: 3 @@ -56,22 +64,30 @@ Rectangle { PinchHandler { id: pincharea objectName: "pinchHandler" - minimumScale: 1.0 + minimumScale: 0.5 maximumScale: 4.0 minimumRotation: 0.0 maximumRotation: 90.0 xAxis.maximum: 140 yAxis.maximum: 170 onActiveChanged: { - whiteRect.scale = pincharea.scale + whiteRect.pinchScale = pincharea.scale if (active) ++activeCount else ++deactiveCount; } onUpdated: { - whiteRect.scale = pincharea.scale + whiteRect.pinchScale = pincharea.scale //whiteRect.pointCount = pincharea.pointCount } - } - } - } + } + } + + Rectangle { + color: "transparent"; border.color: "green" + width: 12; height: 12; radius: 6; border.width: 2 + visible: pincharea.active + x: pincharea.centroid.scenePosition.x - radius + y: pincharea.centroid.scenePosition.y - radius + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml index 4d1a520c01..ed9220f99e 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/threeFingers.qml @@ -1,39 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.12 +import QtQuick Rectangle { id: root - property variant centroid : pinchHandler.centroid - property real scale: pinchHandler.scale - property int pointCount: 0 - property bool pinchActive: pinchHandler.active width: 240; height: 320 Rectangle { @@ -48,6 +19,7 @@ Rectangle { PinchHandler { id: pinchHandler objectName: "pinchHandler" + dragThreshold: 10 minimumScale: 0.5 maximumScale: 2.0 minimumRotation: 0.0 diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml index 46e9ccca87..f25d0e9f38 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/data/transformedPinchHandler.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp index cd24d8643e..78483c5bdb 100644 --- a/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpinchhandler/tst_qquickpinchhandler.cpp @@ -1,55 +1,41 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtTest/QSignalSpy> #include <QtGui/QStyleHints> -#include <QtGui/private/qevent_p.h> +#include <QtGui/private/qeventpoint_p.h> #include <qpa/qwindowsysteminterface.h> #include <QtQuick/private/qquickpinchhandler_p.h> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/qquickview.h> #include <QtQml/qqmlcontext.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") +class PinchHandler : public QQuickPinchHandler { +public: + const QQuickHandlerPoint &firstPoint() { return currentPoints().first(); } + const QQuickHandlerPoint &lastPoint() { return currentPoints().last(); } +}; + class tst_QQuickPinchHandler: public QQmlDataTest { Q_OBJECT public: - tst_QQuickPinchHandler() { } + tst_QQuickPinchHandler() : QQmlDataTest(QT_QMLTEST_DATADIR) { } private slots: void cleanupTestCase(); void pinchProperties(); + void scale_data(); void scale(); void scaleThreeFingers(); + void scaleNativeGesture_data(); + void scaleNativeGesture(); + void cumulativeNativeGestures_data(); + void cumulativeNativeGestures(); void pan(); void dragAxesEnabled_data(); void dragAxesEnabled(); @@ -59,8 +45,8 @@ private slots: void transformedpinchHandler(); private: - QQuickView *createView(); - QPointingDevice *device = QTest::createTouchDevice(); + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); + QScopedPointer<QPointingDevice> touchpad = QScopedPointer<QPointingDevice>(QTest::createTouchDevice(QInputDevice::DeviceType::TouchPad)); }; void tst_QQuickPinchHandler::cleanupTestCase() @@ -75,7 +61,7 @@ static bool withinBounds(qreal lower, qreal num, qreal upper) void tst_QQuickPinchHandler::pinchProperties() { - QScopedPointer<QQuickView> window(createView()); + QScopedPointer<QQuickView> window(QQuickViewTestUtils::createView()); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); QVERIFY(window->rootObject() != nullptr); @@ -91,193 +77,292 @@ void tst_QQuickPinchHandler::pinchProperties() QVERIFY(rootItem != nullptr); QSignalSpy targetSpy(pinchHandler, SIGNAL(targetChanged())); pinchHandler->setTarget(rootItem); - QCOMPARE(targetSpy.count(),1); + QCOMPARE(targetSpy.size(),1); pinchHandler->setTarget(rootItem); - QCOMPARE(targetSpy.count(),1); - - // axis - /* - QCOMPARE(pinchHandler->axis(), QQuickPinch::XAndYAxis); - QSignalSpy axisSpy(pinchHandler, SIGNAL(dragAxisChanged())); - pinchHandler->setAxis(QQuickPinch::XAxis); - QCOMPARE(pinchHandler->axis(), QQuickPinch::XAxis); - QCOMPARE(axisSpy.count(),1); - pinchHandler->setAxis(QQuickPinch::XAxis); - QCOMPARE(axisSpy.count(),1); + QCOMPARE(targetSpy.size(),1); + + // drag axes + QCOMPARE(pinchHandler->xAxis()->enabled(), true); + QCOMPARE(pinchHandler->yAxis()->enabled(), true); + QSignalSpy xEnabledSpy(pinchHandler->xAxis(), &QQuickDragAxis::enabledChanged); + QSignalSpy yEnabledSpy(pinchHandler->yAxis(), &QQuickDragAxis::enabledChanged); + QSignalSpy scaleEnabledSpy(pinchHandler->scaleAxis(), &QQuickDragAxis::enabledChanged); + QSignalSpy rotationEnabledSpy(pinchHandler->rotationAxis(), &QQuickDragAxis::enabledChanged); + pinchHandler->xAxis()->setEnabled(false); + QCOMPARE(xEnabledSpy.count(), 1); + pinchHandler->yAxis()->setEnabled(false); + QCOMPARE(yEnabledSpy.count(), 1); + pinchHandler->scaleAxis()->setEnabled(false); + QCOMPARE(scaleEnabledSpy.count(), 1); + pinchHandler->rotationAxis()->setEnabled(false); + QCOMPARE(rotationEnabledSpy.count(), 1); // minimum and maximum drag properties - QSignalSpy xminSpy(pinchHandler, SIGNAL(minimumXChanged())); - QSignalSpy xmaxSpy(pinchHandler, SIGNAL(maximumXChanged())); - QSignalSpy yminSpy(pinchHandler, SIGNAL(minimumYChanged())); - QSignalSpy ymaxSpy(pinchHandler, SIGNAL(maximumYChanged())); - - QCOMPARE(pinchHandler->xmin(), 0.0); - QCOMPARE(pinchHandler->xmax(), rootItem->width()-blackRect->width()); - QCOMPARE(pinchHandler->ymin(), 0.0); - QCOMPARE(pinchHandler->ymax(), rootItem->height()-blackRect->height()); - - pinchHandler->setXmin(10); - pinchHandler->setXmax(10); - pinchHandler->setYmin(10); - pinchHandler->setYmax(10); - - QCOMPARE(pinchHandler->xmin(), 10.0); - QCOMPARE(pinchHandler->xmax(), 10.0); - QCOMPARE(pinchHandler->ymin(), 10.0); - QCOMPARE(pinchHandler->ymax(), 10.0); + QSignalSpy xminSpy(pinchHandler->xAxis(), &QQuickDragAxis::minimumChanged); + QSignalSpy xmaxSpy(pinchHandler->xAxis(), &QQuickDragAxis::maximumChanged); + QSignalSpy yminSpy(pinchHandler->yAxis(), &QQuickDragAxis::minimumChanged); + QSignalSpy ymaxSpy(pinchHandler->yAxis(), &QQuickDragAxis::maximumChanged); + + QCOMPARE(pinchHandler->xAxis()->minimum(), std::numeric_limits<qreal>::lowest()); + QCOMPARE(pinchHandler->xAxis()->maximum(), 140); + QCOMPARE(pinchHandler->yAxis()->minimum(), std::numeric_limits<qreal>::lowest()); + QCOMPARE(pinchHandler->yAxis()->maximum(), 170); + + pinchHandler->xAxis()->setMinimum(10); + pinchHandler->xAxis()->setMaximum(10); + pinchHandler->yAxis()->setMinimum(10); + pinchHandler->yAxis()->setMaximum(10); + + QCOMPARE(pinchHandler->xAxis()->minimum(), 10); + QCOMPARE(pinchHandler->xAxis()->maximum(), 10); + QCOMPARE(pinchHandler->yAxis()->minimum(), 10); + QCOMPARE(pinchHandler->yAxis()->maximum(), 10); QCOMPARE(xminSpy.count(),1); QCOMPARE(xmaxSpy.count(),1); QCOMPARE(yminSpy.count(),1); QCOMPARE(ymaxSpy.count(),1); - pinchHandler->setXmin(10); - pinchHandler->setXmax(10); - pinchHandler->setYmin(10); - pinchHandler->setYmax(10); + pinchHandler->xAxis()->setMinimum(10); + pinchHandler->xAxis()->setMaximum(10); + pinchHandler->yAxis()->setMinimum(10); + pinchHandler->yAxis()->setMaximum(10); QCOMPARE(xminSpy.count(),1); QCOMPARE(xmaxSpy.count(),1); QCOMPARE(yminSpy.count(),1); QCOMPARE(ymaxSpy.count(),1); - */ // minimum and maximum scale properties - QSignalSpy scaleMinSpy(pinchHandler, SIGNAL(minimumScaleChanged())); - QSignalSpy scaleMaxSpy(pinchHandler, SIGNAL(maximumScaleChanged())); - - QCOMPARE(pinchHandler->minimumScale(), 1.0); - QCOMPARE(pinchHandler->maximumScale(), 4.0); + QSignalSpy scaleAxisMinSpy(pinchHandler->scaleAxis(), &QQuickDragAxis::minimumChanged); + QSignalSpy scaleAxisMaxSpy(pinchHandler->scaleAxis(), &QQuickDragAxis::maximumChanged); - pinchHandler->setMinimumScale(0.5); - pinchHandler->setMaximumScale(1.5); + QCOMPARE(pinchHandler->scaleAxis()->minimum(), 0.5); + QCOMPARE(pinchHandler->scaleAxis()->maximum(), 4); +#if QT_DEPRECATED_SINCE(6, 5) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QCOMPARE(pinchHandler->minimumScale(), 0.5); - QCOMPARE(pinchHandler->maximumScale(), 1.5); - - QCOMPARE(scaleMinSpy.count(),1); - QCOMPARE(scaleMaxSpy.count(),1); + QCOMPARE(pinchHandler->maximumScale(), 4); +QT_WARNING_POP +#endif - pinchHandler->setMinimumScale(0.5); - pinchHandler->setMaximumScale(1.5); + pinchHandler->scaleAxis()->setMinimum(0.25); + pinchHandler->scaleAxis()->setMaximum(1.5); - QCOMPARE(scaleMinSpy.count(),1); - QCOMPARE(scaleMaxSpy.count(),1); + QCOMPARE(pinchHandler->scaleAxis()->minimum(), 0.25); + QCOMPARE(pinchHandler->scaleAxis()->maximum(), 1.5); - // minimum and maximum rotation properties - QSignalSpy rotMinSpy(pinchHandler, SIGNAL(minimumRotationChanged())); - QSignalSpy rotMaxSpy(pinchHandler, SIGNAL(maximumRotationChanged())); - - QCOMPARE(pinchHandler->minimumRotation(), 0.0); - QCOMPARE(pinchHandler->maximumRotation(), 90.0); - - pinchHandler->setMinimumRotation(-90.0); - pinchHandler->setMaximumRotation(45.0); +#if QT_DEPRECATED_SINCE(6, 5) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + QCOMPARE(pinchHandler->minimumScale(), 0.25); + QCOMPARE(pinchHandler->maximumScale(), 1.5); +QT_WARNING_POP +#endif - QCOMPARE(pinchHandler->minimumRotation(), -90.0); - QCOMPARE(pinchHandler->maximumRotation(), 45.0); + QCOMPARE(scaleAxisMinSpy.size(),1); + QCOMPARE(scaleAxisMaxSpy.size(),1); - QCOMPARE(rotMinSpy.count(),1); - QCOMPARE(rotMaxSpy.count(),1); + pinchHandler->scaleAxis()->setMinimum(0.25); + pinchHandler->scaleAxis()->setMaximum(1.5); - pinchHandler->setMinimumRotation(-90.0); - pinchHandler->setMaximumRotation(45.0); + QCOMPARE(scaleAxisMinSpy.size(),1); + QCOMPARE(scaleAxisMaxSpy.size(),1); - QCOMPARE(rotMinSpy.count(),1); - QCOMPARE(rotMaxSpy.count(),1); + // minimum and maximum rotation properties + QSignalSpy rotAxisMinSpy(pinchHandler->rotationAxis(), &QQuickDragAxis::minimumChanged); + QSignalSpy rotAxisMaxSpy(pinchHandler->rotationAxis(), &QQuickDragAxis::maximumChanged); + +#if QT_DEPRECATED_SINCE(6, 5) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + QCOMPARE(pinchHandler->minimumRotation(), 0); + QCOMPARE(pinchHandler->maximumRotation(), 90); +QT_WARNING_POP +#endif + QCOMPARE(pinchHandler->rotationAxis()->minimum(), 0); + QCOMPARE(pinchHandler->rotationAxis()->maximum(), 90); + + pinchHandler->rotationAxis()->setMinimum(-90); + pinchHandler->rotationAxis()->setMaximum(45); + +#if QT_DEPRECATED_SINCE(6, 5) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + QCOMPARE(pinchHandler->minimumRotation(), -90); + QCOMPARE(pinchHandler->maximumRotation(), 45); +QT_WARNING_POP +#endif + + QCOMPARE(rotAxisMinSpy.size(),1); + QCOMPARE(rotAxisMaxSpy.size(),1); + + pinchHandler->rotationAxis()->setMinimum(-90); + pinchHandler->rotationAxis()->setMaximum(45); + + QCOMPARE(rotAxisMinSpy.size(),1); + QCOMPARE(rotAxisMaxSpy.size(),1); } -QMutableEventPoint makeTouchPoint(int id, QPoint p, QQuickView *v, QQuickItem *i) +QEventPoint makeTouchPoint(int id, QPoint p, QQuickView *v, QQuickItem *i) { - QMutableEventPoint touchPoint(id); - touchPoint.setPosition(i->mapFromScene(p)); - touchPoint.setGlobalPosition(v->mapToGlobal(p)); - touchPoint.setScenePosition(p); + QEventPoint touchPoint(id); + QMutableEventPoint::setPosition(touchPoint, i->mapFromScene(p)); + QMutableEventPoint::setGlobalPosition(touchPoint, v->mapToGlobal(p)); + QMutableEventPoint::setScenePosition(touchPoint, p); return touchPoint; } -void tst_QQuickPinchHandler::scale() +void tst_QQuickPinchHandler::scale_data() { - QQuickView *window = createView(); - QScopedPointer<QQuickView> scope(window); - window->setSource(testFileUrl("pinchproperties.qml")); - window->show(); - QVERIFY(QTest::qWaitForWindowExposed(window)); - QVERIFY(window->rootObject() != nullptr); - qApp->processEvents(); + QTest::addColumn<QUrl>("qmlfile"); + QTest::addColumn<bool>("hasTarget"); + QTest::newRow("targetModifying") << testFileUrl("pinchproperties.qml") << true; + QTest::newRow("nullTarget") << testFileUrl("nullTarget.qml") << false; +} - QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler"); - QVERIFY(pinchHandler != nullptr); +void tst_QQuickPinchHandler::scale() +{ + QFETCH(QUrl, qmlfile); + QFETCH(bool, hasTarget); - QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject()); + QQuickView window; + QVERIFY(QQuickTest::showView(window, qmlfile)); + QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject()); QVERIFY(root != nullptr); - - // target - QQuickItem *blackRect = window->rootObject()->findChild<QQuickItem*>("blackrect"); + auto *pinchHandler = static_cast<PinchHandler *>(root->findChild<QQuickPinchHandler*>()); + QVERIFY(pinchHandler != nullptr); + QQuickItem *blackRect = (hasTarget ? pinchHandler->target() : pinchHandler->parentItem()); QVERIFY(blackRect != nullptr); + QSignalSpy grabChangedSpy(pinchHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint))); + QSignalSpy scaleChangedSpy(pinchHandler, &QQuickPinchHandler::scaleChanged); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); QPoint p0(80, 80); QPoint p1(100, 100); - { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); - pinchSequence.press(0, p0, window).commit(); - QQuickTouchUtils::flush(window); - // In order for the stationary point to remember its previous position, - // we have to reuse the same pinchSequence object. Otherwise if we let it - // be destroyed and then start a new sequence, point 0 will default to being - // stationary at 0, 0, and pinchHandler will filter out that touchpoint because - // it is outside its bounds. - pinchSequence.stationary(0).press(1, p1, window).commit(); - QQuickTouchUtils::flush(window); - - QPoint pd(10, 10); - // move one point until PinchHandler activates - for (int pi = 0; pi < 10 && !pinchHandler->active(); ++pi) { - p1 += pd; - pinchSequence.stationary(0).move(1, p1, window).commit(); - QQuickTouchUtils::flush(window); - } - QCOMPARE(pinchHandler->active(), true); - QLineF line(p0, p1); - const qreal startLength = line.length(); - - p1+=pd; - pinchSequence.stationary(0).move(1, p1, window).commit(); - QQuickTouchUtils::flush(window); - line.setP2(p1); - qreal scale = line.length() / startLength; - QVERIFY(qFloatDistance(root->property("scale").toReal(), scale) < 10); - QVERIFY(qFloatDistance(blackRect->scale(), scale) < 10); - - p1+=pd; - pinchSequence.stationary(0).move(1, p1, window).commit(); - QQuickTouchUtils::flush(window); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(&window, touchscreen.get()); + pinchSequence.press(0, p0, &window).commit(); + QQuickTouchUtils::flush(&window); + // In order for the stationary point to remember its previous position, + // we have to reuse the same pinchSequence object. Otherwise if we let it + // be destroyed and then start a new sequence, point 0 will default to being + // stationary at 0, 0, and pinchHandler will filter out that touchpoint because + // it is outside its bounds. + pinchSequence.stationary(0).press(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(grabChangedSpy.size(), 1); // passive grab + + QPoint pd(10, 10); + // move one point until PinchHandler activates + for (int pi = 0; pi < 10 && !pinchHandler->active(); ++pi) { + p1 += pd; + pinchSequence.stationary(0).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + } + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(pinchHandler->active(), true); + // grabs occur when the handler becomes active; at that time, QQuickHandlerPoint.sceneGrabPosition should be correct + QVERIFY(pinchHandler->firstPoint().sceneGrabPosition() != QPointF()); + QVERIFY(pinchHandler->lastPoint().sceneGrabPosition() != QPointF()); + QCOMPARE(pinchHandler->firstPoint().sceneGrabPosition(), pinchHandler->firstPoint().scenePosition()); + QCOMPARE(pinchHandler->lastPoint().sceneGrabPosition(), pinchHandler->lastPoint().scenePosition()); + // first point got a passive grab; both points got exclusive grabs + QCOMPARE(grabChangedSpy.size(), 3); + QLineF line(p0, p1); + const qreal startLength = line.length(); + + // move the same point even further and observe the change in scale + for (int i = 0; i < 2; ++i) { + qreal lastScale = pinchHandler->activeScale(); + p1 += pd; + pinchSequence.stationary(0).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); line.setP2(p1); - scale = line.length() / startLength; - - QVERIFY(qFloatDistance(root->property("scale").toReal(), scale) < 10); - QVERIFY(qFloatDistance(blackRect->scale(), scale) < 10); - - QPointF expectedCentroid = p0 + (p1 - p0)/2; + qreal expectedScale = line.length() / startLength; + qCDebug(lcPointerTests) << "pinchScale" << root->property("pinchScale").toReal() + << "expected" << expectedScale << "; target scale" << blackRect->scale() + << "increments" << scaleChangedSpy.size() + << "multiplier" << scaleChangedSpy.last().first().toReal(); + QVERIFY(qFloatDistance(root->property("pinchScale").toReal(), expectedScale) < 10); + QVERIFY(qFloatDistance(blackRect->scale(), expectedScale) < 10); + QCOMPARE(pinchHandler->persistentScale(), root->property("pinchScale").toReal()); + QCOMPARE(pinchHandler->persistentScale(), pinchHandler->activeScale()); // in sync for the first gesture + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), pinchHandler->activeScale()); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), pinchHandler->activeScale()); + const qreal expectedIncrement = pinchHandler->activeScale() / lastScale; + QCOMPARE(scaleChangedSpy.size(), i + 1); + QCOMPARE(scaleChangedSpy.last().first().toReal(), expectedIncrement); + QPointF expectedCentroid = p0 + (p1 - p0) / 2; QCOMPARE(pinchHandler->centroid().scenePosition(), expectedCentroid); } - // scale beyond bound - p1 += QPoint(20, 20); - { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); - pinchSequence.stationary(0).move(1, p1, window).commit(); - QQuickTouchUtils::flush(window); - QCOMPARE(blackRect->scale(), qreal(4)); // qquickpinchhandler does not manipulate scale property - pinchSequence.release(0, p0, window).release(1, p1, window).commit(); - QQuickTouchUtils::flush(window); + qreal lastScale = pinchHandler->persistentScale(); + pinchSequence.release(0, p0, &window).release(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + // scale property is persistent after release + QCOMPARE(pinchHandler->persistentScale(), lastScale); + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), lastScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1); + + // pinch a second time: scale picks up where we left off + p0 = QPoint(80, 80); + p1 = QPoint(100, 100); + pinchSequence.press(0, p0, &window).press(1, p1, &window).commit(); + // move one point until PinchHandler activates + for (int pi = 0; pi < 10 && !pinchHandler->active(); ++pi) { + p1 += pd; + pinchSequence.stationary(0).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); } + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(pinchHandler->active(), true); + QCOMPARE(pinchHandler->persistentScale(), lastScale); // just activated, not scaling further yet + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), lastScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1); + for (int i = 0; i < 2; ++i) { + lastScale = pinchHandler->persistentScale(); + p1 += pd; + pinchSequence.stationary(0).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE_GT(pinchHandler->persistentScale(), lastScale); + line.setP2(p1); + qreal expectedActiveScale = line.length() / startLength; + qCDebug(lcPointerTests) << i << "activeScale" << pinchHandler->activeScale() + << "expected" << expectedActiveScale << "; scale" << pinchHandler->persistentScale() + << "increments" << scaleChangedSpy.size() + << "multiplier" << scaleChangedSpy.last().first().toReal(); + QVERIFY(qFloatDistance(pinchHandler->activeScale(), expectedActiveScale) < 10); + QCOMPARE(pinchHandler->persistentScale(), root->property("pinchScale").toReal()); + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), root->property("pinchScale").toReal()); + QCOMPARE_NE(pinchHandler->persistentScale(), pinchHandler->activeScale()); // not in sync anymore + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), pinchHandler->activeScale()); + const qreal expectedIncrement = pinchHandler->persistentScale() / lastScale; + QCOMPARE(scaleChangedSpy.size(), i + 3); + QCOMPARE(scaleChangedSpy.last().first().toReal(), expectedIncrement); + } + + // scale beyond maximumScale + lastScale = pinchHandler->activeScale(); + p1 = QPoint(310, 310); + pinchSequence.stationary(0).move(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(blackRect->scale(), qreal(4)); + QCOMPARE(pinchHandler->persistentScale(), qreal(4)); // limited by maximumScale + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), 4); + const qreal expectedIncrement = pinchHandler->activeScale() / lastScale; + QCOMPARE(scaleChangedSpy.size(), 5); + QCOMPARE(scaleChangedSpy.last().first().toReal(), expectedIncrement); + pinchSequence.release(0, p0, &window).release(1, p1, &window).commit(); + QQuickTouchUtils::flush(&window); QCOMPARE(pinchHandler->active(), false); } void tst_QQuickPinchHandler::scaleThreeFingers() { - QQuickView *window = createView(); + QQuickView *window = QQuickViewTestUtils::createView(); QScopedPointer<QQuickView> scope(window); window->setSource(testFileUrl("threeFingers.qml")); window->show(); @@ -300,7 +385,7 @@ void tst_QQuickPinchHandler::scaleThreeFingers() QPoint p1(220, 80); QPoint p2(150, 220); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -322,9 +407,17 @@ void tst_QQuickPinchHandler::scaleThreeFingers() QCOMPARE(pinchHandler->active(), true); // scale we got was 1.1729088738267854364, but keep some slack - QVERIFY(withinBounds(1.163, root->property("scale").toReal(), 1.183)); + qCDebug(lcPointerTests) << "pinch scale" << pinchHandler->persistentScale() << "expected 1.173"; + QVERIFY(withinBounds(1.163, pinchHandler->persistentScale(), 1.183)); // should not rotate - QCOMPARE(root->property("rotation").toReal(), 0.); + QCOMPARE(root->rotation(), 0); + // rotation should be 0, but could be something tiny + qCDebug(lcPointerTests) << "pinch scale expected zero:" << pinchHandler->activeRotation() + << pinchHandler->rotationAxis()->activeValue() + << pinchHandler->rotationAxis()->persistentValue(); + QCOMPARE_LE(qAbs(pinchHandler->activeRotation()), 0.001); + QCOMPARE(pinchHandler->rotationAxis()->activeValue(), pinchHandler->activeRotation()); + QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), 0); for (int i = 0; i < 5;++i) { p0 += QPoint(-4, -4); @@ -334,10 +427,10 @@ void tst_QQuickPinchHandler::scaleThreeFingers() QQuickTouchUtils::flush(window); } // scale we got was 1.4613, but keep some slack - QVERIFY(withinBounds(1.361, root->property("scale").toReal(), 1.561)); + QVERIFY(withinBounds(1.361, pinchHandler->persistentScale(), 1.561)); // since points were moved symetrically around the y axis, centroid should remain at x:150 - QCOMPARE(root->property("centroid").value<QQuickHandlerPoint>().scenePosition().x(), 150); // blackrect is at 50,50 + QCOMPARE(pinchHandler->centroid().scenePosition().x(), 150); // blackrect is at 50,50 // scale beyond bound, we should reach the maximumScale p0 += QPoint(-40, -40); @@ -346,16 +439,243 @@ void tst_QQuickPinchHandler::scaleThreeFingers() pinchSequence.move(0, p0,window).move(1, p1,window).move(2, p2,window).commit(); QQuickTouchUtils::flush(window); - QCOMPARE(root->property("scale").toReal(), 2.); + QCOMPARE(pinchHandler->persistentScale(), 2); + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), 2); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 2); pinchSequence.release(0, p0, window).release(1, p1, window).release(2, p2, window).commit(); QQuickTouchUtils::flush(window); } QCOMPARE(pinchHandler->active(), false); } +void tst_QQuickPinchHandler::scaleNativeGesture_data() +{ + QTest::addColumn<QString>("qmlfile"); + QTest::addColumn<qreal>("scale"); + + QTest::newRow("just pinch") << "pinchproperties.qml" << 1.1; + QTest::newRow("pinch & drag") << "pinchAndDrag.qml" << 1.1; + QTest::newRow("bigger than limit") << "pinchproperties.qml" << 5.0; + QTest::newRow("smaller than limit") << "pinchproperties.qml" << 0.25; +} + +void tst_QQuickPinchHandler::scaleNativeGesture() +{ + QFETCH(QString, qmlfile); + QFETCH(qreal, scale); + + QQuickView *window = QQuickViewTestUtils::createView(); + QScopedPointer<QQuickView> scope(window); + window->setSource(testFileUrl(qmlfile)); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(window->rootObject() != nullptr); + qApp->processEvents(); + + QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject()); + QVERIFY(root != nullptr); + QQuickItem *target = window->rootObject()->findChild<QQuickItem*>("blackrect"); + QVERIFY(target != nullptr); + + QPointF targetPos = target->position(); + ulong ts = 1; + + // first pinch: scale it + const qreal expectedScale = qBound(qreal(0.5), scale, qreal(4)); + QPointF pinchPos(75, 75); + QPointF pinchLocalPos = target->mapFromScene(pinchPos); + // target position is adjusted in QQuickItemPrivate::adjustedPosForTransform() + // so as to compensate for the change in size, to hold the centroid in place + const QPointF expectedPos = targetPos + QPointF( (pinchPos.x() - target->x()) * (expectedScale - 1), + (pinchPos.y() - target->y()) * (expectedScale - 1) ); + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), + Qt::BeginNativeGesture, pinchPos, pinchPos); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad.get(), + Qt::ZoomNativeGesture, scale - 1, pinchPos, pinchPos); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QTRY_COMPARE(target->scale(), expectedScale); + QCOMPARE(pinchHandler->active(), true); + qCDebug(lcPointerTests) << "centroid: local" << pinchHandler->centroid().position() + << "scene" << pinchHandler->centroid().scenePosition(); + QCOMPARE(pinchHandler->centroid().position().toPoint(), pinchLocalPos.toPoint()); + QCOMPARE(pinchHandler->centroid().scenePosition().toPoint(), pinchPos.toPoint()); + QVERIFY(qAbs(target->position().x() - expectedPos.x()) < 0.001); + QVERIFY(qAbs(target->position().y() - expectedPos.y()) < 0.001); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), scale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), scale); + QCOMPARE(pinchHandler->activeTranslation(), QPointF()); + QCOMPARE(pinchHandler->activeRotation(), 0); + QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), 0); + QCOMPARE(pinchHandler->rotationAxis()->activeValue(), 0); + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), + Qt::EndNativeGesture, pinchPos, pinchPos); + QTRY_COMPARE(pinchHandler->active(), false); + QCOMPARE(target->scale(), expectedScale); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), 1); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1); + QCOMPARE(pinchHandler->activeTranslation(), QPointF()); + QCOMPARE(pinchHandler->activeRotation(), 0); + QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), 0); + QCOMPARE(pinchHandler->rotationAxis()->activeValue(), 0); + + // second pinch at a different position: scale it back to original size again + // but remove the limits first, so that we can scale arbitrarily + pinchHandler->scaleAxis()->setMaximum(qInf()); + pinchHandler->scaleAxis()->setMinimum(-qInf()); + const qreal reverseScale = (1 / expectedScale); + pinchPos = QPointF(110, 110); + pinchLocalPos = target->mapFromScene(pinchPos); + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), + Qt::BeginNativeGesture, pinchPos, pinchPos); + QWindowSystemInterface::handleGestureEventWithRealValue(window, ts++, touchpad.get(), + Qt::ZoomNativeGesture, reverseScale - 1, pinchPos, pinchPos); + QTRY_COMPARE(target->scale(), 1); + QCOMPARE(pinchHandler->active(), true); + qCDebug(lcPointerTests) << "centroid: local" << pinchHandler->centroid().position() + << "scene" << pinchHandler->centroid().scenePosition(); + QCOMPARE(pinchHandler->centroid().position().toPoint(), pinchLocalPos.toPoint()); + QCOMPARE(pinchHandler->centroid().scenePosition().toPoint(), pinchPos.toPoint()); + QCOMPARE(pinchHandler->persistentScale(), 1); + QCOMPARE(pinchHandler->activeScale(), reverseScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), reverseScale); + QWindowSystemInterface::handleGestureEvent(window, ts++, touchpad.get(), + Qt::EndNativeGesture, pinchPos, pinchPos); + QTRY_COMPARE(pinchHandler->active(), false); + QCOMPARE(target->scale(), 1); + QCOMPARE(pinchHandler->persistentScale(), 1); + QCOMPARE(pinchHandler->activeScale(), 1); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1); +} + +void tst_QQuickPinchHandler::cumulativeNativeGestures_data() +{ + QTest::addColumn<const QPointingDevice*>("device"); + QTest::addColumn<Qt::NativeGestureType>("gesture"); + QTest::addColumn<qreal>("value"); + QTest::addColumn<QList<QPoint>>("expectedTargetTranslations"); + + const auto *touchpadDevice = touchpad.get(); + const auto *mouse = QPointingDevice::primaryPointingDevice(); + + QTest::newRow("touchpad: rotate") << touchpadDevice << Qt::RotateNativeGesture << 5.0 + << QList<QPoint>{{-2, 2}, {-5, 4}, {-7, 6}, {-10, 7}}; + QTest::newRow("touchpad: scale") << touchpadDevice << Qt::ZoomNativeGesture << 0.1 + << QList<QPoint>{{3, 3}, {5, 5}, {8, 8}, {12, 12}}; + if (mouse->type() == QInputDevice::DeviceType::Mouse) { + QTest::newRow("mouse: rotate") << mouse << Qt::RotateNativeGesture << 5.0 + << QList<QPoint>{{-2, 2}, {-5, 4}, {-7, 6}, {-10, 7}}; + QTest::newRow("mouse: scale") << mouse << Qt::ZoomNativeGesture << 0.1 + << QList<QPoint>{{3, 3}, {5, 5}, {8, 8}, {12, 12}}; + } else { + qCWarning(lcPointerTests) << "skipping mouse tests: primary device is not a mouse" << mouse; + } +} + +void tst_QQuickPinchHandler::cumulativeNativeGestures() +{ + QFETCH(const QPointingDevice*, device); + QFETCH(Qt::NativeGestureType, gesture); + QFETCH(qreal, value); + QFETCH(QList<QPoint>, expectedTargetTranslations); + + QCOMPARE(expectedTargetTranslations.size(), 4); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pinchproperties.qml"))); + QVERIFY(window.rootObject() != nullptr); + qApp->processEvents(); + + QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject()); + QVERIFY(root != nullptr); + QQuickPinchHandler *pinchHandler = root->findChild<QQuickPinchHandler*>("pinchHandler"); + QVERIFY(pinchHandler != nullptr); + QQuickItem *target = root->findChild<QQuickItem*>("blackrect"); + QVERIFY(target != nullptr); + QCOMPARE(pinchHandler->target(), target); + + ulong ts = 1; + qreal expectedScale = 1; + qreal expectedRotation = 0; + QPointF pinchPos(75, 75); + const QPointF initialTargetPos(target->position()); + QWindowSystemInterface::handleGestureEvent(&window, ts++, device, + Qt::BeginNativeGesture, pinchPos, pinchPos); + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + for (int i = 1; i <= 4; ++i) { + QWindowSystemInterface::handleGestureEventWithRealValue(&window, ts++, device, + gesture, value, pinchPos, pinchPos); + qApp->processEvents(); + switch (gesture) { + case Qt::ZoomNativeGesture: + expectedScale = qBound(qreal(0.5), qPow(1 + value, i), qreal(4)); + break; + case Qt::RotateNativeGesture: + expectedRotation = qBound(qreal(0), value * i, qreal(90)); + break; + default: + break; // PinchHandler doesn't react to the others + } + + qCDebug(lcPointerTests) << i << gesture << "with value" << value + << ": scale" << target->scale() << "expected" << expectedScale + << ": rotation" << target->rotation() << "expected" << expectedRotation; + if (lcPointerTests().isDebugEnabled()) QTest::qWait(500); + QCOMPARE(target->scale(), expectedScale); + QCOMPARE(target->rotation(), expectedRotation); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), expectedScale); + QCOMPARE(pinchHandler->persistentRotation(), expectedRotation); + QCOMPARE(pinchHandler->activeRotation(), expectedRotation); + QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), expectedRotation); + QCOMPARE(pinchHandler->rotationAxis()->activeValue(), expectedRotation); + // The target gets transformed around the gesture position, for which + // QQuickItemPrivate::adjustedPosForTransform() computes its new position to compensate. + QPointF delta = target->position() - initialTargetPos; + qCDebug(lcPointerTests) << "target moved by" << delta << "to" << target->position() + << "active trans" << pinchHandler->activeTranslation() + << "perst trans" << pinchHandler->persistentTranslation(); + QCOMPARE_NE(target->position(), initialTargetPos); + QCOMPARE(delta.toPoint(), expectedTargetTranslations.at(i - 1)); + // The native pinch gesture cannot include a translation component (and + // the cursor doesn't move while you are performing the gesture on a touchpad). + QCOMPARE(pinchHandler->activeTranslation(), QPointF()); + // The target only moves to compensate for scale and rotation changes, and that's + // not reflected in PinchHandler.persistentTranslation. + QCOMPARE(pinchHandler->persistentTranslation(), QPointF()); + } + QCOMPARE(pinchHandler->active(), true); + qCDebug(lcPointerTests) << "centroid: local" << pinchHandler->centroid().position() + << "scene" << pinchHandler->centroid().scenePosition(); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), expectedScale); + QWindowSystemInterface::handleGestureEvent(&window, ts++, device, + Qt::EndNativeGesture, pinchPos, pinchPos); + QTRY_COMPARE(pinchHandler->active(), false); + QCOMPARE(target->scale(), expectedScale); + QCOMPARE(target->rotation(), expectedRotation); + QCOMPARE(pinchHandler->persistentScale(), expectedScale); + QCOMPARE(pinchHandler->activeScale(), 1); + QCOMPARE(pinchHandler->scaleAxis()->persistentValue(), expectedScale); + QCOMPARE(pinchHandler->scaleAxis()->activeValue(), 1); + QCOMPARE(pinchHandler->persistentRotation(), expectedRotation); + QCOMPARE(pinchHandler->activeRotation(), 0); + QCOMPARE(pinchHandler->rotationAxis()->persistentValue(), expectedRotation); + QCOMPARE(pinchHandler->rotationAxis()->activeValue(), 0); + QCOMPARE(pinchHandler->activeTranslation(), QPointF()); + QCOMPARE(pinchHandler->persistentTranslation(), QPointF()); +} + void tst_QQuickPinchHandler::pan() { - QQuickView *window = createView(); + QQuickView *window = QQuickViewTestUtils::createView(); QScopedPointer<QQuickView> scope(window); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); @@ -365,6 +685,7 @@ void tst_QQuickPinchHandler::pan() QQuickPinchHandler *pinchHandler = window->rootObject()->findChild<QQuickPinchHandler*>("pinchHandler"); QVERIFY(pinchHandler != nullptr); + QSignalSpy translationChangedSpy(pinchHandler, &QQuickPinchHandler::translationChanged); QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject()); QVERIFY(root != nullptr); @@ -377,7 +698,7 @@ void tst_QQuickPinchHandler::pan() QPoint p1(100, 100); { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -385,7 +706,7 @@ void tst_QQuickPinchHandler::pan() pinchSequence.stationary(0).press(1, p1, window).commit(); QQuickTouchUtils::flush(window); QVERIFY(!root->property("pinchActive").toBool()); - QCOMPARE(root->property("scale").toReal(), -1.0); + QCOMPARE(root->property("pinchScale").toReal(), -1.0); p0 += QPoint(dragThreshold, 0); p1 += QPoint(dragThreshold, 0); @@ -393,7 +714,7 @@ void tst_QQuickPinchHandler::pan() QQuickTouchUtils::flush(window); // movement < dragThreshold: pinchHandler not yet active QVERIFY(!root->property("pinchActive").toBool()); - QCOMPARE(root->property("scale").toReal(), -1.0); + QCOMPARE(root->property("pinchScale").toReal(), -1.0); // just above the dragThreshold: pinchHandler starts p0 += QPoint(1, 0); @@ -401,7 +722,7 @@ void tst_QQuickPinchHandler::pan() pinchSequence.move(0, p0, window).move(1, p1, window).commit(); QQuickTouchUtils::flush(window); QCOMPARE(pinchHandler->active(), true); - QCOMPARE(root->property("scale").toReal(), 1.0); + QCOMPARE(root->property("pinchScale").toReal(), 1.0); // Calculation of the center point is tricky at first: // center point of the two touch points in item coordinates: @@ -413,6 +734,8 @@ void tst_QQuickPinchHandler::pan() // blackrect starts at 50,50 QCOMPARE(blackRect->x(), 50.0); QCOMPARE(blackRect->y(), 50.0); + QCOMPARE(translationChangedSpy.size(), 1); + QCOMPARE(translationChangedSpy.first().first().value<QVector2D>(), QVector2D(0, 0)); p0 += QPoint(10, 0); p1 += QPoint(10, 0); @@ -421,6 +744,8 @@ void tst_QQuickPinchHandler::pan() QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 11, 90)); QCOMPARE(blackRect->x(), 60.0); QCOMPARE(blackRect->y(), 50.0); + QCOMPARE(translationChangedSpy.size(), 2); + QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(10, 0)); p0 += QPoint(0, 10); p1 += QPoint(0, 10); @@ -429,6 +754,8 @@ void tst_QQuickPinchHandler::pan() QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 11, 90 + 10)); QCOMPARE(blackRect->x(), 60.0); QCOMPARE(blackRect->y(), 60.0); + QCOMPARE(translationChangedSpy.size(), 3); + QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(0, 10)); p0 += QPoint(10, 10); p1 += QPoint(10, 10); @@ -438,18 +765,22 @@ void tst_QQuickPinchHandler::pan() QCOMPARE(pinchHandler->centroid().scenePosition(), QPointF(90 + dragThreshold + 21, 90 + 20)); QCOMPARE(blackRect->x(), 70.0); QCOMPARE(blackRect->y(), 70.0); + QCOMPARE(translationChangedSpy.size(), 4); + QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(10, 10)); } // pan x beyond bound p0 += QPoint(100,100); p1 += QPoint(100,100); - QTest::touchEvent(window, device).move(0, p0, window).move(1, p1, window); + QTest::touchEvent(window, touchscreen.get()).move(0, p0, window).move(1, p1, window); QQuickTouchUtils::flush(window); QCOMPARE(blackRect->x(), 140.0); QCOMPARE(blackRect->y(), 170.0); + QCOMPARE(translationChangedSpy.size(), 5); + QCOMPARE(translationChangedSpy.last().first().value<QVector2D>(), QVector2D(100, 100)); - QTest::touchEvent(window, device).release(0, p0, window).release(1, p1, window); + QTest::touchEvent(window, touchscreen.get()).release(0, p0, window).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!root->property("pinchActive").toBool()); } @@ -467,7 +798,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled_data() void tst_QQuickPinchHandler::dragAxesEnabled() { - QQuickView *window = createView(); + QQuickView *window = QQuickViewTestUtils::createView(); QScopedPointer<QQuickView> scope(window); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); @@ -489,7 +820,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled() QPoint blackRectPos = blackRect->position().toPoint(); // press two points, one above the rectangle's center and one below - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).press(1, p1, window).commit(); QQuickTouchUtils::flush(window); @@ -526,7 +857,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled() QCOMPARE(blackRect->position().toPoint().x(), xEnabled ? 140 : blackRectPos.x()); // because of xAxis.maximum QCOMPARE(blackRect->position().toPoint().y(), yEnabled ? 170 : blackRectPos.y()); // because of yAxis.maximum - QTest::touchEvent(window, device).release(0, p0, window).release(1, p1, window); + QTest::touchEvent(window, touchscreen.get()).release(0, p0, window).release(1, p1, window); QQuickTouchUtils::flush(window); } @@ -534,7 +865,7 @@ void tst_QQuickPinchHandler::dragAxesEnabled() void tst_QQuickPinchHandler::retouch() { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); - QQuickView *window = createView(); + QQuickView *window = QQuickViewTestUtils::createView(); QScopedPointer<QQuickView> scope(window); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); @@ -555,7 +886,7 @@ void tst_QQuickPinchHandler::retouch() QPoint p0(80, 80); QPoint p1(100, 100); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -568,7 +899,7 @@ void tst_QQuickPinchHandler::retouch() pinchSequence.move(0, p0,window).move(1, p1,window).commit(); QQuickTouchUtils::flush(window); - QCOMPARE(root->property("scale").toReal(), 1.0); + QCOMPARE(root->property("pinchScale").toReal(), 1.0); QCOMPARE(pinchHandler->active(), true); p0 -= delta; @@ -579,8 +910,8 @@ void tst_QQuickPinchHandler::retouch() QCOMPARE(pinchHandler->active(), true); // accept some slack - QVERIFY(withinBounds(1.4, root->property("scale").toReal(), 1.6)); - QCOMPARE(pinchHandler->centroid().position(), QPointF(40, 40)); // blackrect is at 50,50 + QVERIFY(withinBounds(1.4, root->property("pinchScale").toReal(), 1.6)); + QCOMPARE(pinchHandler->centroid().position().toPoint(), QPoint(40, 40)); // blackrect is at 50,50 QVERIFY(withinBounds(1.4, blackRect->scale(), 1.6)); QCOMPARE(root->property("activeCount").toInt(), 1); @@ -617,7 +948,7 @@ void tst_QQuickPinchHandler::retouch() void tst_QQuickPinchHandler::cancel() { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); - QQuickView *window = createView(); + QQuickView *window = QQuickViewTestUtils::createView(); QScopedPointer<QQuickView> scope(window); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); @@ -638,7 +969,7 @@ void tst_QQuickPinchHandler::cancel() QPoint p0(80, 80); QPoint p1(100, 100); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, touchscreen.get()); pinchSequence.press(0, p0, window).commit(); QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, @@ -654,7 +985,7 @@ void tst_QQuickPinchHandler::cancel() pinchSequence.move(0, p0,window).move(1, p1,window).commit(); QQuickTouchUtils::flush(window); - QCOMPARE(root->property("scale").toReal(), 1.0); + QCOMPARE(root->property("pinchScale").toReal(), 1.0); QCOMPARE(pinchHandler->active(), true); p0 -= delta; @@ -662,18 +993,18 @@ void tst_QQuickPinchHandler::cancel() pinchSequence.move(0, p0,window).move(1, p1,window).commit(); QQuickTouchUtils::flush(window); - QVERIFY(withinBounds(1.4, root->property("scale").toReal(), 1.6)); - QCOMPARE(pinchHandler->centroid().position(), QPointF(40, 40)); // blackrect is at 50,50 + QVERIFY(withinBounds(1.4, root->property("pinchScale").toReal(), 1.6)); + QCOMPARE(pinchHandler->centroid().position().toPoint(), QPoint(40, 40)); // blackrect is at 50,50 QVERIFY(withinBounds(1.4, blackRect->scale(), 1.6)); QSKIP("cancel is not supported atm"); - QTouchEvent cancelEvent(QEvent::TouchCancel, device); + QTouchEvent cancelEvent(QEvent::TouchCancel, touchscreen.get()); QCoreApplication::sendEvent(window, &cancelEvent); QQuickTouchUtils::flush(window); - QCOMPARE(root->property("scale").toReal(), 1.0); - QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50 + QCOMPARE(root->property("pinchScale").toReal(), 1.0); + QCOMPARE(root->property("center").toPoint(), QPoint(40, 40)); // blackrect is at 50,50 QCOMPARE(blackRect->scale(), 1.0); QVERIFY(!root->property("pinchActive").toBool()); } @@ -710,7 +1041,7 @@ void tst_QQuickPinchHandler::transformedpinchHandler() QFETCH(QPoint, p1); QFETCH(bool, shouldPinch); - QQuickView *view = createView(); + QQuickView *view = QQuickViewTestUtils::createView(); QScopedPointer<QQuickView> scope(view); view->setSource(testFileUrl("transformedPinchHandler.qml")); view->show(); @@ -724,7 +1055,7 @@ void tst_QQuickPinchHandler::transformedpinchHandler() const int threshold = qApp->styleHints()->startDragDistance(); { - QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, device); + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, touchscreen.get()); // start pinchHandler pinchSequence.press(0, p0, view).commit(); QQuickTouchUtils::flush(view); @@ -749,14 +1080,6 @@ void tst_QQuickPinchHandler::transformedpinchHandler() } } -QQuickView *tst_QQuickPinchHandler::createView() -{ - QQuickView *window = new QQuickView(0); - window->setGeometry(0,0,240,320); - - return window; -} - QTEST_MAIN(tst_QQuickPinchHandler) #include "tst_qquickpinchhandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt index 9dc541a845..e15b802814 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from qquickpointerhandler.pro. ##################################################################### ## tst_qquickpointerhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickpointerhandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qquickpointerhandler SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_qquickpointerhandler.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -35,10 +37,10 @@ qt_internal_add_test(tst_qquickpointerhandler qt_internal_extend_target(tst_qquickpointerhandler CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_qquickpointerhandler CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) 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/data/grabberSceneChange.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml new file mode 100644 index 0000000000..7bc6028bc2 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/grabberSceneChange.qml @@ -0,0 +1,78 @@ +import QtQuick + +Window { + id: root + visible: true + objectName: "root" + width: 320 + height: 480 + + property bool useTimer : false + property int grabChangedCounter : 0 + + Item { + id: back + anchors.fill: parent + + Rectangle { + id: background + anchors.fill: parent + color: "blue" + } + + Rectangle { + id: container + objectName: "container" + anchors.fill: parent + anchors.margins: 50 + z: 2 + + Rectangle { + id: likeButton + color: "gray" + anchors.centerIn: parent + width: 200 + height: 200 + + DragHandler { + id: handler + objectName: "dragHandler" + grabPermissions: PointerHandler.CanTakeOverFromItems + onGrabChanged: { + ++grabChangedCounter + } + } + } + } + + Timer { + id: reparentTimer + running: false + interval: 100 + repeat: false + onTriggered: { + container.parent = null + } + } + + Rectangle { + id: likeButton2 + color: "yellow" + anchors.centerIn: parent + width: 100 + height: 100 + z: 3 + + MultiPointTouchArea { + id: press + anchors.fill: parent + onPressed: { + if (useTimer) + reparentTimer.running = true + else + container.parent = null + } + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/itemOnly.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/itemOnly.qml new file mode 100644 index 0000000000..70b4655160 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/itemOnly.qml @@ -0,0 +1,24 @@ +import QtQuick 2.8 +import Qt.test 1.0 + +Item { + id: root + objectName: "root Item" + width: 320 + height: 480 + + Rectangle { + objectName: "eventItem's bounds" + anchors.fill: eventItem + color: "lightsteelblue" + } + + EventItem { + id: eventItem + objectName: "eventItem1" + x: 5 + y: 5 + height: 30 + width: 30 + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/reparenting.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/reparenting.qml new file mode 100644 index 0000000000..7553bc63cc --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/reparenting.qml @@ -0,0 +1,40 @@ +import QtQuick +import Qt.test + +Item { + id: root + objectName: "root Item" + width: 320 + height: 480 + + Rectangle { + id: top + objectName: "top" + width: parent.width - 12 + height: parent.height / 2 - 9 + x: 6; y: 6 + color: "teal" + border.width: 3 + border.color: handler.parent == top ? "brown" : "transparent" + EventHandler { + id: handler + objectName: "eventHandler" + } + } + + Rectangle { + id: bottom + objectName: "bottom" + width: parent.width - 12 + height: parent.height / 2 - 9 + x: 6; y: parent.height / 2 + 3 + color: "darkolivegreen" + border.width: 3 + border.color: handler.parent == bottom ? "brown" : "transparent" + } + + Timer { + interval: 1000; running: true; repeat: true + onTriggered: handler.parent = (handler.parent == top ? bottom : top) + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp index 6b0f574178..1120cb54c2 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -33,10 +8,13 @@ #include <QtGui/private/qpointingdevice_p.h> #include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickitem_p.h> #include <QtQuick/qquickview.h> +#include <QtQml/private/qqmlglobal_p.h> // qmlobject_cast -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QQmlComponent> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -103,45 +81,45 @@ public: return (accept && (state != QEventPoint::State::Released)) ? (int)QPointingDevice::GrabExclusive : (int)NoGrab; } - void touchEvent(QTouchEvent *event) + void touchEvent(QTouchEvent *event) override { qCDebug(lcPointerTests) << event << "will accept?" << acceptTouch; for (auto &tp : event->points()) eventList.append(Event(Event::TouchDestination, event->type(), tp.state(), grabTransition(acceptTouch, tp.state()), tp.position(), tp.scenePosition())); event->setAccepted(acceptTouch); } - void mousePressEvent(QMouseEvent *event) + void mousePressEvent(QMouseEvent *event) override { qCDebug(lcPointerTests) << event << "will accept?" << acceptMouse; eventList.append(Event(Event::MouseDestination, event->type(), QEventPoint::State::Pressed, grabTransition(acceptMouse, QEventPoint::State::Pressed), event->position().toPoint(), event->scenePosition())); event->setAccepted(acceptMouse); } - void mouseMoveEvent(QMouseEvent *event) + void mouseMoveEvent(QMouseEvent *event) override { qCDebug(lcPointerTests) << event << "will accept?" << acceptMouse; eventList.append(Event(Event::MouseDestination, event->type(), QEventPoint::State::Updated, grabTransition(acceptMouse, QEventPoint::State::Updated), event->position().toPoint(), event->scenePosition())); event->setAccepted(acceptMouse); } - void mouseReleaseEvent(QMouseEvent *event) + void mouseReleaseEvent(QMouseEvent *event) override { qCDebug(lcPointerTests) << event << "will accept?" << acceptMouse; eventList.append(Event(Event::MouseDestination, event->type(), QEventPoint::State::Released, grabTransition(acceptMouse, QEventPoint::State::Released), event->position().toPoint(), event->scenePosition())); event->setAccepted(acceptMouse); } - void mouseDoubleClickEvent(QMouseEvent *event) + void mouseDoubleClickEvent(QMouseEvent *event) override { qCDebug(lcPointerTests) << event << "will accept?" << acceptMouse; eventList.append(Event(Event::MouseDestination, event->type(), QEventPoint::State::Pressed, grabTransition(acceptMouse, QEventPoint::State::Pressed), event->position().toPoint(), event->scenePosition())); event->setAccepted(acceptMouse); } - void mouseUngrabEvent() + void mouseUngrabEvent() override { qCDebug(lcPointerTests); eventList.append(Event(Event::MouseDestination, QEvent::UngrabMouse, QEventPoint::State::Released, QPointingDevice::UngrabExclusive, QPoint(0,0), QPoint(0,0))); } - bool event(QEvent *event) + bool event(QEvent *event) override { qCDebug(lcPointerTests) << event; return QQuickItem::event(event); @@ -154,7 +132,7 @@ public: bool acceptTouch; bool filterTouch; // when used as event filter - bool eventFilter(QObject *o, QEvent *event) + bool eventFilter(QObject *o, QEvent *event) override { qCDebug(lcPointerTests) << event << o; if (event->type() == QEvent::TouchBegin || @@ -184,7 +162,11 @@ public: class EventHandler : public QQuickPointerHandler { + Q_OBJECT public: + EventHandler(QQuickItem *parent = nullptr) : + QQuickPointerHandler(parent) {} + void handlePointerEventImpl(QPointerEvent *event) override { QQuickPointerHandler::handlePointerEventImpl(event); @@ -233,7 +215,8 @@ class tst_PointerHandlers : public QQmlDataTest Q_OBJECT public: tst_PointerHandlers() - : touchDevice(QTest::createTouchDevice()) + : QQmlDataTest(QT_QMLTEST_DATADIR) + , touchDevice(QTest::createTouchDevice()) {} private slots: @@ -247,9 +230,14 @@ private slots: void dynamicCreation(); void handlerInWindow(); void dynamicCreationInWindow(); + void cppConstruction(); + void reparenting(); + void grabberSceneChange_data(); + void grabberSceneChange(); + void clip(); protected: - bool eventFilter(QObject *, QEvent *event) + bool eventFilter(QObject *, QEvent *event) override { QEventPoint::State tpState; switch (event->type()) { @@ -284,8 +272,8 @@ void tst_PointerHandlers::createView(QScopedPointer<QQuickView> &window, const c // window->setGeometry(0,0,240,320); window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickViewTestUtils::centerOnScreen(window.data()); + QQuickViewTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); @@ -335,11 +323,11 @@ void tst_PointerHandlers::touchEventDelivery() QTest::touchEvent(window, touchDevice).move(0, p1, window); QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "events after touch move" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 2); // no grabs -> no updates + QCOMPARE(eventItem1->eventList.size(), 3); // no grabs -> only the handler gets the update QTest::touchEvent(window, touchDevice).release(0, p1, window); QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "events after touch release" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 4); QCOMPARE_EVENT(eventItem1->eventList.size() - 1, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Released, NoGrab); eventItem1->eventList.clear(); @@ -390,12 +378,12 @@ void tst_PointerHandlers::touchEventDelivery() QTest::touchEvent(window, touchDevice).move(0, p1, window); QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "events after touch move" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 3); QTest::touchEvent(window, touchDevice).release(0, p1, window); QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "events after touch release" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 3); - QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Released, NoGrab); + QCOMPARE(eventItem1->eventList.size(), 4); + QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Released, NoGrab); eventItem1->eventList.clear(); // wait to avoid getting a double click event @@ -417,11 +405,11 @@ void tst_PointerHandlers::touchEventDelivery() QTest::touchEvent(window, touchDevice).move(0, p1, window); QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "events after touch move" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 3); QTest::touchEvent(window, touchDevice).release(0, p1, window); QQuickTouchUtils::flush(window); qCDebug(lcPointerTests) << "events after touch release" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE(eventItem1->eventList.size(), 4); eventItem1->eventList.clear(); // wait to avoid getting a double click event @@ -485,18 +473,24 @@ void tst_PointerHandlers::mouseEventDelivery() EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1"); QVERIFY(eventItem1); + EventHandler *handler = window->rootObject()->findChild<EventHandler*>("eventHandler"); + QVERIFY(handler); + QCOMPARE(handler->parentItem(), eventItem1); + QCOMPARE(handler->target(), eventItem1); + QVERIFY(QQuickItemPrivate::get(eventItem1)->extra->resourcesList.contains(handler)); + // Do not accept anything QPoint p1 = QPoint(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); qCDebug(lcPointerTests) << "events after mouse press" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 3); // handler: hover; handler: press; item: press p1 += QPoint(10, 0); QTest::mouseMove(window, p1); qCDebug(lcPointerTests) << "events after mouse move" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 4); // handler: hover QTest::mouseRelease(window, Qt::LeftButton); qCDebug(lcPointerTests) << "events after mouse release" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 2); + QCOMPARE(eventItem1->eventList.size(), 4); eventItem1->eventList.clear(); // wait to avoid getting a double click event @@ -509,9 +503,9 @@ void tst_PointerHandlers::mouseEventDelivery() p1 = QPoint(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); qCDebug(lcPointerTests) << "events after mouse press" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 2); - QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab); - QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, QPointingDevice::GrabExclusive); + QCOMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab); + QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, QPointingDevice::GrabExclusive); QCOMPARE(window->mouseGrabberItem(), eventItem1); QPointF localPos = eventItem1->mapFromScene(p1); @@ -524,11 +518,11 @@ void tst_PointerHandlers::mouseEventDelivery() p1 += QPoint(10, 0); QTest::mouseMove(window, p1); qCDebug(lcPointerTests) << "events after mouse move" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 4); - QCOMPARE_EVENT(3, Event::MouseDestination, QEvent::MouseMove, QEventPoint::State::Updated, QPointingDevice::GrabExclusive); + QCOMPARE(eventItem1->eventList.size(), 5); + QCOMPARE_EVENT(4, Event::MouseDestination, QEvent::MouseMove, QEventPoint::State::Updated, QPointingDevice::GrabExclusive); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); qCDebug(lcPointerTests) << "events after mouse release" << eventItem1->eventList; - QCOMPARE(eventItem1->eventList.size(), 7); + QCOMPARE(eventItem1->eventList.size(), 8); QCOMPARE_EVENT(eventItem1->eventList.size() - 2, Event::MouseDestination, QEvent::MouseButtonRelease, QEventPoint::State::Released, NoGrab); QCOMPARE_EVENT(eventItem1->eventList.size() - 1, Event::MouseDestination, QEvent::UngrabMouse, QEventPoint::State::Released, QPointingDevice::UngrabExclusive); eventItem1->eventList.clear(); @@ -631,12 +625,14 @@ void tst_PointerHandlers::dynamicCreation() QCOMPARE(handler->parentItem(), eventItem1); QCOMPARE(handler->target(), eventItem1); + QVERIFY(QQuickItemPrivate::get(eventItem1)->extra->resourcesList.contains(handler)); QPoint p1(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(eventItem1->eventList.size(), 2); - QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab); - QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, NoGrab); + QTRY_COMPARE(eventItem1->eventList.size(), 3); + QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Updated, NoGrab); + QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, QEventPoint::State::Pressed, NoGrab); + QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, QEventPoint::State::Pressed, NoGrab); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); } @@ -688,6 +684,181 @@ void tst_PointerHandlers::dynamicCreationInWindow() QTRY_COMPARE(handler->releaseEventCount, 1); } +void tst_PointerHandlers::cppConstruction() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "itemOnly.qml"); + QQuickView * window = windowPtr.data(); + + EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1"); + QVERIFY(eventItem1); + QQuickItemPrivate *eventItemPriv = QQuickItemPrivate::get(eventItem1); + // tell the handler to grab on press + eventItem1->grabPointer = true; + + EventHandler handler(eventItem1); + QCOMPARE(handler.parentItem(), eventItem1); + QCOMPARE(handler.target(), eventItem1); + QCOMPARE(eventItemPriv->extra->pointerHandlers.first(), &handler); + QVERIFY(eventItemPriv->extra->resourcesList.contains(&handler)); + + // the handler and then eventItem1 sees each event + QPoint p1 = QPoint(20, 20); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(handler.pressEventCount, 1); + qCDebug(lcPointerTests) << "events after mouse press" << eventItem1->eventList; + QCOMPARE(eventItem1->eventList.size(), 3); + QTest::mouseRelease(window, Qt::LeftButton); + QCOMPARE(handler.releaseEventCount, 1); + qCDebug(lcPointerTests) << "events after mouse release" << eventItem1->eventList; + QCOMPARE(eventItem1->eventList.size(), 7); + + // wait to avoid getting a double click event + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); +} + +void tst_PointerHandlers::reparenting() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "reparenting.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *topItem = window->rootObject()->findChild<QQuickItem*>("top"); + QVERIFY(topItem); + QQuickItem *bottomItem = window->rootObject()->findChild<QQuickItem*>("bottom"); + QVERIFY(bottomItem); + EventHandler *handler = window->rootObject()->findChild<EventHandler*>("eventHandler"); + QVERIFY(handler); + topItem->setAcceptedMouseButtons(Qt::RightButton); + + for (int i = 1; i < 5; ++i) { + QQuickItem *expectedParentItem = (i % 2 ? topItem : bottomItem); + QQuickItem *unexpectedParentItem = (i % 2 ? bottomItem : topItem); + qCDebug(lcPointerTests) << "initial parent" << handler->parentItem() << "waiting for" << expectedParentItem; + QTRY_COMPARE(handler->parentItem(), expectedParentItem); + QCOMPARE(handler->target(), expectedParentItem); + QVERIFY(QQuickItemPrivate::get(expectedParentItem)->extra.isAllocated()); + QVERIFY(QQuickItemPrivate::get(expectedParentItem)->extra->resourcesList.contains(handler)); + if (QQuickItemPrivate::get(unexpectedParentItem)->extra.isAllocated()) + QVERIFY(!QQuickItemPrivate::get(unexpectedParentItem)->extra->resourcesList.contains(handler)); + QCOMPARE(expectedParentItem->acceptedMouseButtons(), Qt::AllButtons); + QCOMPARE(unexpectedParentItem->acceptedMouseButtons(), unexpectedParentItem == topItem ? Qt::RightButton : Qt::NoButton); + + QPoint pt = expectedParentItem->mapToScene(expectedParentItem->boundingRect().center()).toPoint(); + qCDebug(lcPointerTests) << "click @" << pt; + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, pt); + QTRY_COMPARE(handler->pressEventCount, i); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, pt); + QTRY_COMPARE(handler->releaseEventCount, i); + } +} + +/*! + Verify that removing an item that has a grabbing handler from the scene + does not result in crashes in our event dispatching code. The item's window() + pointer will be nullptr, so the handler must have released the grab, or never + gotten the grab, depending on when the item gets removed. + + See QTBUG-114475. +*/ +void tst_PointerHandlers::grabberSceneChange_data() +{ + QTest::addColumn<bool>("useTimer"); + QTest::addColumn<int>("grabChangedCount"); + + QTest::addRow("Immediately") << false << 0; + QTest::addRow("Delayed") << true << 2; +} + +void tst_PointerHandlers::grabberSceneChange() +{ + QFETCH(const bool, useTimer); + QFETCH(const int, grabChangedCount); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("grabberSceneChange.qml")); + QQuickWindow *window = qobject_cast<QQuickWindow*>(component.create()); + QScopedPointer<QQuickWindow> cleanup(window); + QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + window->setProperty("useTimer", useTimer); + + QQuickItem *container = window->findChild<QQuickItem *>("container"); + + QPoint p1 = QPoint(window->width() / 2, window->height() / 2); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + // The container gets removed from this window, either immediately on + // press, or through a timer. + QTRY_COMPARE(container->parentItem(), nullptr); + + QEXPECT_FAIL("Delayed", + "PointerHandlers don't release their grab when item is removed", Continue); + QCOMPARE(window->property("grabChangedCounter").toInt(), grabChangedCount); + + // this should not crash + QTest::mouseMove(window, p1 + QPoint(5, 5)); +} + +void tst_PointerHandlers::clip() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "clip.qml"); + QQuickView * window = windowPtr.data(); + QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + EventHandler *handler = window->contentItem()->findChild<EventHandler*>("eventHandler"); + EventHandler *circleHandler = window->contentItem()->findChild<EventHandler*>("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" diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquickpointhandler/BLACKLIST index f4d271c230..5d6a59e1dd 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/BLACKLIST @@ -1,2 +1,4 @@ [tabletStylus] macos ci +qnx +android # QTBUG-103068 diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt index 507bc7549b..aa73218361 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from qquickpointhandler.pro. ##################################################################### ## tst_qquickpointhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickpointhandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qquickpointhandler SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_qquickpointhandler.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_qquickpointhandler qt_internal_extend_target(tst_qquickpointhandler CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_qquickpointhandler CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml index 3f50d7c761..616d526592 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/multiPointTracker.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml index 674bbda850..2de03703e8 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/data/pointTracker.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp index a2415fedd7..e1641c282e 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointhandler/tst_qquickpointhandler.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -33,13 +8,17 @@ #include <QtQuick/private/qquickpointhandler_p.h> #include <qpa/qwindowsysteminterface.h> +#include <private/qhighdpiscaling_p.h> #include <private/qquickwindow_p.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> +#include <QtQuickTestUtils/private/visualtestutils_p.h> + +#include <QtCore/qpointer.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -48,7 +27,8 @@ class tst_PointHandler : public QQmlDataTest Q_OBJECT public: tst_PointHandler() - : touchDevice(QTest::createTouchDevice()) + : QQmlDataTest(QT_QMLTEST_DATADIR) + , touchDevice(QTest::createTouchDevice()) {} private slots: @@ -59,6 +39,7 @@ private slots: void simultaneousMultiTouch(); void pressedMultipleButtons_data(); void pressedMultipleButtons(); + void ignoreSystemSynthMouse(); private: void createView(QScopedPointer<QQuickView> &window, const char *fileName); @@ -70,8 +51,8 @@ void tst_PointHandler::createView(QScopedPointer<QQuickView> &window, const char window.reset(new QQuickView); window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickVisualTestUtils::centerOnScreen(window.data()); + QQuickVisualTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); @@ -104,20 +85,20 @@ void tst_PointHandler::singleTouch() QTest::touchEvent(window, touchDevice).press(1, point, window); QQuickTouchUtils::flush(window); QTRY_COMPARE(handler->active(), true); - QCOMPARE(activeSpy.count(), 1); - QCOMPARE(pointSpy.count(), 1); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 1); QCOMPARE(handler->point().position().toPoint(), point); QCOMPARE(handler->point().scenePosition().toPoint(), point); QCOMPARE(handler->point().pressedButtons(), Qt::NoButton); QCOMPARE(handler->translation(), QVector2D()); - QCOMPARE(translationSpy.count(), 1); + QCOMPARE(translationSpy.size(), 1); point += QPoint(10, 10); QTest::touchEvent(window, touchDevice).move(1, point, window); QQuickTouchUtils::flush(window); QCOMPARE(handler->active(), true); - QCOMPARE(activeSpy.count(), 1); - QCOMPARE(pointSpy.count(), 2); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 2); QCOMPARE(handler->point().position().toPoint(), point); QCOMPARE(handler->point().scenePosition().toPoint(), point); QCOMPARE(handler->point().pressPosition().toPoint(), QPoint(100, 100)); @@ -126,15 +107,15 @@ void tst_PointHandler::singleTouch() QVERIFY(handler->point().velocity().x() > 0); QVERIFY(handler->point().velocity().y() > 0); QCOMPARE(handler->translation(), QVector2D(10, 10)); - QCOMPARE(translationSpy.count(), 2); + QCOMPARE(translationSpy.size(), 2); QTest::touchEvent(window, touchDevice).release(1, point, window); QQuickTouchUtils::flush(window); QTRY_COMPARE(handler->active(), false); - QCOMPARE(activeSpy.count(), 2); - QCOMPARE(pointSpy.count(), 3); + QCOMPARE(activeSpy.size(), 2); + QCOMPARE(pointSpy.size(), 3); QCOMPARE(handler->translation(), QVector2D()); - QCOMPARE(translationSpy.count(), 3); + QCOMPARE(translationSpy.size(), 3); } void tst_PointHandler::tabletStylus() @@ -146,54 +127,59 @@ void tst_PointHandler::tabletStylus() QQuickPointHandler *handler = window->rootObject()->findChild<QQuickPointHandler *>("pointHandler"); QVERIFY(handler); handler->setAcceptedDevices(QInputDevice::DeviceType::Stylus); + // avoid generating a double click + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); QSignalSpy activeSpy(handler, SIGNAL(activeChanged())); QSignalSpy pointSpy(handler, SIGNAL(pointChanged())); QSignalSpy translationSpy(handler, SIGNAL(translationChanged())); QPoint point(100,100); + QPoint pointLocalDPI = QHighDpi::fromNativeLocalPosition(point, window); const qint64 stylusId = 1234567890; QWindowSystemInterface::handleTabletEvent(window, point, window->mapToGlobal(point), int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen), Qt::LeftButton, 0.5, 25, 35, 0.6, 12.3, 3, stylusId, Qt::NoModifier); QTRY_COMPARE(handler->active(), true); - QCOMPARE(activeSpy.count(), 1); - QCOMPARE(pointSpy.count(), 1); - QCOMPARE(handler->point().position().toPoint(), point); - QCOMPARE(handler->point().scenePosition().toPoint(), point); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 1); + QCOMPARE(handler->point().position().toPoint(), pointLocalDPI); + QCOMPARE(handler->point().scenePosition().toPoint(), pointLocalDPI); QCOMPARE(handler->point().pressedButtons(), Qt::LeftButton); QCOMPARE(handler->point().pressure(), 0.5); QCOMPARE(handler->point().rotation(), 12.3); QCOMPARE(handler->point().uniqueId().numericId(), stylusId); QCOMPARE(handler->translation(), QVector2D()); - QCOMPARE(translationSpy.count(), 1); + QCOMPARE(translationSpy.size(), 1); - point += QPoint(10, 10); + QPoint delta(10, 10); + QPoint deltaLocalDPI = QHighDpi::fromNativeLocalPosition(delta, window); + point += delta; QWindowSystemInterface::handleTabletEvent(window, point, window->mapToGlobal(point), int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen), Qt::LeftButton, 0.45, 23, 33, 0.57, 15.6, 3, stylusId, Qt::NoModifier); - QTRY_COMPARE(pointSpy.count(), 2); + QTRY_COMPARE(pointSpy.size(), 2); QCOMPARE(handler->active(), true); - QCOMPARE(activeSpy.count(), 1); - QCOMPARE(handler->point().position().toPoint(), point); - QCOMPARE(handler->point().scenePosition().toPoint(), point); - QCOMPARE(handler->point().pressPosition().toPoint(), QPoint(100, 100)); - QCOMPARE(handler->point().scenePressPosition().toPoint(), QPoint(100, 100)); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(handler->point().position().toPoint(), pointLocalDPI + deltaLocalDPI); + QCOMPARE(handler->point().scenePosition().toPoint(), pointLocalDPI + deltaLocalDPI); + QCOMPARE(handler->point().pressPosition().toPoint(), pointLocalDPI); + QCOMPARE(handler->point().scenePressPosition().toPoint(), pointLocalDPI); QCOMPARE(handler->point().pressedButtons(), Qt::LeftButton); QCOMPARE(handler->point().pressure(), 0.45); QCOMPARE(handler->point().rotation(), 15.6); QCOMPARE(handler->point().uniqueId().numericId(), stylusId); - QVERIFY(handler->point().velocity().x() > 0); - QVERIFY(handler->point().velocity().y() > 0); - QCOMPARE(handler->translation(), QVector2D(10, 10)); - QCOMPARE(translationSpy.count(), 2); + QCOMPARE_GT(handler->point().velocity().x(), 0); + QCOMPARE_GT(handler->point().velocity().y(), 0); + QCOMPARE(handler->translation(), QVector2D(deltaLocalDPI)); + QCOMPARE(translationSpy.size(), 2); QWindowSystemInterface::handleTabletEvent(window, point, window->mapToGlobal(point), int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen), Qt::NoButton, 0, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier); QTRY_COMPARE(handler->active(), false); - QCOMPARE(activeSpy.count(), 2); - QCOMPARE(pointSpy.count(), 3); + QCOMPARE(activeSpy.size(), 2); + QCOMPARE(pointSpy.size(), 3); QCOMPARE(handler->translation(), QVector2D()); - QCOMPARE(translationSpy.count(), 3); + QCOMPARE(translationSpy.size(), 3); } void tst_PointHandler::simultaneousMultiTouch() @@ -202,7 +188,7 @@ void tst_PointHandler::simultaneousMultiTouch() createView(windowPtr, "multiPointTracker.qml"); QQuickView * window = windowPtr.data(); QList<QQuickPointHandler *> handlers = window->rootObject()->findChildren<QQuickPointHandler *>(); - QCOMPARE(handlers.count(), 3); + QCOMPARE(handlers.size(), 3); QVector<QSignalSpy*> activeSpies; QVector<QSignalSpy*> pointSpies; @@ -221,8 +207,8 @@ void tst_PointHandler::simultaneousMultiTouch() int i = 0; for (auto h : handlers) { QTRY_COMPARE(h->active(), true); - QCOMPARE(activeSpies[i]->count(), 1); - QCOMPARE(pointSpies[i]->count(), 1); + QCOMPARE(activeSpies[i]->size(), 1); + QCOMPARE(pointSpies[i]->size(), 1); int chosenPointIndex = points.indexOf(h->point().position().toPoint()); QVERIFY(chosenPointIndex != -1); // Verify that each handler chose a unique point @@ -232,7 +218,7 @@ void tst_PointHandler::simultaneousMultiTouch() QCOMPARE(h->point().scenePosition().toPoint(), point); QCOMPARE(h->point().pressedButtons(), Qt::NoButton); QCOMPARE(h->translation(), QVector2D()); - QCOMPARE(translationSpies[i]->count(), 1); + QCOMPARE(translationSpies[i]->size(), 1); ++i; } @@ -243,8 +229,8 @@ void tst_PointHandler::simultaneousMultiTouch() i = 0; for (auto h : handlers) { QCOMPARE(h->active(), true); - QCOMPARE(activeSpies[i]->count(), 1); - QCOMPARE(pointSpies[i]->count(), 2); + QCOMPARE(activeSpies[i]->size(), 1); + QCOMPARE(pointSpies[i]->size(), 2); QCOMPARE(h->point().position().toPoint(), points[pointIndexPerHandler[i]]); QCOMPARE(h->point().scenePosition().toPoint(), points[pointIndexPerHandler[i]]); QCOMPARE(h->point().pressPosition().toPoint(), pressPoints[pointIndexPerHandler[i]]); @@ -253,7 +239,7 @@ void tst_PointHandler::simultaneousMultiTouch() QVERIFY(h->point().velocity().x() > 0); QVERIFY(h->point().velocity().y() > 0); QCOMPARE(h->translation(), QVector2D(10 + 10 * pointIndexPerHandler[i], 10 + 10 * pointIndexPerHandler[i] % 2)); - QCOMPARE(translationSpies[i]->count(), 2); + QCOMPARE(translationSpies[i]->size(), 2); ++i; } @@ -262,10 +248,10 @@ void tst_PointHandler::simultaneousMultiTouch() i = 0; for (auto h : handlers) { QTRY_COMPARE(h->active(), false); - QCOMPARE(activeSpies[i]->count(), 2); - QCOMPARE(pointSpies[i]->count(), 3); + QCOMPARE(activeSpies[i]->size(), 2); + QCOMPARE(pointSpies[i]->size(), 3); QCOMPARE(h->translation(), QVector2D()); - QCOMPARE(translationSpies[i]->count(), 3); + QCOMPARE(translationSpies[i]->size(), 3); ++i; } @@ -379,7 +365,7 @@ void tst_PointHandler::pressedMultipleButtons() QPoint point(100,100); - for (int i = 0; i < buttons.count(); ++i) { + for (int i = 0; i < buttons.size(); ++i) { int btns = int(buttons.at(i)); int release = 0; if (i > 0) { @@ -399,8 +385,91 @@ void tst_PointHandler::pressedMultipleButtons() QTest::mousePress(windowPtr.data(), Qt::NoButton, Qt::NoModifier, point); QCOMPARE(handler->active(), false); - QCOMPARE(activeSpy.count(), activeChangeCount); - QCOMPARE(pointSpy.count(), changeCount); + QCOMPARE(activeSpy.size(), activeChangeCount); + QCOMPARE(pointSpy.size(), changeCount); +} + +void tst_PointHandler::ignoreSystemSynthMouse() // QTBUG-104890 +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("pointTracker.qml"))); + QQuickPointHandler *handler = window.rootObject()->findChild<QQuickPointHandler *>(); + QVERIFY(handler); + auto devPriv = QPointingDevicePrivate::get(touchDevice); + QSignalSpy activeSpy(handler, SIGNAL(activeChanged())); + QSignalSpy pointSpy(handler, SIGNAL(pointChanged())); + + // touch press + QPoint point(100,100); + QTest::touchEvent(&window, touchDevice).press(0, point, &window); + QQuickTouchUtils::flush(&window); + + // touch move + point += QPoint(10, 10); + QTest::touchEvent(&window, touchDevice).move(0, point, &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(handler->active(), true); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 2); + QVERIFY(devPriv->queryPointById(0)->passiveGrabbers.contains(handler)); + + // Windows begins to synthesize mouse events in parallel with the touch event stream: move to touchpoint position, then press + { + QMouseEvent move(QEvent::MouseMove, point, point, window.mapToGlobal(point), + Qt::NoButton, Qt::NoButton, Qt::NoModifier, Qt::MouseEventSynthesizedBySystem, touchDevice); + move.setTimestamp(235); // slightly after the last touch event + QGuiApplication::sendEvent(&window, &move); + } + QCOMPARE(handler->active(), true); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 2); + QVERIFY(devPriv->queryPointById(0)->passiveGrabbers.contains(handler)); + { + QMouseEvent press(QEvent::MouseButtonPress, point, point, window.mapToGlobal(point), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier, Qt::MouseEventSynthesizedBySystem, touchDevice); + press.setTimestamp(235); + QGuiApplication::sendEvent(&window, &press); + } + QCOMPARE(handler->active(), true); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 2); + QVERIFY(devPriv->queryPointById(0)->passiveGrabbers.contains(handler)); + + // another touch move + point += QPoint(10, 10); + QTest::touchEvent(&window, touchDevice).move(0, point, &window); + QQuickTouchUtils::flush(&window); + QCOMPARE(handler->active(), true); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 3); + QCOMPARE(handler->point().position().toPoint(), point); + QCOMPARE(handler->point().scenePosition().toPoint(), point); + QCOMPARE(handler->point().pressPosition().toPoint(), QPoint(100, 100)); + QCOMPARE(handler->point().scenePressPosition().toPoint(), QPoint(100, 100)); + QVERIFY(devPriv->queryPointById(0)->passiveGrabbers.contains(handler)); + + // another fake mouse move + { + QMouseEvent move(QEvent::MouseMove, point, point, window.mapToGlobal(point), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier, Qt::MouseEventSynthesizedBySystem, touchDevice); + move.setTimestamp(240); + QGuiApplication::sendEvent(&window, &move); + } + QCOMPARE(handler->active(), true); + QCOMPARE(activeSpy.size(), 1); + QCOMPARE(pointSpy.size(), 3); + QCOMPARE(handler->point().position().toPoint(), point); + QCOMPARE(handler->point().scenePosition().toPoint(), point); + QCOMPARE(handler->point().pressPosition().toPoint(), QPoint(100, 100)); + QCOMPARE(handler->point().scenePressPosition().toPoint(), QPoint(100, 100)); + QVERIFY(devPriv->queryPointById(0)->passiveGrabbers.contains(handler)); + + // end with released state + QTest::touchEvent(&window, touchDevice).release(0, point, &window); + QMouseEvent release(QEvent::MouseButtonRelease, point, point, window.mapToGlobal(point), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier, Qt::MouseEventSynthesizedBySystem); + release.setTimestamp(280); + QGuiApplication::sendEvent(&window, &release); } QTEST_MAIN(tst_PointHandler) diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST b/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST new file mode 100644 index 0000000000..1559014480 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/BLACKLIST @@ -0,0 +1,3 @@ +# QTBUG-103072 +[gesturePolicyDragWithinBounds] +android diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt index babe276519..94834e04c6 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from qquicktaphandler.pro. ##################################################################### ## tst_qquicktaphandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquicktaphandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qquicktaphandler SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_qquicktaphandler.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -35,10 +37,10 @@ qt_internal_add_test(tst_qquicktaphandler qt_internal_extend_target(tst_qquicktaphandler CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_qquicktaphandler CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml index 800c25c77d..fc1e623902 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 @@ -35,13 +10,20 @@ Rectangle { property alias pressed: tap.pressed property bool checked: false property alias gesturePolicy: tap.gesturePolicy + property alias longPressThreshold: tap.longPressThreshold property point tappedPosition: Qt.point(0, 0) + property real timeHeldWhenTapped: 0 + property real timeHeldWhenLongPressed: 0 signal tapped signal canceled width: label.implicitWidth * 1.5; height: label.implicitHeight * 2.0 border.color: "#9f9d9a"; border.width: 1; radius: height / 4; antialiasing: true + function assignUndefinedLongPressThreshold() { + tap.longPressThreshold = undefined + } + gradient: Gradient { GradientStop { position: 0.0; color: tap.pressed ? "#b8b5b2" : "#efebe7" } GradientStop { position: 1.0; color: "#b8b5b2" } @@ -50,11 +32,17 @@ Rectangle { TapHandler { id: tap objectName: label.text - longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped - onTapped: { + onSingleTapped: console.log("Single tap") + onDoubleTapped: console.log("Double tap") + onTapped: (eventPoint, button) => { + console.log("Tapped", button, eventPoint) tapFlash.start() root.tappedPosition = point.scenePosition root.tapped() + root.timeHeldWhenTapped = tap.timeHeld // eventPoint.timeHeld is already 0 + } + onLongPressed: { + root.timeHeldWhenLongPressed = tap.timeHeld } onCanceled: root.canceled() } diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml index 158a02b7a6..9483a12d1c 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/FlashAnimation.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.0 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml index 4a8b8e1375..b36fcdef08 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml index 821b8073e9..04fbbc176b 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttons.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 @@ -34,19 +9,25 @@ Item { Button { objectName: "DragThreshold" label: "DragThreshold" - x: 10; y: 10; width: parent.width - 20; height: 40 + x: 10; y: 10; width: 300; height: 40 gesturePolicy: TapHandler.DragThreshold } Button { objectName: "WithinBounds" label: "WithinBounds" - x: 10; y: 60; width: parent.width - 20; height: 40 + x: 10; y: 60; width: 300; height: 40 gesturePolicy: TapHandler.WithinBounds } Button { objectName: "ReleaseWithinBounds" label: "ReleaseWithinBounds" - x: 10; y: 110; width: parent.width - 20; height: 40 + x: 10; y: 110; width: 300; height: 40 gesturePolicy: TapHandler.ReleaseWithinBounds } + Button { + objectName: "DragWithinBounds" + label: "DragWithinBounds" + x: 10; y: 160; width: 300; height: 40 + gesturePolicy: TapHandler.DragWithinBounds + } } diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml new file mode 100644 index 0000000000..5f60ef879e --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/dragReleaseMenu.qml @@ -0,0 +1,64 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick + +Item { + width: 480 + height: 480 + property alias feedbackText: feedback.text + + Rectangle { + id: rect + anchors.fill: parent; anchors.margins: 40 + color: tapHandler.active ? "lightgrey" : "darkgrey" + + TapHandler { + id: tapHandler + gesturePolicy: TapHandler.DragWithinBounds + onPressedChanged: if (pressed) { + menu.x = point.position.x - menu.width / 2 + menu.y = point.position.y - menu.height / 2 + } else { + if (menu.highlightedMenuItem !== "") + feedback.text = menu.highlightedMenuItem + } + onCanceled: feedback.text = "canceled" + } + + Column { + id: menu + visible: tapHandler.pressed + opacity: Math.min(1, tapHandler.timeHeld) + property string highlightedMenuItem: "" + Repeater { + model: [ "top", "middle", "bottom" ] + delegate: Rectangle { + property bool highlighted: tapHandler.pressed && + contains(mapFromItem(rect, tapHandler.point.position)) + onHighlightedChanged: { + if (highlighted) + menu.highlightedMenuItem = menuItemText.text + else if (menu.highlightedMenuItem === menuItemText.text) + menu.highlightedMenuItem = "" + } + width: 100 + height: 20 + color: highlighted ? "lightsteelblue" : "aliceblue" + Text { + id: menuItemText + anchors.centerIn: parent + text: modelData + } + } + } + } + + Text { + id: feedback + x: 6; y: 6 + textFormat: Text.MarkdownText + text: "hold for context menu" + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml new file mode 100644 index 0000000000..c0f0b10bf4 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nested.qml @@ -0,0 +1,35 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +import QtQuick 2.12 + +Item { + width: 300 + height: 200 + property var taps: [] + + Rectangle { + x: 25 + y: 25 + width: 200 + height: 100 + color: "salmon" + + TapHandler { + objectName: "parentTapHandler" + onTapped: taps.push(objectName) + } + + Rectangle { + x: 25 + y: 25 + width: 200 + height: 100 + color: "lightsteelblue" + + TapHandler { + objectName: "childTapHandler" + onTapped: taps.push(objectName) + } + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml new file mode 100644 index 0000000000..7732c42082 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/nestedAndSibling.qml @@ -0,0 +1,39 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 + +Item { + width: 360 + height: 280 + + Rectangle { + width: 200; height: 200; x: 100; y: 10 + color: th1.pressed ? "blue" : "lightblue" + + TapHandler { + id: th1 + objectName: "th1" + } + + Rectangle { + width: 200; height: 200; x: 50; y: 50 + color: th2.pressed ? "steelblue" : "lightsteelblue" + + TapHandler { + id: th2 + objectName: "th2" + } + } + } + + Rectangle { + width: 200; height: 200; x: 10; y: 50 + color: th3.pressed ? "goldenrod" : "beige" + + TapHandler { + id: th3 + objectName: "th3" + } + } +} + diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml index aea01c154c..85f5c87b39 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/rightTapHandler.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.12 diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml new file mode 100644 index 0000000000..026be48f83 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml @@ -0,0 +1,14 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.12 + +Item { + id: root + width: 100 + height: 100 + property int tapCount: 0 + TapHandler { + onTapped: { ++root.tapCount } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml deleted file mode 100644 index 8d2e36d921..0000000000 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/tapHandlersOverlapped.qml +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -import QtQuick 2.12 - -Item { - width: 300 - height: 200 - property var taps: [] - - Rectangle { - x: 25 - y: 25 - width: 200 - height: 100 - color: "salmon" - - TapHandler { - objectName: "parentTapHandler" - onTapped: taps.push(objectName) - } - - Rectangle { - x: 25 - y: 25 - width: 200 - height: 100 - color: "lightsteelblue" - - TapHandler { - objectName: "childTapHandler" - onTapped: taps.push(objectName) - } - } - } -} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp index 8457488cd9..1dab0836cd 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -41,8 +16,8 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlproperty.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -51,6 +26,7 @@ class tst_TapHandler : public QQmlDataTest Q_OBJECT public: tst_TapHandler() + : QQmlDataTest(QT_QMLTEST_DATADIR) {} private slots: @@ -63,33 +39,68 @@ private slots: void mouseGesturePolicyWithinBounds(); void touchGesturePolicyReleaseWithinBounds(); void mouseGesturePolicyReleaseWithinBounds(); + void gesturePolicyDragWithinBounds_data(); + void gesturePolicyDragWithinBounds(); void touchMultiTap(); + void mouseMultiTap_data(); void mouseMultiTap(); - void touchLongPress(); - void mouseLongPress(); + void mouseMultiTapLeftRight_data(); + void mouseMultiTapLeftRight(); + void singleTapDoubleTap_data(); + void singleTapDoubleTap(); + void longPress_data(); + void longPress(); void buttonsMultiTouch(); void componentUserBehavioralOverride(); void rightLongPressIgnoreWheel(); void negativeZStackingOrder(); + void nonTopLevelParentWindow(); + void nestedDoubleTap_data(); + void nestedDoubleTap(); + void nestedAndSiblingPropagation_data(); + void nestedAndSiblingPropagation(); private: - void createView(QScopedPointer<QQuickView> &window, const char *fileName); - QPointingDevice *touchDevice = QTest::createTouchDevice(); + void createView(QScopedPointer<QQuickView> &window, const char *fileName, + QWindow *parent = nullptr); + QPointingDevice *touchDevice = QTest::createTouchDevice(); // TODO const after fixing QTBUG-107864 + void mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point, + QWindow *targetWindow, QWindow *mapToWindow); }; -void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) +void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName, + QWindow *parent) { - window.reset(new QQuickView); + window.reset(new QQuickView(parent)); + if (parent) { + parent->show(); + QVERIFY(QTest::qWaitForWindowActive(parent)); + } + window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); - QQuickViewTestUtil::centerOnScreen(window.data()); - QQuickViewTestUtil::moveMouseAway(window.data()); + QQuickViewTestUtils::centerOnScreen(window.data()); + QQuickViewTestUtils::moveMouseAway(window.data()); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != nullptr); } +void tst_TapHandler::mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point, + QWindow *targetWindow, QWindow *mapToWindow) +{ + QVERIFY(targetWindow); + QVERIFY(mapToWindow); + auto buttons = button; + if (type == QEvent::MouseButtonRelease) { + buttons = Qt::NoButton; + } + QMouseEvent me(type, point, mapToWindow->mapToGlobal(point), button, buttons, + Qt::KeyboardModifiers(), QPointingDevice::primaryPointingDevice()); + QVERIFY(qApp->notify(targetWindow, &me)); +} + void tst_TapHandler::initTestCase() { // This test assumes that we don't get synthesized mouse events from QGuiApplication @@ -123,7 +134,7 @@ void tst_TapHandler::touchGesturePolicyDragThreshold() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); - QCOMPARE(dragThresholdTappedSpy.count(), 1); + QCOMPARE(dragThresholdTappedSpy.size(), 1); QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1); QCOMPARE(tapHandler->point().position(), QPointF()); @@ -144,7 +155,7 @@ void tst_TapHandler::touchGesturePolicyDragThreshold() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!buttonDragThreshold->property("pressed").toBool()); - QCOMPARE(dragThresholdTappedSpy.count(), 0); + QCOMPARE(dragThresholdTappedSpy.size(), 0); } void tst_TapHandler::mouseGesturePolicyDragThreshold() @@ -169,7 +180,7 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold() QVERIFY(buttonDragThreshold->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); - QTRY_COMPARE(dragThresholdTappedSpy.count(), 1); + QTRY_COMPARE(dragThresholdTappedSpy.size(), 1); QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1); QCOMPARE(tapHandler->point().position(), QPointF()); @@ -186,7 +197,7 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold() QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QVERIFY(!buttonDragThreshold->property("pressed").toBool()); - QCOMPARE(dragThresholdTappedSpy.count(), 0); + QCOMPARE(dragThresholdTappedSpy.size(), 0); } void tst_TapHandler::touchMouseGesturePolicyDragThreshold() @@ -206,8 +217,8 @@ void tst_TapHandler::touchMouseGesturePolicyDragThreshold() QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); QTest::mouseMove(window, p2); - QTRY_COMPARE(canceledSpy.count(), 1); - QCOMPARE(tappedSpy.count(), 0); + QTRY_COMPARE(canceledSpy.size(), 1); + QCOMPARE(tappedSpy.size(), 0); QCOMPARE(buttonDragThreshold->property("pressed").toBool(), false); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p2); @@ -218,7 +229,7 @@ void tst_TapHandler::touchMouseGesturePolicyDragThreshold() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 1); + QCOMPARE(tappedSpy.size(), 1); // Press touch, drag it outside the button, release QTest::touchEvent(window, touchDevice).press(1, p1, window); @@ -229,16 +240,16 @@ void tst_TapHandler::touchMouseGesturePolicyDragThreshold() QTRY_COMPARE(buttonDragThreshold->property("pressed").toBool(), false); QTest::touchEvent(window, touchDevice).release(1, p2, window); QQuickTouchUtils::flush(window); - QTRY_COMPARE(canceledSpy.count(), 2); - QCOMPARE(tappedSpy.count(), 1); // didn't increase + QTRY_COMPARE(canceledSpy.size(), 2); + QCOMPARE(tappedSpy.size(), 1); // didn't increase QCOMPARE(buttonDragThreshold->property("pressed").toBool(), false); // Press and release mouse, verify that it still works QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(tappedSpy.count(), 2); - QCOMPARE(canceledSpy.count(), 2); // didn't increase + QTRY_COMPARE(tappedSpy.size(), 2); + QCOMPARE(canceledSpy.size(), 2); // didn't increase QCOMPARE(buttonDragThreshold->property("pressed").toBool(), false); } @@ -264,7 +275,7 @@ void tst_TapHandler::touchGesturePolicyWithinBounds() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); - QCOMPARE(withinBoundsTappedSpy.count(), 1); + QCOMPARE(withinBoundsTappedSpy.size(), 1); // WithinBounds button is no longer pressed if touchpoint leaves bounds withinBoundsTappedSpy.clear(); @@ -279,7 +290,7 @@ void tst_TapHandler::touchGesturePolicyWithinBounds() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QVERIFY(!buttonWithinBounds->property("pressed").toBool()); - QCOMPARE(withinBoundsTappedSpy.count(), 0); + QCOMPARE(withinBoundsTappedSpy.size(), 0); } void tst_TapHandler::mouseGesturePolicyWithinBounds() @@ -301,7 +312,7 @@ void tst_TapHandler::mouseGesturePolicyWithinBounds() QVERIFY(buttonWithinBounds->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); - QCOMPARE(withinBoundsTappedSpy.count(), 1); + QCOMPARE(withinBoundsTappedSpy.size(), 1); // WithinBounds button is no longer pressed if touchpoint leaves bounds withinBoundsTappedSpy.clear(); @@ -313,7 +324,7 @@ void tst_TapHandler::mouseGesturePolicyWithinBounds() QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QVERIFY(!buttonWithinBounds->property("pressed").toBool()); - QCOMPARE(withinBoundsTappedSpy.count(), 0); + QCOMPARE(withinBoundsTappedSpy.size(), 0); } void tst_TapHandler::touchGesturePolicyReleaseWithinBounds() @@ -347,7 +358,7 @@ void tst_TapHandler::touchGesturePolicyReleaseWithinBounds() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); - QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + QCOMPARE(releaseWithinBoundsTappedSpy.size(), 1); // ReleaseWithinBounds button does not emit tapped if released out of bounds releaseWithinBoundsTappedSpy.clear(); @@ -362,7 +373,7 @@ void tst_TapHandler::touchGesturePolicyReleaseWithinBounds() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); - QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QCOMPARE(releaseWithinBoundsTappedSpy.size(), 0); } void tst_TapHandler::mouseGesturePolicyReleaseWithinBounds() @@ -391,7 +402,7 @@ void tst_TapHandler::mouseGesturePolicyReleaseWithinBounds() QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); - QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + QCOMPARE(releaseWithinBoundsTappedSpy.size(), 1); // ReleaseWithinBounds button does not emit tapped if released out of bounds releaseWithinBoundsTappedSpy.clear(); @@ -403,7 +414,52 @@ void tst_TapHandler::mouseGesturePolicyReleaseWithinBounds() QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_VERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); - QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QCOMPARE(releaseWithinBoundsTappedSpy.size(), 0); +} + +void tst_TapHandler::gesturePolicyDragWithinBounds_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + QTest::addColumn<QPoint>("dragStart"); + QTest::addColumn<QPoint>("dragDistance"); + QTest::addColumn<QString>("expectedFeedback"); + + const QPointingDevice *constTouchDevice = touchDevice; + + QTest::newRow("mouse: click") << QPointingDevice::primaryPointingDevice() << QPoint(200, 200) << QPoint(0, 0) << "middle"; + QTest::newRow("touch: tap") << constTouchDevice << QPoint(200, 200) << QPoint(0, 0) << "middle"; + QTest::newRow("mouse: drag up") << QPointingDevice::primaryPointingDevice() << QPoint(200, 200) << QPoint(0, -20) << "top"; + QTest::newRow("touch: drag up") << constTouchDevice << QPoint(200, 200) << QPoint(0, -20) << "top"; + QTest::newRow("mouse: drag out to cancel") << QPointingDevice::primaryPointingDevice() << QPoint(435, 200) << QPoint(10, 0) << "canceled"; + QTest::newRow("touch: drag out to cancel") << constTouchDevice << QPoint(435, 200) << QPoint(10, 0) << "canceled"; +} + +void tst_TapHandler::gesturePolicyDragWithinBounds() +{ + QFETCH(const QPointingDevice *, device); + QFETCH(QPoint, dragStart); + QFETCH(QPoint, dragDistance); + QFETCH(QString, expectedFeedback); + const bool expectedCanceled = expectedFeedback == "canceled"; + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("dragReleaseMenu.qml"))); + QQuickTapHandler *tapHandler = window.rootObject()->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandler); + QSignalSpy canceledSpy(tapHandler, &QQuickTapHandler::canceled); + + QQuickTest::pointerPress(device, &window, 0, dragStart); + QTRY_VERIFY(tapHandler->isPressed()); + QQuickTest::pointerMove(device, &window, 0, dragStart + dragDistance); + if (expectedCanceled) + QTRY_COMPARE(tapHandler->timeHeld(), -1); + else + QTRY_VERIFY(tapHandler->timeHeld() > 0.1); + QQuickTest::pointerRelease(device, &window, 0, dragStart + dragDistance); + + QCOMPARE(window.rootObject()->property("feedbackText"), expectedFeedback); + if (expectedCanceled) + QCOMPARE(canceledSpy.size(), 1); } void tst_TapHandler::touchMultiTap() @@ -425,7 +481,7 @@ void tst_TapHandler::touchMultiTap() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 1); + QCOMPARE(tappedSpy.size(), 1); // Tap again in exactly the same place (not likely with touch in the real world) QTest::touchEvent(window, touchDevice).press(1, p1, window); @@ -434,7 +490,7 @@ void tst_TapHandler::touchMultiTap() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 2); + QCOMPARE(tappedSpy.size(), 2); // Tap a third time, nearby p1 += QPoint(dragThreshold, dragThreshold); @@ -444,7 +500,7 @@ void tst_TapHandler::touchMultiTap() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 3); + QCOMPARE(tappedSpy.size(), 3); // Tap a fourth time, drifting farther away p1 += QPoint(dragThreshold, dragThreshold); @@ -454,11 +510,52 @@ void tst_TapHandler::touchMultiTap() QTest::touchEvent(window, touchDevice).release(1, p1, window); QQuickTouchUtils::flush(window); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 4); + QCOMPARE(tappedSpy.size(), 4); + + // Test a stray touch begin + tappedSpy.clear(); + constexpr int count = 2; + for (int i = 0; i < count; ++i) { + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + p1 -= QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).move(1, p1, window); + QQuickTouchUtils::flush(window); + QTest::touchEvent(window, touchDevice).release(1, p1, window); + QQuickTouchUtils::flush(window); + p1 += QPoint(dragThreshold, dragThreshold); + QTest::touchEvent(window, touchDevice).press(1, p1, window); + QQuickTouchUtils::flush(window); + } + QCOMPARE(tappedSpy.count(), count); +} + +void tst_TapHandler::mouseMultiTap_data() +{ + QTest::addColumn<QQuickTapHandler::ExclusiveSignals>("exclusiveSignals"); + QTest::addColumn<int>("expectedSingleTaps"); + QTest::addColumn<int>("expectedSingleTapsAfterMovingAway"); + QTest::addColumn<int>("expectedSingleTapsAfterWaiting"); + QTest::addColumn<int>("expectedDoubleTaps"); + + QTest::newRow("NotExclusive") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive) + << 1 << 2 << 3 << 1; + QTest::newRow("SingleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap) + << 1 << 2 << 3 << 0; + QTest::newRow("DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap) + << 0 << 0 << 0 << 1; + QTest::newRow("SingleTap|DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap) + << 0 << 0 << 0 << 0; } void tst_TapHandler::mouseMultiTap() { + QFETCH(QQuickTapHandler::ExclusiveSignals, exclusiveSignals); + QFETCH(int, expectedSingleTaps); + QFETCH(int, expectedSingleTapsAfterMovingAway); + QFETCH(int, expectedSingleTapsAfterWaiting); + QFETCH(int, expectedDoubleTaps); + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); QScopedPointer<QQuickView> windowPtr; createView(windowPtr, "buttons.qml"); @@ -466,110 +563,409 @@ void tst_TapHandler::mouseMultiTap() QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); QVERIFY(button); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandler); + tapHandler->setExclusiveSignals(exclusiveSignals); QSignalSpy tappedSpy(button, SIGNAL(tapped())); + QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped); + QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped); - // Tap once + // Click once QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_VERIFY(button->property("pressed").toBool()); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 1); - - // Tap again in exactly the same place (not likely with touch in the real world) - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10); QTRY_VERIFY(button->property("pressed").toBool()); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 2); - - // Tap a third time, nearby + QCOMPARE(tappedSpy.size(), 1); + // If exclusiveSignals == SingleTap | DoubleTap: + // This would be a single-click if we waited longer than the double-click interval, + // but it's too early for the signal at this moment; and we're going to click again. + // If exclusiveSignals == DoubleTap: singleTapped() won't happen. + // Otherwise: we got singleTapped() immediately. + QCOMPARE(singleTapSpy.size(), expectedSingleTaps); + QCOMPARE(tapHandler->timeHeld(), -1); + + // Click again in exactly the same place + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + QCOMPARE(tappedSpy.size(), 2); + QCOMPARE(singleTapSpy.size(), expectedSingleTaps); + QCOMPARE(doubleTapSpy.size(), expectedDoubleTaps); + + // Click a third time, nearby: that'll be a triple-click + p1 += QPoint(1, 1); + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + QCOMPARE(tappedSpy.size(), 3); + QCOMPARE(singleTapSpy.size(), expectedSingleTaps); + QCOMPARE(doubleTapSpy.size(), expectedDoubleTaps); + QCOMPARE(tapHandler->tapCount(), 3); + + // Click a fourth time, drifting farther away: treated as a separate click, regardless of timing p1 += QPoint(dragThreshold, dragThreshold); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_VERIFY(button->property("pressed").toBool()); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 3); + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1); // default delay to prevent double-click + QCOMPARE(tappedSpy.size(), 4); + QCOMPARE(tapHandler->tapCount(), 1); + QTRY_COMPARE(singleTapSpy.size(), expectedSingleTapsAfterMovingAway); + + // Click a fifth time later on at the same place: treated as a separate click + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1); + QCOMPARE(tappedSpy.size(), 5); + QCOMPARE(tapHandler->tapCount(), 1); + QCOMPARE(singleTapSpy.size(), expectedSingleTapsAfterWaiting); +} - // Tap a fourth time, drifting farther away - p1 += QPoint(dragThreshold, dragThreshold); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_VERIFY(button->property("pressed").toBool()); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 4); +void tst_TapHandler::mouseMultiTapLeftRight_data() +{ + QTest::addColumn<QQuickTapHandler::ExclusiveSignals>("exclusiveSignals"); + QTest::addColumn<int>("expectedSingleTaps"); + QTest::addColumn<int>("expectedDoubleTaps"); + QTest::addColumn<int>("expectedTabCount2"); + QTest::addColumn<int>("expectedTabCount3"); + + QTest::newRow("NotExclusive") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive) + << 3 << 0 << 1 << 1; + QTest::newRow("SingleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap) + << 3 << 0 << 1 << 1; + QTest::newRow("DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap) + << 0 << 0 << 1 << 1; + QTest::newRow("SingleTap|DoubleTap") << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap) + << 0 << 0 << 1 << 1; } -void tst_TapHandler::touchLongPress() +void tst_TapHandler::mouseMultiTapLeftRight() //QTBUG-111557 { + QFETCH(QQuickTapHandler::ExclusiveSignals, exclusiveSignals); + QFETCH(int, expectedSingleTaps); + QFETCH(int, expectedDoubleTaps); + QFETCH(int, expectedTabCount2); + QFETCH(int, expectedTabCount3); + QScopedPointer<QQuickView> windowPtr; createView(windowPtr, "buttons.qml"); QQuickView * window = windowPtr.data(); QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); QVERIFY(button); - QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold"); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(); QVERIFY(tapHandler); + tapHandler->setExclusiveSignals(exclusiveSignals); + tapHandler->setAcceptedButtons(Qt::LeftButton | Qt::RightButton); QSignalSpy tappedSpy(button, SIGNAL(tapped())); - QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); - QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); - QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); + QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped); + QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped); - // Reduce the threshold so that we can get a long press quickly - tapHandler->setLongPressThreshold(0.5); - QCOMPARE(longPressThresholdChangedSpy.count(), 1); + // Click once with the left button + QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10); - // Press and hold - QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); - QTest::touchEvent(window, touchDevice).press(1, p1, window); - QQuickTouchUtils::flush(window); - QTRY_VERIFY(button->property("pressed").toBool()); - QTRY_COMPARE(longPressedSpy.count(), 1); - timeHeldSpy.wait(); // the longer we hold it, the more this will occur - qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times"; - QVERIFY(timeHeldSpy.count() > 0); - QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere + // Click again with the right button -> should reset tabCount() + QTest::mousePress(window, Qt::RightButton, Qt::NoModifier, p1, 10); + QTest::mouseRelease(window, Qt::RightButton, Qt::NoModifier, p1, 10); - // Release and verify that tapped was not emitted - QTest::touchEvent(window, touchDevice).release(1, p1, window); - QQuickTouchUtils::flush(window); - QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(tapHandler->tapCount(), expectedTabCount2); + + // Click again with the left button -> should reset tabCount() + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 10); + + QCOMPARE(tapHandler->tapCount(), expectedTabCount3); + QCOMPARE(singleTapSpy.size(), expectedSingleTaps); + QCOMPARE(doubleTapSpy.size(), expectedDoubleTaps); +} + +void tst_TapHandler::singleTapDoubleTap_data() +{ + QTest::addColumn<QPointingDevice::DeviceType>("deviceType"); + QTest::addColumn<QQuickTapHandler::ExclusiveSignals>("exclusiveSignals"); + QTest::addColumn<int>("expectedEndingSingleTapCount"); + QTest::addColumn<int>("expectedDoubleTapCount"); + + QTest::newRow("mouse:NotExclusive") + << QPointingDevice::DeviceType::Mouse + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive) + << 1 << 1; + QTest::newRow("mouse:SingleTap") + << QPointingDevice::DeviceType::Mouse + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap) + << 1 << 0; + QTest::newRow("mouse:DoubleTap") + << QPointingDevice::DeviceType::Mouse + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap) + << 0 << 1; + QTest::newRow("mouse:SingleTap|DoubleTap") + << QPointingDevice::DeviceType::Mouse + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap) + << 0 << 1; + QTest::newRow("touch:NotExclusive") + << QPointingDevice::DeviceType::TouchScreen + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::NotExclusive) + << 1 << 1; + QTest::newRow("touch:SingleTap") + << QPointingDevice::DeviceType::TouchScreen + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap) + << 1 << 0; + QTest::newRow("touch:DoubleTap") + << QPointingDevice::DeviceType::TouchScreen + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::DoubleTap) + << 0 << 1; + QTest::newRow("touch:SingleTap|DoubleTap") + << QPointingDevice::DeviceType::TouchScreen + << QQuickTapHandler::ExclusiveSignals(QQuickTapHandler::SingleTap | QQuickTapHandler::DoubleTap) + << 0 << 1; } -void tst_TapHandler::mouseLongPress() +void tst_TapHandler::singleTapDoubleTap() { + QFETCH(QPointingDevice::DeviceType, deviceType); + QFETCH(QQuickTapHandler::ExclusiveSignals, exclusiveSignals); + QFETCH(int, expectedEndingSingleTapCount); + QFETCH(int, expectedDoubleTapCount); + QScopedPointer<QQuickView> windowPtr; createView(windowPtr, "buttons.qml"); QQuickView * window = windowPtr.data(); QQuickItem *button = window->rootObject()->findChild<QQuickItem*>("DragThreshold"); QVERIFY(button); - QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>("DragThreshold"); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(); + QVERIFY(tapHandler); + tapHandler->setExclusiveSignals(exclusiveSignals); + QSignalSpy tappedSpy(tapHandler, &QQuickTapHandler::tapped); + QSignalSpy singleTapSpy(tapHandler, &QQuickTapHandler::singleTapped); + QSignalSpy doubleTapSpy(tapHandler, &QQuickTapHandler::doubleTapped); + + auto tap = [window, tapHandler, deviceType, this](const QPoint &p1, int delay = 10) { + switch (static_cast<QPointingDevice::DeviceType>(deviceType)) { + case QPointingDevice::DeviceType::Mouse: + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, p1, delay); + break; + case QPointingDevice::DeviceType::TouchScreen: + QTest::qWait(delay); + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QTRY_VERIFY(tapHandler->isPressed()); + QTest::touchEvent(window, touchDevice).release(0, p1, window); + break; + default: + break; + } + }; + + // tap once + const QPoint p1 = button->mapToScene(QPointF(2, 2)).toPoint(); + tap(p1); + QCOMPARE(tappedSpy.size(), 1); + QCOMPARE(doubleTapSpy.size(), 0); + + // tap again immediately afterwards + tap(p1); + QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount); + QCOMPARE(tappedSpy.size(), 2); + QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount); + + // wait past the double-tap interval, then do it again + const auto delay = qApp->styleHints()->mouseDoubleClickInterval() + 10; + tappedSpy.clear(); + singleTapSpy.clear(); + doubleTapSpy.clear(); + + // tap once with delay + tap(p1, delay); + QCOMPARE(tappedSpy.size(), 1); + QCOMPARE(doubleTapSpy.size(), 0); + + // tap again immediately afterwards + tap(p1); + QTRY_COMPARE(doubleTapSpy.size(), expectedDoubleTapCount); + QCOMPARE(tappedSpy.size(), 2); + QCOMPARE(singleTapSpy.size(), expectedEndingSingleTapCount); +} + +void tst_TapHandler::longPress_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + QTest::addColumn<QString>("buttonName"); + QTest::addColumn<qreal>("longPressThreshold"); + QTest::addColumn<QPoint>("releaseOffset"); + QTest::addColumn<bool>("expectLongPress"); + QTest::addColumn<bool>("expectTapped"); + + const QPointingDevice *constTouchDevice = touchDevice; + + // Reduce the threshold so that we can get a long press quickly (faster in CI) + const qreal longPressThreshold = 0.3; + QTest::newRow("mouse, lpt longPressThreshold: DragThreshold") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: DragThreshold") + << constTouchDevice << "DragThreshold" << longPressThreshold + << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: DragThreshold, drag") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << longPressThreshold << QPoint(50, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: DragThreshold, drag") + << constTouchDevice << "DragThreshold" + << longPressThreshold << QPoint(50, 0) << false << false; + + QTest::newRow("mouse, lpt longPressThreshold: WithinBounds") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: WithinBounds") + << constTouchDevice << "WithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: WithinBounds, drag") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: WithinBounds, drag") + << constTouchDevice << "WithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: WithinBounds, drag out") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: WithinBounds, drag out") + << constTouchDevice << "WithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + + QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds") + << constTouchDevice << "ReleaseWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds, drag") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds, drag") + << constTouchDevice << "ReleaseWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: ReleaseWithinBounds, drag out") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: ReleaseWithinBounds, drag out") + << constTouchDevice << "ReleaseWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + + QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds") + << constTouchDevice << "DragWithinBounds" + << longPressThreshold << QPoint(0, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds, drag") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds, drag") + << constTouchDevice << "DragWithinBounds" + << longPressThreshold << QPoint(50, 0) << true << false; + QTest::newRow("mouse, lpt longPressThreshold: DragWithinBounds, drag out") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + QTest::newRow("touch, lpt longPressThreshold: DragWithinBounds, drag out") + << constTouchDevice << "DragWithinBounds" + << longPressThreshold << QPoint(155, 0) << false << false; + + // Zero or negative threshold means long press is disabled + QTest::newRow("mouse, lpt 0: DragThreshold") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: DragThreshold") + << QPointingDevice::primaryPointingDevice() << "DragThreshold" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: DragThreshold") + << constTouchDevice << "DragThreshold" + << qreal(0) << QPoint(0, 0) << false << true; + + QTest::newRow("mouse, lpt 0: WithinBounds") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: WithinBounds") + << QPointingDevice::primaryPointingDevice() << "WithinBounds" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: WithinBounds") + << constTouchDevice << "WithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + + QTest::newRow("mouse, lpt 0: ReleaseWithinBounds") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: ReleaseWithinBounds") + << QPointingDevice::primaryPointingDevice() << "ReleaseWithinBounds" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: ReleaseWithinBounds") + << constTouchDevice << "ReleaseWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + + QTest::newRow("mouse, lpt 0: DragWithinBounds") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; + QTest::newRow("mouse, lpt -1: DragWithinBounds") + << QPointingDevice::primaryPointingDevice() << "DragWithinBounds" + << qreal(-1) << QPoint(0, 0) << true << false; + QTest::newRow("touch, lpt 0: DragWithinBounds") + << constTouchDevice << "DragWithinBounds" + << qreal(0) << QPoint(0, 0) << false << true; +} + +void tst_TapHandler::longPress() +{ + QFETCH(const QPointingDevice *, device); + QFETCH(QString, buttonName); + QFETCH(qreal, longPressThreshold); + QFETCH(QPoint, releaseOffset); + QFETCH(bool, expectLongPress); + QFETCH(bool, expectTapped); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("buttons.qml"))); + + QQuickItem *button = window.rootObject()->findChild<QQuickItem*>(buttonName); + QVERIFY(button); + QQuickTapHandler *tapHandler = button->findChild<QQuickTapHandler*>(buttonName); QVERIFY(tapHandler); QSignalSpy tappedSpy(button, SIGNAL(tapped())); QSignalSpy longPressThresholdChangedSpy(tapHandler, SIGNAL(longPressThresholdChanged())); QSignalSpy timeHeldSpy(tapHandler, SIGNAL(timeHeldChanged())); QSignalSpy longPressedSpy(tapHandler, SIGNAL(longPressed())); - // Reduce the threshold so that we can get a long press quickly - tapHandler->setLongPressThreshold(0.5); - QCOMPARE(longPressThresholdChangedSpy.count(), 1); + const qreal defaultThreshold = tapHandler->longPressThreshold(); + qsizetype changedCount = 0; + QCOMPARE_GT(defaultThreshold, 0); + tapHandler->setLongPressThreshold(longPressThreshold); + if (longPressThreshold > 0) + QCOMPARE(longPressThresholdChangedSpy.size(), ++changedCount); + QVERIFY(QMetaObject::invokeMethod(button, "assignUndefinedLongPressThreshold")); + if (longPressThreshold > 0) + QCOMPARE(longPressThresholdChangedSpy.size(), ++changedCount); + QCOMPARE(tapHandler->longPressThreshold(), defaultThreshold); + tapHandler->setLongPressThreshold(longPressThreshold); // Press and hold QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); - QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QQuickTest::pointerPress(device, &window, 1, p1); QTRY_VERIFY(button->property("pressed").toBool()); - QTRY_COMPARE(longPressedSpy.count(), 1); + QTRY_COMPARE(longPressedSpy.size(), expectLongPress ? 1 : 0); timeHeldSpy.wait(); // the longer we hold it, the more this will occur - qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.count() << "times"; - QVERIFY(timeHeldSpy.count() > 0); - QVERIFY(tapHandler->timeHeld() > 0.4); // Should be > 0.5 but slow CI and timer granularity can interfere + qDebug() << "held" << tapHandler->timeHeld() << "secs; timeHeld updated" << timeHeldSpy.size() << "times"; + QCOMPARE_GT(timeHeldSpy.size(), 0); + if (expectLongPress) { + // Should be > longPressThreshold but slow CI and timer granularity can interfere + QCOMPARE_GT(tapHandler->timeHeld(), longPressThreshold - 0.1); + } else { + // Should be quite small, but event delivery is not instantaneous + QCOMPARE_LT(tapHandler->timeHeld(), 0.3); + } + + // If we have an offset, we need a move between press and release for realistic simulation + if (!releaseOffset.isNull()) + QQuickTest::pointerMove(device, &window, 1, p1 + releaseOffset); - // Release and verify that tapped was not emitted - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1, 500); + // Release (optionally at an offset) and check whether tapped was emitted + QQuickTest::pointerRelease(device, &window, 1, p1 + releaseOffset); QTRY_VERIFY(!button->property("pressed").toBool()); - QCOMPARE(tappedSpy.count(), 0); + if (expectLongPress) + QCOMPARE_GT(button->property("timeHeldWhenLongPressed").toReal(), longPressThreshold - 0.1); + QCOMPARE(tapHandler->timeHeld(), -1); + QCOMPARE(tappedSpy.size(), expectTapped ? 1 : 0); + QCOMPARE(longPressedSpy.size(), expectLongPress ? 1 : 0); } void tst_TapHandler::buttonsMultiTouch() @@ -631,11 +1027,11 @@ void tst_TapHandler::buttonsMultiTouch() touchSeq.stationary(2).stationary(3).release(1, p1, window).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool()); - QCOMPARE(dragThresholdTappedSpy.count(), 1); + QCOMPARE(dragThresholdTappedSpy.size(), 1); QVERIFY(buttonWithinBounds->property("pressed").toBool()); - QCOMPARE(withinBoundsTappedSpy.count(), 0); + QCOMPARE(withinBoundsTappedSpy.size(), 0); QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); - QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QCOMPARE(releaseWithinBoundsTappedSpy.size(), 0); touchSeq.stationary(2).stationary(3).press(1, p1, window).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); @@ -646,11 +1042,11 @@ void tst_TapHandler::buttonsMultiTouch() touchSeq.stationary(1).stationary(3).release(2, p2, window).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(!buttonWithinBounds->property("pressed").toBool()); - QCOMPARE(withinBoundsTappedSpy.count(), 1); + QCOMPARE(withinBoundsTappedSpy.size(), 1); QVERIFY(buttonDragThreshold->property("pressed").toBool()); - QCOMPARE(dragThresholdTappedSpy.count(), 1); + QCOMPARE(dragThresholdTappedSpy.size(), 1); QVERIFY(buttonReleaseWithinBounds->property("pressed").toBool()); - QCOMPARE(releaseWithinBoundsTappedSpy.count(), 0); + QCOMPARE(releaseWithinBoundsTappedSpy.size(), 0); touchSeq.stationary(1).stationary(3).press(2, p2, window).commit(); QQuickTouchUtils::flush(window); QVERIFY(buttonDragThreshold->property("pressed").toBool()); @@ -660,11 +1056,11 @@ void tst_TapHandler::buttonsMultiTouch() // can release bottom button and press again: others stay pressed the whole time touchSeq.stationary(1).stationary(2).release(3, p3, window).commit(); QQuickTouchUtils::flush(window); - QCOMPARE(releaseWithinBoundsTappedSpy.count(), 1); + QCOMPARE(releaseWithinBoundsTappedSpy.size(), 1); QVERIFY(buttonWithinBounds->property("pressed").toBool()); - QCOMPARE(withinBoundsTappedSpy.count(), 1); + QCOMPARE(withinBoundsTappedSpy.size(), 1); QVERIFY(!buttonReleaseWithinBounds->property("pressed").toBool()); - QCOMPARE(dragThresholdTappedSpy.count(), 1); + QCOMPARE(dragThresholdTappedSpy.size(), 1); touchSeq.stationary(1).stationary(2).press(3, p3, window).commit(); QQuickTouchUtils::flush(window); QTRY_VERIFY(buttonDragThreshold->property("pressed").toBool()); @@ -685,26 +1081,26 @@ void tst_TapHandler::componentUserBehavioralOverride() QQuickTapHandler *userTapHandler = button->findChild<QQuickTapHandler*>("override"); QVERIFY(userTapHandler); QSignalSpy tappedSpy(button, SIGNAL(tapped())); - QSignalSpy innerGrabChangedSpy(innerTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint))); - QSignalSpy userGrabChangedSpy(userTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition, QEventPoint))); + QSignalSpy innerGrabChangedSpy(innerTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint))); + QSignalSpy userGrabChangedSpy(userTapHandler, SIGNAL(grabChanged(QPointingDevice::GrabTransition,QEventPoint))); QSignalSpy innerPressedChangedSpy(innerTapHandler, SIGNAL(pressedChanged())); QSignalSpy userPressedChangedSpy(userTapHandler, SIGNAL(pressedChanged())); // Press QPoint p1 = button->mapToScene(button->clipRect().center()).toPoint(); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(userPressedChangedSpy.count(), 1); - QCOMPARE(innerPressedChangedSpy.count(), 0); - QCOMPARE(innerGrabChangedSpy.count(), 0); - QCOMPARE(userGrabChangedSpy.count(), 1); + QTRY_COMPARE(userPressedChangedSpy.size(), 1); + QCOMPARE(innerPressedChangedSpy.size(), 0); + QCOMPARE(innerGrabChangedSpy.size(), 0); + QCOMPARE(userGrabChangedSpy.size(), 1); // Release QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(userPressedChangedSpy.count(), 2); - QCOMPARE(innerPressedChangedSpy.count(), 0); - QCOMPARE(tappedSpy.count(), 1); // only because the override handler makes that happen - QCOMPARE(innerGrabChangedSpy.count(), 0); - QCOMPARE(userGrabChangedSpy.count(), 2); + QTRY_COMPARE(userPressedChangedSpy.size(), 2); + QCOMPARE(innerPressedChangedSpy.size(), 0); + QCOMPARE(tappedSpy.size(), 1); // only because the override handler makes that happen + QCOMPARE(innerGrabChangedSpy.size(), 0); + QCOMPARE(userGrabChangedSpy.size(), 2); } void tst_TapHandler::rightLongPressIgnoreWheel() @@ -715,8 +1111,8 @@ void tst_TapHandler::rightLongPressIgnoreWheel() QQuickTapHandler *tap = window->rootObject()->findChild<QQuickTapHandler*>(); QVERIFY(tap); - QSignalSpy tappedSpy(tap, SIGNAL(tapped(QEventPoint *))); - QSignalSpy longPressedSpy(tap, SIGNAL(longPressed())); + QSignalSpy tappedSpy(tap, &QQuickTapHandler::tapped); + QSignalSpy longPressedSpy(tap, &QQuickTapHandler::longPressed); QPoint p1(100, 100); // Mouse wheel with ScrollBegin phase (because as soon as two fingers are touching @@ -735,20 +1131,20 @@ void tst_TapHandler::rightLongPressIgnoreWheel() QWheelEvent wheelEvent(p1, p1, QPoint(0, 0), QPoint(0, 0), Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false, Qt::MouseEventNotSynthesized); QGuiApplication::sendEvent(window, &wheelEvent); - QTRY_COMPARE(longPressedSpy.count(), 1); + QTRY_COMPARE(longPressedSpy.size(), 1); QCOMPARE(tap->isPressed(), true); - QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(tappedSpy.size(), 0); // Release QTest::mouseRelease(window, Qt::RightButton, Qt::NoModifier, p1, 500); QTRY_COMPARE(tap->isPressed(), false); - QCOMPARE(tappedSpy.count(), 0); + QCOMPARE(tappedSpy.size(), 0); } void tst_TapHandler::negativeZStackingOrder() // QTBUG-83114 { QScopedPointer<QQuickView> windowPtr; - createView(windowPtr, "tapHandlersOverlapped.qml"); + createView(windowPtr, "nested.qml"); QQuickView *window = windowPtr.data(); QQuickItem *root = window->rootObject(); @@ -760,8 +1156,8 @@ void tst_TapHandler::negativeZStackingOrder() // QTBUG-83114 QSignalSpy clickSpyChild(childTapHandler, &QQuickTapHandler::tapped); QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 100)); - QCOMPARE(clickSpyChild.count(), 1); - QCOMPARE(clickSpyParent.count(), 1); + QCOMPARE(clickSpyChild.size(), 1); + QCOMPARE(clickSpyParent.size(), 1); auto order = root->property("taps").toList(); QVERIFY(order.at(0) == "childTapHandler"); QVERIFY(order.at(1) == "parentTapHandler"); @@ -770,13 +1166,129 @@ void tst_TapHandler::negativeZStackingOrder() // QTBUG-83114 childTapHandler->parentItem()->setZ(-1); root->setProperty("taps", QVariantList()); QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 100)); - QCOMPARE(clickSpyChild.count(), 2); - QCOMPARE(clickSpyParent.count(), 2); + QCOMPARE(clickSpyChild.size(), 2); + QCOMPARE(clickSpyParent.size(), 2); order = root->property("taps").toList(); QVERIFY(order.at(0) == "parentTapHandler"); QVERIFY(order.at(1) == "childTapHandler"); } +void tst_TapHandler::nonTopLevelParentWindow() // QTBUG-91716 +{ + QScopedPointer<QQuickWindow> parentWindowPtr(new QQuickWindow); + auto parentWindow = parentWindowPtr.get(); + parentWindow->setGeometry(400, 400, 250, 250); + + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "simpleTapHandler.qml", parentWindow); + auto window = windowPtr.get(); + window->setGeometry(10, 10, 100, 100); + + QQuickItem *root = window->rootObject(); + + auto p1 = QPoint(20, 20); + mouseEvent(QEvent::MouseButtonPress, Qt::LeftButton, p1, window, parentWindow); + mouseEvent(QEvent::MouseButtonRelease, Qt::LeftButton, p1, window, parentWindow); + + QCOMPARE(root->property("tapCount").toInt(), 1); + + QTest::touchEvent(window, touchDevice).press(0, p1, parentWindow).commit(); + QTest::touchEvent(window, touchDevice).release(0, p1, parentWindow).commit(); + + QCOMPARE(root->property("tapCount").toInt(), 2); +} + +void tst_TapHandler::nestedDoubleTap_data() +{ + QTest::addColumn<QQuickTapHandler::GesturePolicy>("childGesturePolicy"); + + QTest::newRow("DragThreshold") << QQuickTapHandler::GesturePolicy::DragThreshold; + QTest::newRow("WithinBounds") << QQuickTapHandler::GesturePolicy::WithinBounds; + QTest::newRow("ReleaseWithinBounds") << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds; + QTest::newRow("DragWithinBounds") << QQuickTapHandler::GesturePolicy::DragWithinBounds; +} + +void tst_TapHandler::nestedDoubleTap() // QTBUG-102625 +{ + QFETCH(QQuickTapHandler::GesturePolicy, childGesturePolicy); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("nested.qml"))); + QQuickItem *root = window.rootObject(); + QQuickTapHandler *parentTapHandler = root->findChild<QQuickTapHandler*>("parentTapHandler"); + QVERIFY(parentTapHandler); + QSignalSpy parentSpy(parentTapHandler, &QQuickTapHandler::doubleTapped); + QQuickTapHandler *childTapHandler = root->findChild<QQuickTapHandler*>("childTapHandler"); + QVERIFY(childTapHandler); + QSignalSpy childSpy(childTapHandler, &QQuickTapHandler::doubleTapped); + childTapHandler->setGesturePolicy(childGesturePolicy); + + QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(150, 100)); + + QCOMPARE(childSpy.size(), 1); + // If the child gets by with a passive grab, both handlers see tap and double-tap. + // If the child takes an exclusive grab and stops event propagation, the parent doesn't see them. + QCOMPARE(parentSpy.size(), + childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 1 : 0); + QCOMPARE(root->property("taps").toList().size(), + childGesturePolicy == QQuickTapHandler::GesturePolicy::DragThreshold ? 4 : 2); +} + +void tst_TapHandler::nestedAndSiblingPropagation_data() +{ + QTest::addColumn<const QPointingDevice *>("device"); + QTest::addColumn<QQuickTapHandler::GesturePolicy>("gesturePolicy"); + QTest::addColumn<bool>("expectPropagation"); + + const QPointingDevice *constTouchDevice = touchDevice; + + QTest::newRow("primary, DragThreshold") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::DragThreshold << true; + QTest::newRow("primary, WithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::WithinBounds << false; + QTest::newRow("primary, ReleaseWithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false; + QTest::newRow("primary, DragWithinBounds") << QPointingDevice::primaryPointingDevice() + << QQuickTapHandler::GesturePolicy::DragWithinBounds << false; + + QTest::newRow("touch, DragThreshold") << constTouchDevice + << QQuickTapHandler::GesturePolicy::DragThreshold << true; + QTest::newRow("touch, WithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::WithinBounds << false; + QTest::newRow("touch, ReleaseWithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::ReleaseWithinBounds << false; + QTest::newRow("touch, DragWithinBounds") << constTouchDevice + << QQuickTapHandler::GesturePolicy::DragWithinBounds << false; +} + +void tst_TapHandler::nestedAndSiblingPropagation() // QTBUG-117387 +{ + QFETCH(const QPointingDevice *, device); + QFETCH(QQuickTapHandler::GesturePolicy, gesturePolicy); + QFETCH(bool, expectPropagation); + + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("nestedAndSibling.qml"))); + QQuickItem *root = window.rootObject(); + QQuickTapHandler *th1 = root->findChild<QQuickTapHandler*>("th1"); + QVERIFY(th1); + th1->setGesturePolicy(gesturePolicy); + QQuickTapHandler *th2 = root->findChild<QQuickTapHandler*>("th2"); + QVERIFY(th2); + th2->setGesturePolicy(gesturePolicy); + QQuickTapHandler *th3 = root->findChild<QQuickTapHandler*>("th3"); + QVERIFY(th3); + th3->setGesturePolicy(gesturePolicy); + + QPoint middle(180, 140); + QQuickTest::pointerPress(device, &window, 0, middle); + QVERIFY(th3->isPressed()); // it's on top + QCOMPARE(th2->isPressed(), expectPropagation); + QCOMPARE(th1->isPressed(), expectPropagation); + + QQuickTest::pointerRelease(device, &window, 0, middle); +} + QTEST_MAIN(tst_TapHandler) #include "tst_qquicktaphandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt b/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt index 588fd8f2e2..d50181dfe4 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/CMakeLists.txt @@ -1,9 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Generated from qquickwheelhandler.pro. ##################################################################### ## tst_qquickwheelhandler Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qquickwheelhandler LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -12,21 +21,14 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qquickwheelhandler SOURCES - ../../../shared/util.cpp ../../../shared/util.h - ../../shared/geometrytestutil.cpp ../../shared/geometrytestutil.h - ../../shared/viewtestutil.cpp ../../shared/viewtestutil.h - ../../shared/visualtestutil.cpp ../../shared/visualtestutil.h tst_qquickwheelhandler.cpp - DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" - INCLUDE_DIRECTORIES - ../../../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::QmlPrivate Qt::QuickPrivate + Qt::QuickTestUtilsPrivate TESTDATA ${test_data} ) @@ -38,10 +40,10 @@ qt_internal_add_test(tst_qquickwheelhandler qt_internal_extend_target(tst_qquickwheelhandler CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + QT_QMLTEST_DATADIR=":/data" ) qt_internal_extend_target(tst_qquickwheelhandler CONDITION NOT ANDROID AND NOT IOS DEFINES - QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml index 49e44f2b1f..9c43df46da 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.14 diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml index d4875d5313..ca6053ebcf 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.14 @@ -39,7 +14,23 @@ Rectangle { } } + Rectangle { + color: "red" + width: 6; height: 6; radius: 3 + x: wheelHandler.point.position.x - radius + y: wheelHandler.point.position.y - radius + } + + Text { + anchors.centerIn: parent + anchors.verticalCenterOffset: 20 + color: "white" + font.pixelSize: 18 + text: parent.x.toFixed(2) + ", " + parent.y.toFixed(2) + } + WheelHandler { + id: wheelHandler activeTimeout: 0.5 } } diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp index 2abf2ea8c3..c88b1af2ea 100644 --- a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtTest/QSignalSpy> @@ -34,8 +9,8 @@ #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/qquickview.h> #include <QtQml/qqmlcontext.h> -#include "../../../shared/util.h" -#include "../../shared/viewtestutil.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/viewtestutils_p.h> Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") @@ -43,13 +18,15 @@ class tst_QQuickWheelHandler: public QQmlDataTest { Q_OBJECT public: - tst_QQuickWheelHandler() { } + tst_QQuickWheelHandler() : QQmlDataTest(QT_QMLTEST_DATADIR) { } private slots: void singleHandler_data(); void singleHandler(); void nestedHandler_data(); void nestedHandler(); + void blocking_data(); + void blocking(); private: void sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, @@ -188,13 +165,13 @@ void tst_QQuickWheelHandler::singleHandler() sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::NoScrollPhase, eventInverted); } QCOMPARE(rect->position().toPoint(), expectedPosition); - QCOMPARE(activeChangedSpy.count(), 1); + QCOMPARE(activeChangedSpy.size(), 1); QCOMPARE(handler->active(), true); QCOMPARE(rect->scale(), expectedScale); QCOMPARE(rect->rotation(), expectedRotation); if (!eventPhases) { QTRY_COMPARE(handler->active(), false); - QCOMPARE(activeChangedSpy.count(), 2); + QCOMPARE(activeChangedSpy.size(), 2); } // restore by rotating backwards @@ -204,7 +181,7 @@ void tst_QQuickWheelHandler::singleHandler() } else { sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::NoScrollPhase, eventInverted); } - QCOMPARE(activeChangedSpy.count(), eventPhases ? 2 : 3); + QCOMPARE(activeChangedSpy.size(), eventPhases ? 2 : 3); QCOMPARE(handler->active(), !eventPhases); QCOMPARE(rect->position().toPoint(), QPoint(0, 0)); QCOMPARE(rect->scale(), 1); @@ -329,16 +306,61 @@ void tst_QQuickWheelHandler::nestedHandler() QCOMPARE(innerRect->scale(), innerScale); QCOMPARE(innerRect->rotation(), innerRotation); QCOMPARE(outerRect->position().toPoint(), outerPosition); - QCOMPARE(outerActiveChangedSpy.count(), 1); + QCOMPARE(outerActiveChangedSpy.size(), 1); QCOMPARE(outerHandler->active(), true); QCOMPARE(outerRect->scale(), outerScale); QCOMPARE(outerRect->rotation(), outerRotation); if (!eventPhases) { QTRY_COMPARE(outerHandler->active(), false); - QCOMPARE(outerActiveChangedSpy.count(), 2); + QCOMPARE(outerActiveChangedSpy.size(), 2); } } +void tst_QQuickWheelHandler::blocking_data() +{ + // handler properties + QTest::addColumn<bool>("blocking"); + + // move the item + QTest::newRow("default: blocking") << true; + QTest::newRow("nonblocking") << false; +} + +void tst_QQuickWheelHandler::blocking() +{ + QFETCH(bool, blocking); + + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("nested.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickItem *outerRect = window.rootObject(); + QVERIFY(outerRect != nullptr); + QQuickWheelHandler *outerHandler = outerRect->findChild<QQuickWheelHandler*>("outerWheelHandler"); + QVERIFY(outerHandler != nullptr); + QQuickWheelHandler *innerHandler = outerRect->findChild<QQuickWheelHandler*>("innerWheelHandler"); + QVERIFY(innerHandler != nullptr); + QQuickItem *innerRect = innerHandler->parentItem(); + QVERIFY(innerRect != nullptr); + QPoint eventPos = innerRect->mapToScene(innerRect->boundingRect().center()).toPoint(); + + QCOMPARE(innerHandler->isBlocking(), true); // default property value + innerHandler->setBlocking(blocking); + QSignalSpy innerActiveChangedSpy(innerHandler, SIGNAL(activeChanged())); + QSignalSpy outerActiveChangedSpy(outerHandler, SIGNAL(activeChanged())); + + qreal innerPosWas = innerRect->position().x(); + qreal outerPosWas = outerRect->position().x(); + + sendWheelEvent(window, eventPos, {0, 120}, {0, 0}, Qt::NoModifier, Qt::NoScrollPhase, false); + QTRY_COMPARE(innerActiveChangedSpy.size(), 2); + QCOMPARE(innerRect->position().x(), innerPosWas + 15); + QCOMPARE(outerActiveChangedSpy.size(), blocking ? 0 : 2); + QCOMPARE(outerRect->position().x(), outerPosWas + (blocking ? 0 : 15)); +} + QTEST_MAIN(tst_QQuickWheelHandler) #include "tst_qquickwheelhandler.moc" |