diff options
author | J-P Nurmi <jpnurmi@qt.io> | 2017-04-19 14:02:32 +0200 |
---|---|---|
committer | J-P Nurmi <jpnurmi@qt.io> | 2017-04-20 06:12:39 +0000 |
commit | 211014bfcac08ae763f8b8c8497425c48eac6a77 (patch) | |
tree | 18ec60874ed73363e52ef85c1da8c28db2b6bbd0 | |
parent | ce4961b063fa3bfae1996e23014d8f6b5a69f994 (diff) |
QQuickControl: implement focus handling on touch
QQuickControl::focusPolicy was only managed in mousePressEvent() and
mouseReleaseEvent(). All controls call the base class implementations
of these event handlers to get the focus policy handling "for free".
Move focus policy handling to handlePress() and handleRelease() that
can be re-used for touch events, and make sure that various controls
call the base class implementation of touch event handlers.
There's still a bit of duplication in QQuickControl::touchEvent() and
the overridden handlers in sub-classes, but this will be improved step
by step. QQuickControlPrivate::handlePress/Move/Release/Ungrab() are
planned to be made virtual, overridden in subclasses, and only called
from QQuickControl event handlers. Most mouse and touch event handlers
in QQuickControl subclasses can be removed later when we get there.
Task-number: QTBUG-58389
Change-Id: I7e82dc2ef49762a005c482ce1353e03cc841659f
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r-- | src/quicktemplates2/qquickabstractbutton.cpp | 9 | ||||
-rw-r--r-- | src/quicktemplates2/qquickabstractbutton_p_p.h | 1 | ||||
-rw-r--r-- | src/quicktemplates2/qquickcombobox.cpp | 7 | ||||
-rw-r--r-- | src/quicktemplates2/qquickcontrol.cpp | 104 | ||||
-rw-r--r-- | src/quicktemplates2/qquickcontrol_p.h | 3 | ||||
-rw-r--r-- | src/quicktemplates2/qquickcontrol_p_p.h | 6 | ||||
-rw-r--r-- | src/quicktemplates2/qquickdial.cpp | 9 | ||||
-rw-r--r-- | src/quicktemplates2/qquickscrollbar.cpp | 6 | ||||
-rw-r--r-- | src/quicktemplates2/qquickscrollbar_p_p.h | 1 | ||||
-rw-r--r-- | src/quicktemplates2/qquickslider.cpp | 7 | ||||
-rw-r--r-- | src/quicktemplates2/qquickspinbox.cpp | 7 | ||||
-rw-r--r-- | tests/auto/focus/tst_focus.cpp | 55 |
12 files changed, 172 insertions, 43 deletions
diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp index 7c2ced66..de954570 100644 --- a/src/quicktemplates2/qquickabstractbutton.cpp +++ b/src/quicktemplates2/qquickabstractbutton.cpp @@ -121,7 +121,6 @@ QQuickAbstractButtonPrivate::QQuickAbstractButtonPrivate() autoExclusive(false), autoRepeat(false), wasHeld(false), - touchId(-1), holdTimer(0), delayTimer(0), repeatTimer(0), @@ -165,7 +164,6 @@ void QQuickAbstractButtonPrivate::handleRelease(const QPointF &point) Q_Q(QQuickAbstractButton); bool wasPressed = pressed; q->setPressed(false); - touchId = -1; if (!wasHeld && (keepPressed || q->contains(point))) q->nextCheckState(); @@ -191,7 +189,6 @@ void QQuickAbstractButtonPrivate::handleCancel() return; q->setPressed(false); - touchId = -1; stopPressRepeat(); stopPressAndHold(); emit q->canceled(); @@ -579,7 +576,8 @@ void QQuickAbstractButton::focusOutEvent(QFocusEvent *event) { Q_D(QQuickAbstractButton); QQuickControl::focusOutEvent(event); - d->handleCancel(); + if (d->touchId == -1) // don't ungrab on multi-touch if another control gets focused + d->handleCancel(); } void QQuickAbstractButton::keyPressEvent(QKeyEvent *event) @@ -713,9 +711,10 @@ void QQuickAbstractButton::touchEvent(QTouchEvent *event) break; default: - QQuickControl::touchEvent(event); break; } + + QQuickControl::touchEvent(event); } void QQuickAbstractButton::touchUngrabEvent() diff --git a/src/quicktemplates2/qquickabstractbutton_p_p.h b/src/quicktemplates2/qquickabstractbutton_p_p.h index e27f5c82..6d149b4f 100644 --- a/src/quicktemplates2/qquickabstractbutton_p_p.h +++ b/src/quicktemplates2/qquickabstractbutton_p_p.h @@ -95,7 +95,6 @@ public: bool autoExclusive; bool autoRepeat; bool wasHeld; - int touchId; int holdTimer; int delayTimer; int repeatTimer; diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp index 09a5eede..5c763d21 100644 --- a/src/quicktemplates2/qquickcombobox.cpp +++ b/src/quicktemplates2/qquickcombobox.cpp @@ -257,7 +257,6 @@ public: bool hasCurrentIndex; int highlightedIndex; int currentIndex; - int touchId; QVariant model; QString textRole; QString currentText; @@ -296,7 +295,6 @@ QQuickComboBoxPrivate::QQuickComboBoxPrivate() hasCurrentIndex(false), highlightedIndex(-1), currentIndex(-1), - touchId(-1), delegateModel(nullptr), delegate(nullptr), indicator(nullptr), @@ -648,14 +646,12 @@ void QQuickComboBoxPrivate::handleRelease(const QPointF &) q->setPressed(false); togglePopup(false); } - touchId = -1; } void QQuickComboBoxPrivate::handleUngrab() { Q_Q(QQuickComboBox); q->setPressed(false); - touchId = -1; } QQuickComboBox::QQuickComboBox(QQuickItem *parent) @@ -1580,9 +1576,10 @@ void QQuickComboBox::touchEvent(QTouchEvent *event) break; default: - QQuickControl::touchEvent(event); break; } + + QQuickControl::touchEvent(event); } void QQuickComboBox::touchUngrabEvent() diff --git a/src/quicktemplates2/qquickcontrol.cpp b/src/quicktemplates2/qquickcontrol.cpp index eb66f313..b086f3ee 100644 --- a/src/quicktemplates2/qquickcontrol.cpp +++ b/src/quicktemplates2/qquickcontrol.cpp @@ -120,6 +120,7 @@ QQuickControlPrivate::QQuickControlPrivate() hovered(false), explicitHoverEnabled(false), #endif + touchId(-1), padding(0), topPadding(0), leftPadding(0), @@ -144,6 +145,36 @@ QQuickControlPrivate::~QQuickControlPrivate() #endif } +void QQuickControlPrivate::handlePress(const QPointF &) +{ + Q_Q(QQuickControl); + if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) + q->forceActiveFocus(Qt::MouseFocusReason); +} + +void QQuickControlPrivate::handleMove(const QPointF &point) +{ +#if QT_CONFIG(quicktemplates2_hover) + Q_Q(QQuickControl); + q->setHovered(hoverEnabled && q->contains(point)); +#else + Q_UNUSED(point); +#endif +} + +void QQuickControlPrivate::handleRelease(const QPointF &) +{ + Q_Q(QQuickControl); + if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) + q->forceActiveFocus(Qt::MouseFocusReason); + touchId = -1; +} + +void QQuickControlPrivate::handleUngrab() +{ + touchId = -1; +} + void QQuickControlPrivate::mirrorChange() { Q_Q(QQuickControl); @@ -1287,30 +1318,85 @@ void QQuickControl::hoverLeaveEvent(QHoverEvent *event) void QQuickControl::mousePressEvent(QMouseEvent *event) { Q_D(QQuickControl); - if ((d->focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) - forceActiveFocus(Qt::MouseFocusReason); - + d->handlePress(event->localPos()); event->accept(); } void QQuickControl::mouseMoveEvent(QMouseEvent *event) { -#if QT_CONFIG(quicktemplates2_hover) Q_D(QQuickControl); - setHovered(d->hoverEnabled && contains(event->pos())); -#endif + d->handleMove(event->localPos()); event->accept(); } void QQuickControl::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickControl); - if ((d->focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) - forceActiveFocus(Qt::MouseFocusReason); - + d->handleRelease(event->localPos()); event->accept(); } +void QQuickControl::mouseUngrabEvent() +{ + Q_D(QQuickControl); + d->handleUngrab(); +} + +void QQuickControl::touchEvent(QTouchEvent *event) +{ + Q_D(QQuickControl); + switch (event->type()) { + case QEvent::TouchBegin: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (point.id() == d->touchId) + d->handlePress(point.pos()); + } + break; + + case QEvent::TouchUpdate: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (point.id() != d->touchId) + continue; + + switch (point.state()) { + case Qt::TouchPointPressed: + d->handlePress(point.pos()); + break; + case Qt::TouchPointMoved: + d->handleMove(point.pos()); + break; + case Qt::TouchPointReleased: + d->handleRelease(point.pos()); + break; + default: + break; + } + } + break; + + case QEvent::TouchEnd: + for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { + if (point.id() == d->touchId) + d->handleRelease(point.pos()); + } + break; + + case QEvent::TouchCancel: + d->handleUngrab(); + break; + + default: + QQuickItem::touchEvent(event); + break; + } +} + +void QQuickControl::touchUngrabEvent() +{ + Q_D(QQuickControl); + d->handleUngrab(); +} + #if QT_CONFIG(wheelevent) void QQuickControl::wheelEvent(QWheelEvent *event) { diff --git a/src/quicktemplates2/qquickcontrol_p.h b/src/quicktemplates2/qquickcontrol_p.h index a94ef01d..6ad89376 100644 --- a/src/quicktemplates2/qquickcontrol_p.h +++ b/src/quicktemplates2/qquickcontrol_p.h @@ -184,6 +184,9 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void touchEvent(QTouchEvent *event) override; + void touchUngrabEvent() override; #if QT_CONFIG(wheelevent) void wheelEvent(QWheelEvent *event) override; #endif diff --git a/src/quicktemplates2/qquickcontrol_p_p.h b/src/quicktemplates2/qquickcontrol_p_p.h index 14a468f3..2c79c887 100644 --- a/src/quicktemplates2/qquickcontrol_p_p.h +++ b/src/quicktemplates2/qquickcontrol_p_p.h @@ -78,6 +78,11 @@ public: return control->d_func(); } + void handlePress(const QPointF &point); + void handleMove(const QPointF &point); + void handleRelease(const QPointF &point); + void handleUngrab(); + void mirrorChange() override; void setTopPadding(qreal value, bool reset = false); @@ -136,6 +141,7 @@ public: bool hovered; bool explicitHoverEnabled; #endif + int touchId; qreal padding; qreal topPadding; qreal leftPadding; diff --git a/src/quicktemplates2/qquickdial.cpp b/src/quicktemplates2/qquickdial.cpp index 5b98992b..285dda49 100644 --- a/src/quicktemplates2/qquickdial.cpp +++ b/src/quicktemplates2/qquickdial.cpp @@ -96,8 +96,7 @@ class QQuickDialPrivate : public QQuickControlPrivate public: QQuickDialPrivate() - : touchId(-1), - from(0), + : from(0), to(1), value(0), position(0), @@ -123,7 +122,6 @@ public: void handleRelease(const QPointF &point); void handleUngrab(); - int touchId; qreal from; qreal to; qreal value; @@ -242,14 +240,12 @@ void QQuickDialPrivate::handleRelease(const QPointF &point) q->setPressed(false); pressPoint = QPointF(); - touchId = -1; } void QQuickDialPrivate::handleUngrab() { Q_Q(QQuickDial); pressPoint = QPointF(); - touchId = -1; q->setPressed(false); } @@ -729,9 +725,10 @@ void QQuickDial::touchEvent(QTouchEvent *event) break; default: - QQuickControl::touchEvent(event); break; } + + QQuickControl::touchEvent(event); } void QQuickDial::touchUngrabEvent() diff --git a/src/quicktemplates2/qquickscrollbar.cpp b/src/quicktemplates2/qquickscrollbar.cpp index b0d55dfb..be97f071 100644 --- a/src/quicktemplates2/qquickscrollbar.cpp +++ b/src/quicktemplates2/qquickscrollbar.cpp @@ -165,7 +165,6 @@ QQuickScrollBarPrivate::QQuickScrollBarPrivate() moving(false), interactive(true), explicitInteractive(false), - touchId(-1), orientation(Qt::Vertical), snapMode(QQuickScrollBar::NoSnap), policy(QQuickScrollBar::AsNeeded) @@ -269,7 +268,6 @@ void QQuickScrollBarPrivate::handleRelease(const QPointF &point) pos = snapPosition(pos); q->setPosition(pos); offset = 0.0; - touchId = -1; q->setPressed(false); } @@ -277,7 +275,6 @@ void QQuickScrollBarPrivate::handleUngrab() { Q_Q(QQuickScrollBar); offset = 0.0; - touchId = -1; q->setPressed(false); } @@ -657,9 +654,10 @@ void QQuickScrollBar::touchEvent(QTouchEvent *event) break; default: - QQuickControl::touchEvent(event); break; } + + QQuickControl::touchEvent(event); } void QQuickScrollBar::touchUngrabEvent() diff --git a/src/quicktemplates2/qquickscrollbar_p_p.h b/src/quicktemplates2/qquickscrollbar_p_p.h index 409b952b..cbbef2c8 100644 --- a/src/quicktemplates2/qquickscrollbar_p_p.h +++ b/src/quicktemplates2/qquickscrollbar_p_p.h @@ -88,7 +88,6 @@ public: bool moving; bool interactive; bool explicitInteractive; - int touchId; Qt::Orientation orientation; QQuickScrollBar::SnapMode snapMode; QQuickScrollBar::Policy policy; diff --git a/src/quicktemplates2/qquickslider.cpp b/src/quicktemplates2/qquickslider.cpp index 77e5856c..ef074c73 100644 --- a/src/quicktemplates2/qquickslider.cpp +++ b/src/quicktemplates2/qquickslider.cpp @@ -96,7 +96,6 @@ public: stepSize(0), live(true), pressed(false), - touchId(-1), orientation(Qt::Horizontal), snapMode(QQuickSlider::NoSnap), handle(nullptr) @@ -120,7 +119,6 @@ public: qreal stepSize; bool live; bool pressed; - int touchId; QPointF pressPoint; Qt::Orientation orientation; QQuickSlider::SnapMode snapMode; @@ -209,7 +207,6 @@ void QQuickSliderPrivate::handleMove(const QPointF &point) void QQuickSliderPrivate::handleRelease(const QPointF &point) { Q_Q(QQuickSlider); - touchId = -1; pressPoint = QPointF(); const qreal oldPos = position; qreal pos = positionAt(point); @@ -230,7 +227,6 @@ void QQuickSliderPrivate::handleRelease(const QPointF &point) void QQuickSliderPrivate::handleUngrab() { Q_Q(QQuickSlider); - touchId = -1; pressPoint = QPointF(); q->setPressed(false); } @@ -693,9 +689,10 @@ void QQuickSlider::touchEvent(QTouchEvent *event) break; default: - QQuickControl::touchEvent(event); break; } + + QQuickControl::touchEvent(event); } void QQuickSlider::touchUngrabEvent() diff --git a/src/quicktemplates2/qquickspinbox.cpp b/src/quicktemplates2/qquickspinbox.cpp index b70366ea..9558cfc5 100644 --- a/src/quicktemplates2/qquickspinbox.cpp +++ b/src/quicktemplates2/qquickspinbox.cpp @@ -114,7 +114,6 @@ public: stepSize(1), delayTimer(0), repeatTimer(0), - touchId(-1), up(nullptr), down(nullptr), validator(nullptr), @@ -150,7 +149,6 @@ public: int stepSize; int delayTimer; int repeatTimer; - int touchId; QQuickSpinButton *up; QQuickSpinButton *down; QValidator *validator; @@ -323,7 +321,6 @@ bool QQuickSpinBoxPrivate::handleRelease(const QPointF &point) if (value != oldValue) emit q->valueModified(); - touchId = -1; q->setAccessibleProperty("pressed", false); stopPressRepeat(); return wasPressed; @@ -335,7 +332,6 @@ bool QQuickSpinBoxPrivate::handleUngrab() up->setPressed(false); down->setPressed(false); - touchId = -1; q->setAccessibleProperty("pressed", false); stopPressRepeat(); return false; @@ -870,9 +866,10 @@ void QQuickSpinBox::touchEvent(QTouchEvent *event) break; default: - QQuickControl::touchEvent(event); break; } + + QQuickControl::touchEvent(event); } void QQuickSpinBox::touchUngrabEvent() diff --git a/tests/auto/focus/tst_focus.cpp b/tests/auto/focus/tst_focus.cpp index e423835c..c5c24233 100644 --- a/tests/auto/focus/tst_focus.cpp +++ b/tests/auto/focus/tst_focus.cpp @@ -44,6 +44,7 @@ #include <QtQuickTemplates2/private/qquickcontrol_p.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/qstylehints.h> +#include <QtGui/qtouchdevice.h> #include "../shared/util.h" #include "../shared/visualtestutil.h" @@ -59,6 +60,7 @@ private slots: void navigation_data(); void navigation(); + void policy_data(); void policy(); void reason_data(); @@ -122,11 +124,24 @@ void tst_focus::navigation() QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusBehavior(-1)); } +void tst_focus::policy_data() +{ + QTest::addColumn<QString>("name"); + + QTest::newRow("Control") << "Control"; + QTest::newRow("ComboBox") << "ComboBox"; + QTest::newRow("Button") << "Button"; + QTest::newRow("Slider") << "Slider"; + QTest::newRow("ScrollBar") << "ScrollBar"; +} + void tst_focus::policy() { + QFETCH(QString, name); + QQmlEngine engine; QQmlComponent component(&engine); - component.setData("import QtQuick.Controls 2.1; ApplicationWindow { width: 100; height: 100; Control { anchors.fill: parent } }", QUrl()); + component.setData(QString("import QtQuick.Controls 2.1; ApplicationWindow { width: 100; height: 100; %1 { anchors.fill: parent } }").arg(name).toUtf8(), QUrl()); QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(component.create())); QVERIFY(window); @@ -141,6 +156,22 @@ void tst_focus::policy() window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window.data())); + struct TouchDeviceDeleter + { + static inline void cleanup(QTouchDevice *device) + { + QWindowSystemInterface::unregisterTouchDevice(device); + delete device; + } + }; + + QScopedPointer<QTouchDevice, TouchDeviceDeleter> device(new QTouchDevice); + device->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(device.data()); + + control->setFocusPolicy(Qt::NoFocus); + QCOMPARE(control->focusPolicy(), Qt::NoFocus); + // Qt::TabFocus vs. QQuickItem::activeFocusOnTab control->setActiveFocusOnTab(true); QCOMPARE(control->focusPolicy(), Qt::TabFocus); @@ -162,7 +193,8 @@ void tst_focus::policy() control->setFocus(false); QVERIFY(!control->hasActiveFocus()); - // Qt::ClickFocus + // Qt::ClickFocus (mouse) + control->setFocusPolicy(Qt::NoFocus); control->setAcceptedMouseButtons(Qt::LeftButton); QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(control->width() / 2, control->height() / 2)); QVERIFY(!control->hasActiveFocus()); @@ -178,6 +210,25 @@ void tst_focus::policy() control->setFocus(false); QVERIFY(!control->hasActiveFocus()); + // Qt::ClickFocus (touch) + control->setFocusPolicy(Qt::NoFocus); + QTest::touchEvent(window.data(), device.data()).press(0, QPoint(control->width() / 2, control->height() / 2)); + QTest::touchEvent(window.data(), device.data()).release(0, QPoint(control->width() / 2, control->height() / 2)); + QVERIFY(!control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + control->setFocusPolicy(Qt::ClickFocus); + QCOMPARE(control->focusPolicy(), Qt::ClickFocus); + QTest::touchEvent(window.data(), device.data()).press(0, QPoint(control->width() / 2, control->height() / 2)); + QTest::touchEvent(window.data(), device.data()).release(0, QPoint(control->width() / 2, control->height() / 2)); + QEXPECT_FAIL("Control", "TODO: Control::touchEvent should set the touchId", Continue); + QVERIFY(control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + // reset + control->setFocus(false); + QVERIFY(!control->hasActiveFocus()); + // Qt::WheelFocus QWheelEvent wheelEvent(QPoint(control->width() / 2, control->height() / 2), 10, Qt::NoButton, Qt::NoModifier); QGuiApplication::sendEvent(control, &wheelEvent); |