QQuickSwitchDelegate: handle touch events
This makes it possible to interact with multiple switch delegates at the same time. Change-Id: I40d1f31d2e361665b2e09b3bb071832f3efcd75b Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
dc5f909fe1
commit
2b60b354e6
|
@ -83,6 +83,10 @@ public:
|
|||
|
||||
qreal positionAt(const QPointF &point) const;
|
||||
|
||||
bool canDrag(const QPointF &movePoint) const;
|
||||
void handleMove(const QPointF &point) override;
|
||||
void handleRelease(const QPointF &point) override;
|
||||
|
||||
qreal position;
|
||||
};
|
||||
|
||||
|
@ -97,6 +101,32 @@ qreal QQuickSwitchDelegatePrivate::positionAt(const QPointF &point) const
|
|||
return pos;
|
||||
}
|
||||
|
||||
bool QQuickSwitchDelegatePrivate::canDrag(const QPointF &movePoint) const
|
||||
{
|
||||
// don't start dragging the handle unless the initial press was at the indicator,
|
||||
// or the drag has reached the indicator area. this prevents unnatural jumps when
|
||||
// dragging far outside the indicator.
|
||||
const qreal pressPos = positionAt(pressPoint);
|
||||
const qreal movePos = positionAt(movePoint);
|
||||
return (pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0);
|
||||
}
|
||||
|
||||
void QQuickSwitchDelegatePrivate::handleMove(const QPointF &point)
|
||||
{
|
||||
Q_Q(QQuickSwitchDelegate);
|
||||
QQuickItemDelegatePrivate::handleMove(point);
|
||||
if (q->keepMouseGrab() || q->keepTouchGrab())
|
||||
q->setPosition(positionAt(point));
|
||||
}
|
||||
|
||||
void QQuickSwitchDelegatePrivate::handleRelease(const QPointF &point)
|
||||
{
|
||||
Q_Q(QQuickSwitchDelegate);
|
||||
QQuickItemDelegatePrivate::handleRelease(point);
|
||||
q->setKeepMouseGrab(false);
|
||||
q->setKeepTouchGrab(false);
|
||||
}
|
||||
|
||||
QQuickSwitchDelegate::QQuickSwitchDelegate(QQuickItem *parent)
|
||||
: QQuickItemDelegate(*(new QQuickSwitchDelegatePrivate), parent)
|
||||
{
|
||||
|
@ -143,35 +173,29 @@ qreal QQuickSwitchDelegate::visualPosition() const
|
|||
return d->position;
|
||||
}
|
||||
|
||||
void QQuickSwitchDelegate::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
QQuickItemDelegate::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void QQuickSwitchDelegate::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_D(QQuickSwitchDelegate);
|
||||
QQuickItemDelegate::mouseMoveEvent(event);
|
||||
|
||||
const QPointF movePoint = event->localPos();
|
||||
if (!keepMouseGrab()) {
|
||||
// don't start dragging the handle unless the initial press was at the indicator,
|
||||
// or the drag has reached the indicator area. this prevents unnatural jumps when
|
||||
// dragging far outside the indicator.
|
||||
const qreal pressPos = d->positionAt(d->pressPoint);
|
||||
const qreal movePos = d->positionAt(movePoint);
|
||||
if ((pressPos >= 0.0 && pressPos <= 1.0) || (movePos >= 0.0 && movePos <= 1.0))
|
||||
const QPointF movePoint = event->localPos();
|
||||
if (d->canDrag(movePoint))
|
||||
setKeepMouseGrab(QQuickWindowPrivate::dragOverThreshold(movePoint.x() - d->pressPoint.x(), Qt::XAxis, event));
|
||||
}
|
||||
|
||||
if (keepMouseGrab())
|
||||
setPosition(d->positionAt(movePoint));
|
||||
QQuickItemDelegate::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void QQuickSwitchDelegate::mouseReleaseEvent(QMouseEvent *event)
|
||||
void QQuickSwitchDelegate::touchEvent(QTouchEvent *event)
|
||||
{
|
||||
QQuickItemDelegate::mouseReleaseEvent(event);
|
||||
setKeepMouseGrab(false);
|
||||
Q_D(QQuickSwitchDelegate);
|
||||
if (!keepTouchGrab() && event->type() == QEvent::TouchUpdate) {
|
||||
for (const QTouchEvent::TouchPoint &point : event->touchPoints()) {
|
||||
if (point.id() != d->touchId || point.state() != Qt::TouchPointMoved)
|
||||
continue;
|
||||
if (d->canDrag(point.pos()))
|
||||
setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(point.pos().x() - d->pressPoint.x(), Qt::XAxis, &point));
|
||||
}
|
||||
}
|
||||
QQuickItemDelegate::touchEvent(event);
|
||||
}
|
||||
|
||||
QFont QQuickSwitchDelegate::defaultFont() const
|
||||
|
@ -188,7 +212,7 @@ void QQuickSwitchDelegate::mirrorChange()
|
|||
void QQuickSwitchDelegate::nextCheckState()
|
||||
{
|
||||
Q_D(QQuickSwitchDelegate);
|
||||
if (keepMouseGrab()) {
|
||||
if (keepMouseGrab() || keepTouchGrab()) {
|
||||
d->toggle(d->position > 0.5);
|
||||
// the checked state might not change => force a position update to
|
||||
// avoid that the handle is left somewhere in the middle (QTBUG-57944)
|
||||
|
|
|
@ -73,9 +73,8 @@ Q_SIGNALS:
|
|||
void visualPositionChanged();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void touchEvent(QTouchEvent *event) override;
|
||||
|
||||
QFont defaultFont() const override;
|
||||
void mirrorChange() override;
|
||||
|
|
|
@ -209,7 +209,100 @@ TestCase {
|
|||
verify(spy.success)
|
||||
}
|
||||
|
||||
function test_drag() {
|
||||
function test_touch() {
|
||||
var control = createTemporaryObject(switchDelegate, testCase)
|
||||
verify(control)
|
||||
|
||||
var touch = touchEvent(control)
|
||||
|
||||
// check
|
||||
var spy = signalSequenceSpy.createObject(control, {target: control})
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
|
||||
"pressed"]
|
||||
touch.press(0, control, control.width / 2, control.height / 2).commit()
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }],
|
||||
["checkedChanged", { "pressed": false, "checked": true }],
|
||||
"toggled",
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control, control.width / 2, control.height / 2).commit()
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
|
||||
// uncheck
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
|
||||
"pressed"]
|
||||
touch.press(0, control, control.width / 2, control.height / 2).commit()
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }],
|
||||
["checkedChanged", { "pressed": false, "checked": false }],
|
||||
"toggled",
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control, control.width / 2, control.height / 2).commit()
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
|
||||
// release on the right
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
|
||||
"pressed"]
|
||||
touch.press(0, control, control.width / 2, control.height / 2).commit()
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
touch.move(0, control, control.width * 2, control.height / 2).commit()
|
||||
compare(control.pressed, true)
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }],
|
||||
["checkedChanged", { "pressed": false, "checked": true }],
|
||||
"toggled",
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control, control.width * 2, control.height / 2).commit()
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
|
||||
// release on the left
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
|
||||
"pressed"]
|
||||
touch.press(0, control, control.width / 2, control.height / 2).commit()
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
touch.move(0, control, -control.width, control.height / 2).commit()
|
||||
compare(control.pressed, true)
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }],
|
||||
["checkedChanged", { "pressed": false, "checked": false }],
|
||||
"toggled",
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control, -control.width, control.height / 2).commit()
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
|
||||
// release in the middle
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
|
||||
"pressed"]
|
||||
touch.press(0, control.indicator, 0, 0).commit()
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
touch.move(0, control.indicator, control.indicator.width / 2 - 1).commit()
|
||||
compare(control.pressed, true)
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }],
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control.indicator, control.indicator.width / 2 - 1, 0).commit()
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, false)
|
||||
tryCompare(control, "position", 0) // QTBUG-57944
|
||||
verify(spy.success)
|
||||
}
|
||||
|
||||
function test_mouseDrag() {
|
||||
var control = createTemporaryObject(switchDelegate, testCase, {leftPadding: 100, rightPadding: 100})
|
||||
verify(control)
|
||||
|
||||
|
@ -313,4 +406,111 @@ TestCase {
|
|||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
}
|
||||
|
||||
function test_touchDrag() {
|
||||
var control = createTemporaryObject(switchDelegate, testCase, {leftPadding: 100, rightPadding: 100})
|
||||
verify(control)
|
||||
|
||||
var touch = touchEvent(control)
|
||||
|
||||
var spy = signalSequenceSpy.createObject(control, {target: control})
|
||||
compare(control.position, 0.0)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, false)
|
||||
|
||||
// press-drag-release inside the indicator
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
|
||||
"pressed"]
|
||||
touch.press(0, control.indicator, 0).commit()
|
||||
compare(control.position, 0.0)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
|
||||
touch.move(0, control.indicator, control.width).commit()
|
||||
compare(control.position, 1.0)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, true)
|
||||
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }],
|
||||
["checkedChanged", { "pressed": false, "checked": true }],
|
||||
"toggled",
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control.indicator, control.indicator.width).commit()
|
||||
compare(control.position, 1.0)
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
|
||||
// press-drag-release outside the indicator
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }],
|
||||
"pressed"]
|
||||
touch.press(0, control, 0).commit()
|
||||
compare(control.position, 1.0)
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
|
||||
touch.move(0, control, control.width - control.rightPadding).commit()
|
||||
compare(control.position, 1.0)
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, true)
|
||||
|
||||
touch.move(0, control, control.width / 2).commit()
|
||||
compare(control.position, 0.5)
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, true)
|
||||
|
||||
touch.move(0, control, control.leftPadding).commit()
|
||||
compare(control.position, 0.0)
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, true)
|
||||
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }],
|
||||
["checkedChanged", { "pressed": false, "checked": false }],
|
||||
"toggled",
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control, control.width).commit()
|
||||
compare(control.position, 0.0)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
|
||||
// press-drag-release from and to outside the indicator
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }],
|
||||
"pressed"]
|
||||
touch.press(0, control, control.width).commit()
|
||||
compare(control.position, 0.0)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, true)
|
||||
verify(spy.success)
|
||||
|
||||
touch.move(0, control, control.width - control.rightPadding).commit()
|
||||
compare(control.position, 0.0)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, true)
|
||||
|
||||
touch.move(0, control, control.width / 2).commit()
|
||||
compare(control.position, 0.5)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, true)
|
||||
|
||||
touch.move(0, control, control.width - control.rightPadding).commit()
|
||||
compare(control.position, 1.0)
|
||||
compare(control.checked, false)
|
||||
compare(control.pressed, true)
|
||||
|
||||
spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }],
|
||||
["checkedChanged", { "pressed": false, "checked": true }],
|
||||
"toggled",
|
||||
"released",
|
||||
"clicked"]
|
||||
touch.release(0, control, control.width).commit()
|
||||
compare(control.position, 1.0)
|
||||
compare(control.checked, true)
|
||||
compare(control.pressed, false)
|
||||
verify(spy.success)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue