diff options
Diffstat (limited to 'src/quicktemplates/qquickselectionrectangle.cpp')
-rw-r--r-- | src/quicktemplates/qquickselectionrectangle.cpp | 157 |
1 files changed, 123 insertions, 34 deletions
diff --git a/src/quicktemplates/qquickselectionrectangle.cpp b/src/quicktemplates/qquickselectionrectangle.cpp index a2adc43dcd..1bf04b7094 100644 --- a/src/quicktemplates/qquickselectionrectangle.cpp +++ b/src/quicktemplates/qquickselectionrectangle.cpp @@ -81,7 +81,7 @@ QT_BEGIN_NAMESPACE The handle is not hidden by default when a selection is removed. Instead, this is the responsibility of the delegate, to open up for custom fade-out animations. The easiest way to ensure that the handle - ends up hidden, is to simply bind \l visible to the the \l active + ends up hidden, is to simply bind \l {Item::}{visible} to the \l active state of the SelectionRectangle: \qml @@ -109,7 +109,7 @@ QT_BEGIN_NAMESPACE The handle is not hidden by default when a selection is removed. Instead, this is the responsibility of the delegate, to open up for custom fade-out animations. The easiest way to ensure that the handle - ends up hidden, is to simply bind \l visible to the the \l active + ends up hidden, is to simply bind \l {Item::}{visible} to the \l active state of the SelectionRectangle: \qml @@ -144,14 +144,14 @@ QT_BEGIN_NAMESPACE */ /*! - \qmlattachedproperty SelectionRectangle QtQuick::SelectionRectangle::control + \qmlattachedproperty SelectionRectangle QtQuick.Controls::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 + \qmlattachedproperty bool QtQuick.Controls::SelectionRectangle::dragging This attached property will be \c true if the user is dragging on the handle. It is attached to each handle instance. @@ -170,56 +170,118 @@ QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate() else m_selectable->setSelectionEndPos(m_scrollToPoint); updateHandles(); - const QSizeF dist = m_selectable->scrollTowardsSelectionPoint(m_scrollToPoint, m_scrollSpeed); + const QSizeF dist = m_selectable->scrollTowardsPoint(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, [this] { - updateActiveState(false); - }); + QObject::connect(m_tapHandler, &QQuickTapHandler::pressedChanged, [this]() { + if (!m_tapHandler->isPressed()) + return; + if (m_effectiveSelectionMode != QQuickSelectionRectangle::Drag) + return; - QObject::connect(m_tapHandler, &QQuickTapHandler::longPressed, [this]() { const QPointF pos = m_tapHandler->point().pressPosition(); const auto modifiers = m_tapHandler->point().modifiers(); + if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier)) + return; + + if (modifiers & Qt::ShiftModifier) { + // Extend the selection towards the pressed cell. If there is no + // existing selection, start a new selection from the current item + // to the pressed item. + if (!m_active) { + if (!m_selectable->startSelection(pos, modifiers)) + return; + m_selectable->setSelectionStartPos(QPoint{-1, -1}); + } + m_selectable->setSelectionEndPos(pos); + updateHandles(); + updateActiveState(true); + } else if (modifiers & Qt::ControlModifier) { + // Select a single cell, but keep the old selection (unless + // m_selectable->startSelection(pos. modifiers) returns false, which + // it will if selectionMode only allows a single selection). + if (handleUnderPos(pos) != nullptr) { + // Don't allow press'n'hold to start a new + // selection if it started on top of a handle. + return; + } + + if (!m_selectable->startSelection(pos, modifiers)) + return; + m_selectable->setSelectionStartPos(pos); + m_selectable->setSelectionEndPos(pos); + updateHandles(); + updateActiveState(true); + } + }); - if (!m_selectable->startSelection(pos)) + QObject::connect(m_tapHandler, &QQuickTapHandler::longPressed, [this]() { + if (m_effectiveSelectionMode != QQuickSelectionRectangle::PressAndHold) return; + + const QPointF pos = m_tapHandler->point().pressPosition(); + const auto modifiers = m_tapHandler->point().modifiers(); if (handleUnderPos(pos) != nullptr) { // Don't allow press'n'hold to start a new // selection if it started on top of a handle. return; } - 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) + + if (modifiers == Qt::ShiftModifier) { + // Extend the selection towards the pressed cell. If there is no + // existing selection, start a new selection from the current item + // to the pressed item. + if (!m_active) { + if (!m_selectable->startSelection(pos, modifiers)) return; - } else if (m_selectionMode != QQuickSelectionRectangle::PressAndHold) { - return; + m_selectable->setSelectionStartPos(QPoint{-1, -1}); } - } - - if (!modifiers.testFlag(Qt::ShiftModifier)) + m_selectable->setSelectionEndPos(pos); + updateHandles(); + updateActiveState(true); + } else if (modifiers == Qt::ControlModifier) { + // Select a single cell, but keep the old selection (unless + // m_selectable->startSelection(pos, modifiers) returns false, which + // it will if selectionMode only allows a single selection). + if (!m_selectable->startSelection(pos, modifiers)) + return; + m_selectable->setSelectionStartPos(pos); + m_selectable->setSelectionEndPos(pos); + updateHandles(); + updateActiveState(true); + } else if (modifiers == Qt::NoModifier) { + // Select a single cell m_selectable->clearSelection(); - m_selectable->setSelectionStartPos(pos); - m_selectable->setSelectionEndPos(pos); - updateHandles(); - updateActiveState(true); + if (!m_selectable->startSelection(pos, modifiers)) + return; + m_selectable->setSelectionStartPos(pos); + m_selectable->setSelectionEndPos(pos); + updateHandles(); + updateActiveState(true); + } }); QObject::connect(m_dragHandler, &QQuickDragHandler::activeChanged, [this]() { + Q_ASSERT(m_effectiveSelectionMode == QQuickSelectionRectangle::Drag); const QPointF startPos = m_dragHandler->centroid().pressPosition(); const QPointF dragPos = m_dragHandler->centroid().position(); const auto modifiers = m_dragHandler->centroid().modifiers(); + if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier)) + return; if (m_dragHandler->active()) { - if (!m_selectable->startSelection(startPos)) - return; - if (!modifiers.testFlag(Qt::ShiftModifier)) - m_selectable->clearSelection(); - m_selectable->setSelectionStartPos(startPos); + // Start a new selection unless there is an active selection + // already, and one of the relevant modifiers are being held. + // In that case we continue to extend the active selection instead. + const bool modifiersHeld = modifiers & (Qt::ControlModifier | Qt::ShiftModifier); + if (!m_active || !modifiersHeld) { + if (!m_selectable->startSelection(startPos, modifiers)) + return; + m_selectable->setSelectionStartPos(startPos); + } m_selectable->setSelectionEndPos(dragPos); m_draggedHandle = nullptr; updateHandles(); @@ -248,7 +310,7 @@ void QQuickSelectionRectanglePrivate::scrollTowardsPos(const QPointF &pos) if (m_scrollTimer.isActive()) return; - const QSizeF dist = m_selectable->scrollTowardsSelectionPoint(m_scrollToPoint, m_scrollSpeed); + const QSizeF dist = m_selectable->scrollTowardsPoint(m_scrollToPoint, m_scrollSpeed); if (!dist.isNull()) m_scrollTimer.start(1); } @@ -409,6 +471,25 @@ void QQuickSelectionRectanglePrivate::connectToTarget() if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) { connect(flickable, &QQuickFlickable::interactiveChanged, this, &QQuickSelectionRectanglePrivate::updateSelectionMode); } + + // Add a callback function that tells if the selection was + // modified outside of the actions taken by SelectionRectangle. + m_selectable->setCallback([this](QQuickSelectable::CallBackFlag flag){ + switch (flag) { + case QQuickSelectable::CallBackFlag::CancelSelection: + // The selection is either cleared, or can no longer be + // represented as a rectangle with two selection handles. + updateActiveState(false); + break; + case QQuickSelectable::CallBackFlag::SelectionRectangleChanged: + // The selection has changed, but the selection is still + // rectangular and without holes. + updateHandles(); + break; + default: + Q_UNREACHABLE(); + } + }); } void QQuickSelectionRectanglePrivate::updateSelectionMode() @@ -419,26 +500,33 @@ void QQuickSelectionRectanglePrivate::updateSelectionMode() m_tapHandler->setEnabled(enabled); if (m_selectionMode == QQuickSelectionRectangle::Auto) { - if (qobject_cast<QQuickScrollView *>(m_target->parentItem())) { + if (m_target && 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_effectiveSelectionMode = QQuickSelectionRectangle::Drag; 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()); + if (enabled && !flickable->isInteractive()) { + m_effectiveSelectionMode = QQuickSelectionRectangle::Drag; + m_dragHandler->setEnabled(true); + } else { + m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold; + m_dragHandler->setEnabled(false); + } } else { + m_effectiveSelectionMode = QQuickSelectionRectangle::Drag; m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse); m_dragHandler->setEnabled(enabled); } } else if (m_selectionMode == QQuickSelectionRectangle::Drag) { + m_effectiveSelectionMode = QQuickSelectionRectangle::Drag; m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::AllDevices); m_dragHandler->setEnabled(enabled); } else { + m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold; 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 @@ -480,6 +568,7 @@ void QQuickSelectionRectangle::setTarget(QQuickItem *target) d->m_tapHandler->setParent(this); d->m_dragHandler->setParent(this); d->m_target->disconnect(this); + d->m_selectable->setCallback(nullptr); } d->m_target = target; |