aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quickcontrols2/basic/CMakeLists.txt4
-rw-r--r--src/quickcontrols2/basic/SelectionRectangle.qml61
-rw-r--r--src/quickcontrols2/basic/basic.pri1
-rw-r--r--src/quickcontrols2/doc/images/qtquickcontrols2-selectionrectangle.pngbin0 -> 52957 bytes
-rw-r--r--src/quickcontrols2/doc/snippets/selectionrectangle.qml95
-rw-r--r--src/quicktemplates2/CMakeLists.txt2
-rw-r--r--src/quicktemplates2/qquickselectionrectangle.cpp540
-rw-r--r--src/quicktemplates2/qquickselectionrectangle_p.h143
-rw-r--r--src/quicktemplates2/qquickselectionrectangle_p_p.h110
-rw-r--r--src/quicktemplates2/quicktemplates2.pri2
-rw-r--r--tests/auto/controls/data/tst_selectionrectangle.qml311
11 files changed, 1269 insertions, 0 deletions
diff --git a/src/quickcontrols2/basic/CMakeLists.txt b/src/quickcontrols2/basic/CMakeLists.txt
index 0067a53f..03e964f5 100644
--- a/src/quickcontrols2/basic/CMakeLists.txt
+++ b/src/quickcontrols2/basic/CMakeLists.txt
@@ -42,6 +42,7 @@ set(qml_files
"ScrollBar.qml"
"ScrollIndicator.qml"
"ScrollView.qml"
+ "SelectionRectangle.qml"
"Slider.qml"
"SpinBox.qml"
"SplitView.qml"
@@ -178,6 +179,9 @@ set_source_files_properties(ScrollIndicator.qml PROPERTIES
set_source_files_properties(ScrollView.qml PROPERTIES
QT_QML_SOURCE_VERSION "2.2;6.0"
)
+set_source_files_properties(SelectionRectangle.qml PROPERTIES
+ QT_QML_SOURCE_VERSION "6.2"
+)
set_source_files_properties(Slider.qml PROPERTIES
QT_QML_SOURCE_VERSION "2.0;6.0"
)
diff --git a/src/quickcontrols2/basic/SelectionRectangle.qml b/src/quickcontrols2/basic/SelectionRectangle.qml
new file mode 100644
index 00000000..060fb909
--- /dev/null
+++ b/src/quickcontrols2/basic/SelectionRectangle.qml
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Shapes
+import QtQuick.Templates as T
+
+T.SelectionRectangle {
+ id: control
+
+ topLeftHandle: Handle {}
+ bottomRightHandle: Handle {}
+
+ component Handle : Rectangle {
+ id: handle
+ width: 28
+ height: width
+ radius: width / 2
+ color: SelectionRectangle.dragging ? control.palette.light : control.palette.window
+ border.width: 1
+ border.color: control.enabled ? control.palette.mid : control.palette.midlight
+ visible: SelectionRectangle.control.active
+
+ property Item control: SelectionRectangle.control
+ }
+
+}
diff --git a/src/quickcontrols2/basic/basic.pri b/src/quickcontrols2/basic/basic.pri
index 337f7f36..dcea52c7 100644
--- a/src/quickcontrols2/basic/basic.pri
+++ b/src/quickcontrols2/basic/basic.pri
@@ -46,6 +46,7 @@ QML_FILES += \
$$PWD/ScrollBar.qml \
$$PWD/ScrollIndicator.qml \
$$PWD/ScrollView.qml \
+ $$PWD/SelectionRectangle.qml \
$$PWD/Slider.qml \
$$PWD/SpinBox.qml \
$$PWD/SplitView.qml \
diff --git a/src/quickcontrols2/doc/images/qtquickcontrols2-selectionrectangle.png b/src/quickcontrols2/doc/images/qtquickcontrols2-selectionrectangle.png
new file mode 100644
index 00000000..69e47b29
--- /dev/null
+++ b/src/quickcontrols2/doc/images/qtquickcontrols2-selectionrectangle.png
Binary files differ
diff --git a/src/quickcontrols2/doc/snippets/selectionrectangle.qml b/src/quickcontrols2/doc/snippets/selectionrectangle.qml
new file mode 100644
index 00000000..02ee9e90
--- /dev/null
+++ b/src/quickcontrols2/doc/snippets/selectionrectangle.qml
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQml.Models
+import Qt.labs.qmlmodels
+
+Window {
+ width: 480
+ height: 640
+ visible: true
+ visibility: Window.AutomaticVisibility
+
+//![0]
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ clip: true
+
+ model: TableModel {
+ TableModelColumn { display: "name" }
+ rows: [ { "name": "Harry" }, { "name": "Hedwig" } ]
+ }
+
+ selectionModel: ItemSelectionModel {
+ model: tableView.model
+ }
+
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 30
+ color: selected ? "green" : "lightgray"
+
+ required property bool selected
+
+ Text { text: display }
+ }
+
+ SelectionRectangle {
+ target: tableView
+ }
+ }
+//![0]
+}
diff --git a/src/quicktemplates2/CMakeLists.txt b/src/quicktemplates2/CMakeLists.txt
index b326e467..fe4bdf9a 100644
--- a/src/quicktemplates2/CMakeLists.txt
+++ b/src/quicktemplates2/CMakeLists.txt
@@ -88,6 +88,8 @@ qt_internal_add_qml_module(QuickTemplates2
qquickscrollbar_p_p.h
qquickscrollindicator.cpp qquickscrollindicator_p.h
qquickscrollview.cpp qquickscrollview_p.h
+ qquickselectionrectangle.cpp qquickselectionrectangle_p.h
+ qquickselectionrectangle_p_p.h
qquickshortcutcontext.cpp
qquickshortcutcontext_p_p.h
qquickslider.cpp qquickslider_p.h
diff --git a/src/quicktemplates2/qquickselectionrectangle.cpp b/src/quicktemplates2/qquickselectionrectangle.cpp
new file mode 100644
index 00000000..fbde94f4
--- /dev/null
+++ b/src/quicktemplates2/qquickselectionrectangle.cpp
@@ -0,0 +1,540 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickselectionrectangle_p.h"
+#include "qquickselectionrectangle_p_p.h"
+
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuick/private/qquicktableview_p_p.h>
+
+#include "qquickscrollview_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype SelectionRectangle
+ \inherits Control
+//! \instantiates QQuickSelectionRectangle
+ \inqmlmodule QtQuick.Controls
+ \since 6.2
+ \ingroup utilities
+ \brief Used to select table cells inside a TableView
+
+ \image qtquickcontrols2-selectionrectangle.png
+
+ SelectionRectangle is used for selecting table cells in a TableView. It lets
+ the user start a selection by doing a pointer drag inside the viewport, or by
+ doing a long press on top of a cell.
+
+ For a SelectionRectangle to be able to select cells, TableView must have
+ an ItemSelectionModel assigned. The ItemSelectionModel will store any
+ selections done on the model, and can be used for querying
+ which cells that the user has selected.
+
+ The following example shows how you can make a SelectionRectangle target
+ a TableView:
+
+ \snippet qml/tableview/selectionmodel.qml 0
+
+ \note A SelectionRectangle itself is not shown as part of a selection. Only the
+ delegates (like topLeftHandle and bottomRightHandle) are used.
+ You should also consider \l {Selecting items}{rendering the TableView delegate as selected}.
+
+ \sa TableView, TableView::selectionModel, ItemSelectionModel
+*/
+
+/*!
+ \qmlproperty Item QtQuick.Controls::SelectionRectangle::target
+
+ This property holds the TableView on which the
+ SelectionRectangle should act.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Controls::SelectionRectangle::active
+ \readonly
+
+ This property is \c true while the user is performing a
+ selection. The selection will be active from the time the
+ the user starts to select, and until the selection is
+ removed again, for example from tapping inside the viewport.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Controls::SelectionRectangle::dragging
+ \readonly
+
+ This property is \c true whenever the user is doing a pointer drag or
+ a handle drag to adjust the selection rectangle.
+*/
+
+/*!
+ \qmlproperty Component QtQuick.Controls::SelectionRectangle::topLeftHandle
+
+ This property holds the delegate that will be shown on the center of the
+ top-left corner of the selection rectangle. When a handle is
+ provided, the user can drag it to adjust the selection.
+
+ You can set this property to \c null if you don't want a top-left selection handle.
+
+ \sa bottomRightHandle
+*/
+
+/*!
+ \qmlproperty Component QtQuick.Controls::SelectionRectangle::bottomRightHandle
+
+ This property holds the delegate that will be shown on the center of the
+ top-left corner of the selection rectangle. When a handle is
+ provided, the user can drag it to adjust the selection.
+
+ You can set this property to \c null if you don't want a top-left selection handle.
+
+ \sa topLeftHandle
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick.Controls::SelectionRectangle::selectionMode
+
+ This property holds when a selection should start.
+
+ \value SelectionRectangle.Drag A selection will start by doing a pointer drag inside the viewport
+ \value SelectionRectangle.PressAndHold A selection will start by doing a press and hold on top a cell
+ \value SelectionRectangle.Auto SelectionRectangle will choose which mode to use based on the target
+ and the platform. This normally means \c PressAndHold on touch based platforms, and \c Drag on desktop.
+ However, \c Drag will only be used if it doesn't conflict with flicking. This means that
+ TableView will need to be configured with \c interactive set to \c false, or placed
+ inside a ScrollView (where flicking, by default, is off for mouse events), for \c Drag to be chosen.
+
+ The default value is \c Auto.
+*/
+
+/*!
+ \qmlattachedproperty SelectionRectangle QtQuick::SelectionRectangle::control
+
+ This attached property holds the SelectionRectangle that manages the delegate instance.
+ It is attached to each handle instance.
+*/
+
+/*!
+ \qmlattachedproperty bool QtQuick::SelectionRectangle::dragging
+
+ This attached property will be \c true if the user is dragging on the handle.
+ It is attached to each handle instance.
+*/
+
+QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
+ : QQuickControlPrivate()
+{
+ m_tapHandler = new QQuickTapHandler();
+ m_dragHandler = new QQuickDragHandler();
+ m_dragHandler->setTarget(nullptr);
+
+ QObject::connect(&m_scrollTimer, &QTimer::timeout, [&]{
+ if (m_topLeftHandle && m_draggedHandle == m_topLeftHandle)
+ m_selectable->setSelectionStartPos(m_scrollToPoint);
+ else
+ m_selectable->setSelectionEndPos(m_scrollToPoint);
+ updateHandles();
+ const QSizeF dist = m_selectable->scrollTowardsSelectionPoint(m_scrollToPoint, m_scrollSpeed);
+ m_scrollToPoint.rx() += dist.width() > 0 ? m_scrollSpeed.width() : -m_scrollSpeed.width();
+ m_scrollToPoint.ry() += dist.height() > 0 ? m_scrollSpeed.height() : -m_scrollSpeed.height();
+ m_scrollSpeed = QSizeF(qAbs(dist.width() * 0.007), qAbs(dist.height() * 0.007));
+ });
+
+ QObject::connect(m_tapHandler, &QQuickTapHandler::tapped, [=](QEventPoint) {
+ m_selectable->clearSelection();
+ updateActiveState(false);
+ });
+
+ QObject::connect(m_tapHandler, &QQuickTapHandler::longPressed, [=]() {
+ if (!m_alwaysAcceptPressAndHold) {
+ if (m_selectionMode == QQuickSelectionRectangle::Auto) {
+ // In Auto mode, we only accept press and hold from touch
+ if (m_tapHandler->point().device()->pointerType() != QPointingDevice::PointerType::Finger)
+ return;
+ } else if (m_selectionMode != QQuickSelectionRectangle::PressAndHold) {
+ return;
+ }
+ }
+
+ const QPointF pos = m_tapHandler->point().position();
+ m_selectable->clearSelection();
+ m_selectable->setSelectionStartPos(pos);
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ });
+
+ QObject::connect(m_dragHandler, &QQuickDragHandler::activeChanged, [=]() {
+ const QPointF pos = m_dragHandler->centroid().position();
+ if (m_dragHandler->active()) {
+ m_selectable->clearSelection();
+ m_selectable->setSelectionStartPos(pos);
+ m_selectable->setSelectionEndPos(pos);
+ m_draggedHandle = nullptr;
+ updateHandles();
+ updateActiveState(true);
+ updateDraggingState(true);
+ } else {
+ m_scrollTimer.stop();
+ m_selectable->normalizeSelection();
+ updateDraggingState(false);
+ }
+ });
+
+ QObject::connect(m_dragHandler, &QQuickDragHandler::centroidChanged, [=]() {
+ if (!m_dragging)
+ return;
+ const QPointF pos = m_dragHandler->centroid().position();
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ scrollTowardsPos(pos);
+ });
+}
+
+void QQuickSelectionRectanglePrivate::scrollTowardsPos(const QPointF &pos)
+{
+ m_scrollToPoint = pos;
+ if (m_scrollTimer.isActive())
+ return;
+
+ const QSizeF dist = m_selectable->scrollTowardsSelectionPoint(m_scrollToPoint, m_scrollSpeed);
+ if (!dist.isNull())
+ m_scrollTimer.start(1);
+}
+
+void QQuickSelectionRectanglePrivate::updateDraggingState(bool dragging)
+{
+ if (dragging != m_dragging) {
+ m_dragging = dragging;
+ emit q_func()->draggingChanged();
+ }
+
+ if (auto attached = getAttachedObject(m_draggedHandle))
+ attached->setDragging(dragging);
+}
+
+void QQuickSelectionRectanglePrivate::updateActiveState(bool active)
+{
+ if (active == m_active)
+ return;
+
+ m_active = active;
+ emit q_func()->activeChanged();
+}
+
+QQuickItem *QQuickSelectionRectanglePrivate::createHandle(QQmlComponent *delegate)
+{
+ Q_Q(QQuickSelectionRectangle);
+
+ const bool topLeft = (delegate == m_topLeftHandleDelegate);
+
+ // Incubate the handle
+ const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
+ QObject *obj = delegate->beginCreate(QQmlEngine::contextForObject(q));
+ QQuickItem *handleItem = qobject_cast<QQuickItem*>(obj);
+ if (auto attached = getAttachedObject(handleItem))
+ attached->setControl(q);
+ delegate->completeCreate();
+ handleItem->setParentItem(handlerTarget);
+ if (handleItem->z() == 0)
+ handleItem->setZ(100);
+
+ // Add pointer handlers to it
+ QQuickDragHandler *dragHandler = new QQuickDragHandler();
+ dragHandler->setTarget(nullptr);
+ dragHandler->setParent(handleItem);
+ QQuickItemPrivate::get(handleItem)->addPointerHandler(dragHandler);
+
+ QObject::connect(dragHandler, &QQuickDragHandler::activeChanged, [=]() {
+ if (dragHandler->active()) {
+ const QPointF localPos = dragHandler->centroid().position();
+ const QPointF pos = handleItem->mapToItem(handleItem->parentItem(), localPos);
+ if (topLeft)
+ m_selectable->setSelectionStartPos(pos);
+ else
+ m_selectable->setSelectionEndPos(pos);
+
+ m_draggedHandle = handleItem;
+ updateHandles();
+ updateDraggingState(true);
+ } else {
+ m_scrollTimer.stop();
+ m_selectable->normalizeSelection();
+ updateDraggingState(false);
+ }
+ });
+
+ QObject::connect(dragHandler, &QQuickDragHandler::centroidChanged, [=]() {
+ if (!m_dragging)
+ return;
+
+ const QPointF localPos = dragHandler->centroid().position();
+ const QPointF pos = handleItem->mapToItem(handleItem->parentItem(), localPos);
+ if (topLeft)
+ m_selectable->setSelectionStartPos(pos);
+ else
+ m_selectable->setSelectionEndPos(pos);
+
+ updateHandles();
+ scrollTowardsPos(pos);
+ });
+
+ return handleItem;
+}
+
+void QQuickSelectionRectanglePrivate::updateHandles()
+{
+ if (!m_selectable)
+ return;
+
+ const QRectF rect = m_selectable->selectionRectangle().normalized();
+
+ if (!m_topLeftHandle && m_topLeftHandleDelegate)
+ m_topLeftHandle = createHandle(m_topLeftHandleDelegate);
+
+ if (!m_bottomRightHandle && m_bottomRightHandleDelegate)
+ m_bottomRightHandle = createHandle(m_bottomRightHandleDelegate);
+
+ if (m_topLeftHandle) {
+ m_topLeftHandle->setX(rect.x() - (m_topLeftHandle->width() / 2));
+ m_topLeftHandle->setY(rect.y() - (m_topLeftHandle->height() / 2));
+ }
+
+ if (m_bottomRightHandle) {
+ m_bottomRightHandle->setX(rect.x() + rect.width() - (m_bottomRightHandle->width() / 2));
+ m_bottomRightHandle->setY(rect.y() + rect.height() - (m_bottomRightHandle->height() / 2));
+ }
+}
+
+void QQuickSelectionRectanglePrivate::connectToTarget()
+{
+ // To support QuickSelectionRectangle::Auto, we need to listen for changes to the target
+ if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
+ connect(flickable, &QQuickFlickable::interactiveChanged, this, &QQuickSelectionRectanglePrivate::updateSelectionMode);
+ }
+}
+
+void QQuickSelectionRectanglePrivate::updateSelectionMode()
+{
+ Q_Q(QQuickSelectionRectangle);
+
+ const bool enabled = q->isEnabled();
+ m_tapHandler->setEnabled(enabled);
+
+ if (m_selectionMode == QQuickSelectionRectangle::Auto) {
+ if (qobject_cast<QQuickScrollView *>(m_target->parentItem())) {
+ // ScrollView allows flicking with touch, but not with mouse. So we do
+ // the same here: you can drag to select with a mouse, but not with touch.
+ m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
+ m_dragHandler->setEnabled(enabled);
+ } else if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
+ m_dragHandler->setEnabled(enabled && !flickable->isInteractive());
+ } else {
+ m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
+ m_dragHandler->setEnabled(enabled);
+ }
+ } else if (m_selectionMode == QQuickSelectionRectangle::Drag) {
+ m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::AllDevices);
+ m_dragHandler->setEnabled(enabled);
+ } else {
+ m_dragHandler->setEnabled(false);
+ }
+
+ // If you can't select using a drag, we always accept a PressAndHold
+ m_alwaysAcceptPressAndHold = !m_dragHandler->enabled();
+}
+
+QQuickSelectionRectangleAttached *QQuickSelectionRectanglePrivate::getAttachedObject(const QObject *object) const
+{
+ QObject *attachedObject = qmlAttachedPropertiesObject<QQuickSelectionRectangle>(object);
+ return static_cast<QQuickSelectionRectangleAttached *>(attachedObject);
+}
+
+// --------------------------------------------------------
+
+QQuickSelectionRectangle::QQuickSelectionRectangle(QQuickItem *parent)
+ : QQuickControl(*(new QQuickSelectionRectanglePrivate), parent)
+{
+ Q_D(QQuickSelectionRectangle);
+
+ QObject::connect(this, &QQuickItem::enabledChanged, [=]() {
+ d->m_scrollTimer.stop();
+ d->updateSelectionMode();
+ d->updateDraggingState(false);
+ d->updateActiveState(false);
+ });
+}
+
+QQuickItem *QQuickSelectionRectangle::target() const
+{
+ return d_func()->m_target;
+}
+
+void QQuickSelectionRectangle::setTarget(QQuickItem *target)
+{
+ Q_D(QQuickSelectionRectangle);
+ if (d->m_target == target)
+ return;
+
+ if (d->m_selectable) {
+ d->m_scrollTimer.stop();
+ d->m_tapHandler->setParent(nullptr);
+ d->m_dragHandler->setParent(nullptr);
+ d->m_target->disconnect(this);
+ }
+
+ d->m_target = target;
+ d->m_selectable = nullptr;
+
+ if (d->m_target) {
+ d->m_selectable = dynamic_cast<QQuickSelectable *>(QObjectPrivate::get(d->m_target.data()));
+ if (!d->m_selectable)
+ qmlWarning(this) << "the assigned target is not supported by the control";
+ }
+
+ if (d->m_selectable) {
+ const auto handlerTarget = d->m_selectable->selectionPointerHandlerTarget();
+ d->m_dragHandler->setParent(handlerTarget);
+ d->m_tapHandler->setParent(handlerTarget);
+ QQuickItemPrivate::get(handlerTarget)->addPointerHandler(d->m_tapHandler);
+ QQuickItemPrivate::get(handlerTarget)->addPointerHandler(d->m_dragHandler);
+ d->connectToTarget();
+ d->updateSelectionMode();
+ }
+
+ emit targetChanged();
+}
+
+bool QQuickSelectionRectangle::active()
+{
+ return d_func()->m_active;
+}
+
+bool QQuickSelectionRectangle::dragging()
+{
+ return d_func()->m_dragging;
+}
+
+QQuickSelectionRectangle::SelectionMode QQuickSelectionRectangle::selectionMode() const
+{
+ return d_func()->m_selectionMode;
+}
+
+void QQuickSelectionRectangle::setSelectionMode(QQuickSelectionRectangle::SelectionMode selectionMode)
+{
+ Q_D(QQuickSelectionRectangle);
+ if (d->m_selectionMode == selectionMode)
+ return;
+
+ d->m_selectionMode = selectionMode;
+
+ if (d->m_target)
+ d->updateSelectionMode();
+
+ emit selectionModeChanged();
+}
+
+QQmlComponent *QQuickSelectionRectangle::topLeftHandle() const
+{
+ return d_func()->m_topLeftHandleDelegate;
+}
+
+void QQuickSelectionRectangle::setTopLeftHandle(QQmlComponent *topLeftHandle)
+{
+ Q_D(QQuickSelectionRectangle);
+ if (d->m_topLeftHandleDelegate == topLeftHandle)
+ return;
+
+ d->m_topLeftHandleDelegate = topLeftHandle;
+ emit topLeftHandleChanged();
+}
+
+QQmlComponent *QQuickSelectionRectangle::bottomRightHandle() const
+{
+ return d_func()->m_bottomRightHandleDelegate;
+}
+
+void QQuickSelectionRectangle::setBottomRightHandle(QQmlComponent *bottomRightHandle)
+{
+ Q_D(QQuickSelectionRectangle);
+ if (d->m_bottomRightHandleDelegate == bottomRightHandle)
+ return;
+
+ d->m_bottomRightHandleDelegate = bottomRightHandle;
+ emit bottomRightHandleChanged();
+}
+
+QQuickSelectionRectangleAttached *QQuickSelectionRectangle::qmlAttachedProperties(QObject *obj)
+{
+ return new QQuickSelectionRectangleAttached(obj);
+}
+
+QQuickSelectionRectangleAttached::QQuickSelectionRectangleAttached(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QQuickSelectionRectangle *QQuickSelectionRectangleAttached::control() const
+{
+ return m_control;
+}
+
+void QQuickSelectionRectangleAttached::setControl(QQuickSelectionRectangle *control)
+{
+ if (m_control == control)
+ return;
+
+ m_control = control;
+ emit controlChanged();
+}
+
+bool QQuickSelectionRectangleAttached::dragging() const
+{
+ return m_dragging;
+}
+
+void QQuickSelectionRectangleAttached::setDragging(bool dragging)
+{
+ if (m_dragging == dragging)
+ return;
+
+ m_dragging = dragging;
+ emit draggingChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quicktemplates2/qquickselectionrectangle_p.h b/src/quicktemplates2/qquickselectionrectangle_p.h
new file mode 100644
index 00000000..3acf1b10
--- /dev/null
+++ b/src/quicktemplates2/qquickselectionrectangle_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSELECTIONRECTANGLE_P_H
+#define QQUICKSELECTIONRECTANGLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/qquickitem.h>
+#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSelectionRectanglePrivate;
+class QQuickSelectable;
+class QQuickSelectionRectangleAttached;
+
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSelectionRectangle : public QQuickControl
+{
+ Q_OBJECT
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged FINAL)
+ Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged FINAL)
+ Q_PROPERTY(QQmlComponent *topLeftHandle READ topLeftHandle WRITE setTopLeftHandle NOTIFY topLeftHandleChanged FINAL)
+ Q_PROPERTY(QQmlComponent *bottomRightHandle READ bottomRightHandle WRITE setBottomRightHandle NOTIFY bottomRightHandleChanged FINAL)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged FINAL)
+ Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged FINAL)
+
+ QML_NAMED_ELEMENT(SelectionRectangle)
+ QML_ATTACHED(QQuickSelectionRectangleAttached)
+ QML_ADDED_IN_VERSION(6, 2)
+
+public:
+ enum SelectionMode {
+ Drag,
+ PressAndHold,
+ Auto
+ };
+ Q_ENUM(SelectionMode)
+
+ explicit QQuickSelectionRectangle(QQuickItem *parent = nullptr);
+
+ QQuickItem *target() const;
+ void setTarget(QQuickItem *target);
+
+ bool active();
+ bool dragging();
+
+ SelectionMode selectionMode() const;
+ void setSelectionMode(SelectionMode selectionMode);
+
+ QQmlComponent *topLeftHandle() const;
+ void setTopLeftHandle(QQmlComponent *topLeftHandle);
+ QQmlComponent *bottomRightHandle() const;
+ void setBottomRightHandle(QQmlComponent *bottomRightHandle);
+
+ static QQuickSelectionRectangleAttached *qmlAttachedProperties(QObject *obj);
+
+Q_SIGNALS:
+ void targetChanged();
+ bool activeChanged();
+ bool draggingChanged();
+ void topLeftHandleChanged();
+ void bottomRightHandleChanged();
+ void selectionModeChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickSelectionRectangle)
+ Q_DECLARE_PRIVATE(QQuickSelectionRectangle)
+};
+
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSelectionRectangleAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickSelectionRectangle *control READ control NOTIFY controlChanged FINAL)
+ Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged FINAL)
+
+public:
+ QQuickSelectionRectangleAttached(QObject *parent);
+
+ QQuickSelectionRectangle *control() const;
+ void setControl(QQuickSelectionRectangle *control);
+
+ bool dragging() const;
+ void setDragging(bool dragging);
+
+Q_SIGNALS:
+ void controlChanged();
+ void draggingChanged();
+
+private:
+ QPointer<QQuickSelectionRectangle> m_control;
+ bool m_dragging = false;
+
+ friend class QQuickSelectionRectanglePrivate;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickSelectionRectangle)
+
+#endif // QQUICKSELECTIONRECTANGLE_P_H
diff --git a/src/quicktemplates2/qquickselectionrectangle_p_p.h b/src/quicktemplates2/qquickselectionrectangle_p_p.h
new file mode 100644
index 00000000..2ed3e035
--- /dev/null
+++ b/src/quicktemplates2/qquickselectionrectangle_p_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSELECTIONRECTANGLE_P_P_H
+#define QQUICKSELECTIONRECTANGLE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickselectionrectangle_p.h"
+
+#include <QtCore/qpointer.h>
+#include <QtCore/qtimer.h>
+
+#include <QtQuick/private/qquickselectable_p.h>
+#include <QtQuick/private/qquicktaphandler_p.h>
+#include <QtQuick/private/qquickdraghandler_p.h>
+
+#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSelectionRectanglePrivate : public QQuickControlPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSelectionRectangle)
+
+public:
+ QQuickSelectionRectanglePrivate();
+
+ void updateDraggingState(bool isDragging);
+ void updateActiveState(bool isActive);
+ void updateHandles();
+ void updateSelectionMode();
+ void connectToTarget();
+ void scrollTowardsPos(const QPointF &pos);
+
+ QQuickItem *createHandle(QQmlComponent *delegate);
+
+ QQuickSelectionRectangleAttached *getAttachedObject(const QObject *object) const;
+
+public:
+ QPointer<QQuickItem> m_target;
+
+ QQmlComponent *m_topLeftHandleDelegate = nullptr;
+ QQmlComponent *m_bottomRightHandleDelegate = nullptr;
+ QPointer<QQuickItem> m_topLeftHandle;
+ QPointer<QQuickItem> m_bottomRightHandle;
+ QPointer<QQuickItem> m_draggedHandle = nullptr;
+
+ QQuickSelectable *m_selectable = nullptr;
+
+ QQuickTapHandler *m_tapHandler = nullptr;
+ QQuickDragHandler *m_dragHandler = nullptr;
+
+ QTimer m_scrollTimer;
+ QPointF m_scrollToPoint;
+ QSizeF m_scrollSpeed = QSizeF(1, 1);
+
+ QQuickSelectionRectangle::SelectionMode m_selectionMode = QQuickSelectionRectangle::Auto;
+ bool m_alwaysAcceptPressAndHold = false;
+
+ bool m_enabled = true;
+ bool m_active = false;
+ bool m_dragging = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSELECTIONRECTANGLE_P_P_H
diff --git a/src/quicktemplates2/quicktemplates2.pri b/src/quicktemplates2/quicktemplates2.pri
index fa377f78..0f19de5e 100644
--- a/src/quicktemplates2/quicktemplates2.pri
+++ b/src/quicktemplates2/quicktemplates2.pri
@@ -71,6 +71,8 @@ HEADERS += \
$$PWD/qquickscrollbar_p_p.h \
$$PWD/qquickscrollindicator_p.h \
$$PWD/qquickscrollview_p.h \
+ $$PWD/qquickselectionrectangle_p.h \
+ $$PWD/qquickselectionrectangle_p_p.h \
$$PWD/qquickshortcutcontext_p_p.h \
$$PWD/qquickslider_p.h \
$$PWD/qquickspinbox_p.h \
diff --git a/tests/auto/controls/data/tst_selectionrectangle.qml b/tests/auto/controls/data/tst_selectionrectangle.qml
new file mode 100644
index 00000000..13ddf00f
--- /dev/null
+++ b/tests/auto/controls/data/tst_selectionrectangle.qml
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 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:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtTest
+import QtQuick.Controls
+import Qt.labs.qmlmodels
+
+TestCase {
+ id: testCase
+ width: 200
+ height: 200
+ visible: true
+ when: windowShown
+ name: "SelectionRectangle"
+
+ property real cellWidth: 50
+ property real cellHeight: 20
+ property Item handle: null
+ property bool handleWasDragged: false
+
+ Component {
+ id: handleComp
+ Rectangle {
+ id: handle
+ width: 28
+ height: width
+ radius: width / 2
+ property bool dragging: SelectionRectangle.dragging
+ property Item control: SelectionRectangle.control
+ border.width: 1
+ border.color: "red"
+ visible: SelectionRectangle.control.active
+
+ SelectionRectangle.onDraggingChanged: {
+ if (SelectionRectangle.dragging)
+ testCase.handleWasDragged = true
+ }
+
+ Component.onCompleted: testCase.handle = handle
+ }
+ }
+
+ Component {
+ id: tableviewComp
+ TableView {
+ id: tableView
+ clip: true
+ anchors.fill: parent
+
+ model: TableModel {
+ TableModelColumn { display: "c1" }
+ TableModelColumn { display: "c2" }
+ TableModelColumn { display: "c3" }
+ TableModelColumn { display: "c4" }
+ rows: [
+ { "c1": "v1", "c2":"v2", "c3":"v3", "c4": "v4" },
+ { "c1": "v1", "c2":"v2", "c3":"v3", "c4": "v4" },
+ { "c1": "v1", "c2":"v2", "c3":"v3", "c4": "v4" },
+ { "c1": "v1", "c2":"v2", "c3":"v3", "c4": "v4" },
+ ]
+ }
+
+ delegate: Rectangle {
+ required property bool selected
+ implicitWidth: cellWidth
+ implicitHeight: cellHeight
+ color: selected ? "lightblue" : "gray"
+ Text { text: "cell" }
+ }
+
+ selectionModel: ItemSelectionModel {
+ model: tableView.model
+ }
+
+ property alias selectionRectangle: selectionRectangle
+ SelectionRectangle {
+ id: selectionRectangle
+ target: tableView
+ }
+ }
+
+ }
+
+ Component {
+ id: signalSpy
+ SignalSpy { }
+ }
+
+ function test_set_target() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ compare(selectionRectangle.target, tableView)
+
+ selectionRectangle.target = null
+ compare(selectionRectangle.target, null)
+
+ selectionRectangle.target = tableView
+ compare(selectionRectangle.target, tableView)
+ }
+
+ function test_set_selectionMode() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ // Default selection mode should be Auto
+ compare(selectionRectangle.selectionMode, SelectionRectangle.Auto)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+ compare(selectionRectangle.selectionMode, SelectionRectangle.Drag)
+
+ selectionRectangle.selectionMode = SelectionRectangle.PressAndHold
+ compare(selectionRectangle.selectionMode, SelectionRectangle.PressAndHold)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Auto
+ compare(selectionRectangle.selectionMode, SelectionRectangle.Auto)
+ }
+
+ function test_set_handles() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.topLeftHandle = null
+ compare(selectionRectangle.topLeftHandle, null)
+
+ selectionRectangle.bottomRightHandle = null
+ compare(selectionRectangle.bottomRightHandle, null)
+
+ selectionRectangle.topLeftHandle = handleComp
+ compare(selectionRectangle.topLeftHandle, handleComp)
+
+ selectionRectangle.bottomRightHandle = handleComp
+ compare(selectionRectangle.bottomRightHandle, handleComp)
+ }
+
+ function test_drag() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ let activeSpy = signalSpy.createObject(selectionRectangle, {target: selectionRectangle, signalName: "activeChanged"})
+ let draggingSpy = signalSpy.createObject(selectionRectangle, {target: selectionRectangle, signalName: "draggingChanged"})
+ verify(activeSpy.valid)
+ verify(draggingSpy.valid)
+
+ verify(!tableView.selectionModel.hasSelection)
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ verify(tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 1)))
+
+ compare(activeSpy.count, 1)
+ compare(draggingSpy.count, 2)
+
+ // Remove selection
+ mouseClick(tableView, 1, 1, Qt.LeftButton)
+ verify(!tableView.selectionModel.hasSelection)
+ compare(draggingSpy.count, 2)
+ compare(activeSpy.count, 2)
+
+ // Ensure that a press and hold doesn't start a selection
+ mousePress(tableView, 1, 1, Qt.LeftButton)
+ mousePress(tableView, 1, 1, Qt.LeftButton, Qt.NoModifier, 1000)
+ verify(!tableView.selectionModel.hasSelection)
+ }
+
+ function test_pressAndHold() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.PressAndHold
+
+ let activeSpy = signalSpy.createObject(selectionRectangle, {target: selectionRectangle, signalName: "activeChanged"})
+ let draggingSpy = signalSpy.createObject(selectionRectangle, {target: selectionRectangle, signalName: "draggingChanged"})
+ verify(activeSpy.valid)
+ verify(draggingSpy.valid)
+
+ verify(!tableView.selectionModel.hasSelection)
+ // Do a press and hold
+ mousePress(tableView, 1, 1, Qt.LeftButton)
+ mousePress(tableView, 1, 1, Qt.LeftButton, Qt.NoModifier, 1000)
+ verify(tableView.selectionModel.hasSelection)
+ compare(tableView.selectionModel.selectedIndexes.length, 1)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(0, 0)))
+
+ compare(draggingSpy.count, 0)
+ compare(activeSpy.count, 1)
+
+ // Remove selection
+ mouseClick(tableView, 1, 1, Qt.LeftButton)
+ verify(!tableView.selectionModel.hasSelection)
+ compare(draggingSpy.count, 0)
+ compare(activeSpy.count, 2)
+
+ // Ensure that a drag doesn't start a selection
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ verify(!tableView.selectionModel.hasSelection)
+ }
+
+ function test_handleDragTopLeft() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ verify(!tableView.selectionModel.hasSelection)
+ // Select four cells in the middle
+ mouseDrag(tableView, cellWidth + 1, cellHeight + 1, (cellWidth * 2) - 2, (cellHeight * 2) - 2, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 4)
+ for (var x = 1; x < 3; ++x) {
+ for (var y = 1; y < 3; ++y) {
+ verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
+ }
+ }
+
+ // Drag on the top left handle, so that the selection extends to cell 0, 0
+ mouseDrag(tableView, cellWidth, cellHeight, -cellWidth / 2, -cellHeight / 2, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 9)
+ for (x = 0; x < 3; ++x) {
+ for (y = 0; y < 3; ++y) {
+ verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
+ }
+ }
+ }
+
+ function test_handleDragBottomRight() {
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ verify(!tableView.selectionModel.hasSelection)
+ // Select four cells in the middle
+ mouseDrag(tableView, cellWidth + 1, cellHeight + 1, (cellWidth * 2) - 2, (cellHeight * 2) - 2, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 4)
+ for (var x = 1; x < 3; ++x) {
+ for (var y = 1; y < 3; ++y) {
+ verify(tableView.selectionModel.isSelected(tableView.model.index(x, y)))
+ }
+ }
+
+ // Drag on the bottom right handle, so that the selection shrinks to cell 1, 1
+ mouseDrag(tableView, cellWidth * 2, cellHeight * 2, -cellWidth / 2, -cellHeight / 2, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 1)
+ verify(tableView.selectionModel.isSelected(tableView.model.index(1, 1)))
+ }
+}