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:
J-P Nurmi 2017-01-24 11:10:15 +01:00
parent dc5f909fe1
commit 2b60b354e6
3 changed files with 247 additions and 24 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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)
}
}