diff options
-rw-r--r-- | src/quickcontrols2/basic/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/quickcontrols2/basic/SelectionRectangle.qml | 61 | ||||
-rw-r--r-- | src/quickcontrols2/basic/basic.pri | 1 | ||||
-rw-r--r-- | src/quickcontrols2/doc/images/qtquickcontrols2-selectionrectangle.png | bin | 0 -> 52957 bytes | |||
-rw-r--r-- | src/quickcontrols2/doc/snippets/selectionrectangle.qml | 95 | ||||
-rw-r--r-- | src/quicktemplates2/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/quicktemplates2/qquickselectionrectangle.cpp | 540 | ||||
-rw-r--r-- | src/quicktemplates2/qquickselectionrectangle_p.h | 143 | ||||
-rw-r--r-- | src/quicktemplates2/qquickselectionrectangle_p_p.h | 110 | ||||
-rw-r--r-- | src/quicktemplates2/quicktemplates2.pri | 2 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_selectionrectangle.qml | 311 |
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 Binary files differnew file mode 100644 index 00000000..69e47b29 --- /dev/null +++ b/src/quickcontrols2/doc/images/qtquickcontrols2-selectionrectangle.png 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))) + } +} |