File indexing completed on 2024-06-02 05:42:14

0001 // SPDX-FileCopyrightText: 2016 The Qt Company Ltd.
0002 // SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
0003 // SPDX-License-Identifier: GPL-2.0-or-later
0004 
0005 #include "delegatetoucharea.h"
0006 
0007 #include <QCursor>
0008 #include <QGuiApplication>
0009 #include <QStyleHints>
0010 
0011 // Some code taken from MouseArea
0012 
0013 DelegateTouchArea::DelegateTouchArea(QQuickItem *parent)
0014     : QQuickItem{parent}
0015     , m_pressAndHoldTimer{new QTimer{this}}
0016 {
0017     m_pressAndHoldTimer->setInterval(600);
0018     m_pressAndHoldTimer->setSingleShot(true);
0019     connect(m_pressAndHoldTimer, &QTimer::timeout, this, &DelegateTouchArea::startPressAndHold);
0020 
0021     // Explcitly call setCursor on QQuickItem since
0022     // it internally keeps a boolean hasCursor that doesn't
0023     // get set to true unless you call setCursor
0024     setCursor(Qt::ArrowCursor);
0025 
0026     setAcceptHoverEvents(true);
0027     setAcceptTouchEvents(true);
0028     setFlags(QQuickItem::ItemIsFocusScope);
0029     setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
0030 }
0031 
0032 bool DelegateTouchArea::pressed()
0033 {
0034     return m_pressed;
0035 }
0036 
0037 void DelegateTouchArea::setPressed(bool pressed)
0038 {
0039     if (pressed != m_pressed) {
0040         m_pressed = pressed;
0041         Q_EMIT pressedChanged(pressed);
0042     }
0043 }
0044 
0045 bool DelegateTouchArea::hovered()
0046 {
0047     return m_hovered;
0048 }
0049 
0050 void DelegateTouchArea::setHovered(bool hovered)
0051 {
0052     if (hovered != m_hovered) {
0053         m_hovered = hovered;
0054         Q_EMIT hoveredChanged(hovered);
0055     }
0056 }
0057 
0058 Qt::CursorShape DelegateTouchArea::cursorShape()
0059 {
0060     return cursor().shape();
0061 }
0062 
0063 void DelegateTouchArea::setCursorShape(Qt::CursorShape cursorShape)
0064 {
0065     if (cursor().shape() == cursorShape) {
0066         return;
0067     }
0068 
0069     setCursor(cursorShape);
0070     Q_EMIT cursorShapeChanged();
0071 }
0072 
0073 void DelegateTouchArea::unsetCursor()
0074 {
0075     setCursorShape(Qt::ArrowCursor);
0076 }
0077 
0078 QPointF DelegateTouchArea::pressPosition()
0079 {
0080     return m_mouseDownPosition;
0081 }
0082 
0083 void DelegateTouchArea::mousePressEvent(QMouseEvent *event)
0084 {
0085     if (event->button() & Qt::RightButton) {
0086         Q_EMIT rightMousePress();
0087     } else if (event->button() & Qt::LeftButton) {
0088         handlePressEvent(event, event->points().first().position());
0089         event->accept();
0090     } else {
0091         QQuickItem::mousePressEvent(event);
0092     }
0093 }
0094 
0095 void DelegateTouchArea::mouseMoveEvent(QMouseEvent *event)
0096 {
0097     handleMoveEvent(event, event->points().first().position());
0098     event->accept();
0099 }
0100 
0101 void DelegateTouchArea::mouseReleaseEvent(QMouseEvent *event)
0102 {
0103     if (event->button() & Qt::LeftButton) {
0104         handleReleaseEvent(event, true);
0105         event->accept();
0106     } else {
0107         QQuickItem::mouseReleaseEvent(event);
0108     }
0109 }
0110 
0111 void DelegateTouchArea::mouseUngrabEvent()
0112 {
0113     if (m_pressed) {
0114         handleReleaseEvent(nullptr, false);
0115     }
0116     QQuickItem::mouseUngrabEvent();
0117 }
0118 
0119 void DelegateTouchArea::touchEvent(QTouchEvent *event)
0120 {
0121     bool unhandled = true;
0122     const auto &firstPoint = event->points().first();
0123 
0124     switch (firstPoint.state()) {
0125     case QEventPoint::State::Pressed:
0126         handlePressEvent(event, firstPoint.position());
0127         event->accept();
0128         unhandled = false;
0129         break;
0130     case QEventPoint::State::Updated:
0131         handleMoveEvent(event, firstPoint.position());
0132         event->accept();
0133         unhandled = false;
0134         break;
0135     case QEventPoint::State::Released:
0136         handleReleaseEvent(event, true);
0137         event->accept();
0138         unhandled = false;
0139         break;
0140     case QEventPoint::State::Stationary:
0141     case QEventPoint::State::Unknown:
0142         break;
0143     }
0144 
0145     if (unhandled) {
0146         QQuickItem::touchEvent(event);
0147     }
0148 }
0149 
0150 void DelegateTouchArea::touchUngrabEvent()
0151 {
0152     if (m_pressed) {
0153         handleReleaseEvent(nullptr, false);
0154     }
0155     QQuickItem::touchUngrabEvent();
0156 }
0157 
0158 void DelegateTouchArea::hoverEnterEvent(QHoverEvent *event)
0159 {
0160     setHovered(true);
0161 
0162     // don't block hover events
0163     event->ignore();
0164 }
0165 
0166 void DelegateTouchArea::hoverLeaveEvent(QHoverEvent *event)
0167 {
0168     setHovered(false);
0169 
0170     // don't block hover events
0171     event->ignore();
0172 }
0173 
0174 void DelegateTouchArea::handlePressEvent(QPointerEvent *event, QPointF point)
0175 {
0176     // ignore multiple press events
0177     if (m_pressed) {
0178         return;
0179     }
0180 
0181     setPressed(true);
0182     forceActiveFocus(Qt::MouseFocusReason);
0183 
0184     m_mouseDownPosition = point;
0185     Q_EMIT pressPositionChanged();
0186 
0187     m_pressAndHoldTimer->start();
0188 }
0189 
0190 void DelegateTouchArea::handleReleaseEvent(QPointerEvent *event, bool click)
0191 {
0192     // NOTE: event can be nullptr!
0193 
0194     bool wasPressed = m_pressed;
0195     setPressed(false);
0196 
0197     if (!m_pressAndHeld && click && wasPressed) {
0198         Q_EMIT clicked();
0199     }
0200 
0201     if (m_pressAndHeld) {
0202         Q_EMIT pressAndHoldReleased();
0203     }
0204 
0205     m_pressAndHoldTimer->stop();
0206     m_pressAndHeld = false;
0207 }
0208 
0209 void DelegateTouchArea::handleMoveEvent(QPointerEvent *event, QPointF point)
0210 {
0211     if (QPointF(point - m_mouseDownPosition).manhattanLength() >= QGuiApplication::styleHints()->startDragDistance()) {
0212         m_pressAndHoldTimer->stop();
0213         setPressed(false);
0214     }
0215 }
0216 
0217 void DelegateTouchArea::startPressAndHold()
0218 {
0219     m_pressAndHeld = true;
0220     Q_EMIT pressAndHold();
0221 }