aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@qt.io>2017-04-19 14:02:32 +0200
committerJ-P Nurmi <jpnurmi@qt.io>2017-04-20 06:12:39 +0000
commit211014bfcac08ae763f8b8c8497425c48eac6a77 (patch)
tree18ec60874ed73363e52ef85c1da8c28db2b6bbd0
parentce4961b063fa3bfae1996e23014d8f6b5a69f994 (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.cpp9
-rw-r--r--src/quicktemplates2/qquickabstractbutton_p_p.h1
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp7
-rw-r--r--src/quicktemplates2/qquickcontrol.cpp104
-rw-r--r--src/quicktemplates2/qquickcontrol_p.h3
-rw-r--r--src/quicktemplates2/qquickcontrol_p_p.h6
-rw-r--r--src/quicktemplates2/qquickdial.cpp9
-rw-r--r--src/quicktemplates2/qquickscrollbar.cpp6
-rw-r--r--src/quicktemplates2/qquickscrollbar_p_p.h1
-rw-r--r--src/quicktemplates2/qquickslider.cpp7
-rw-r--r--src/quicktemplates2/qquickspinbox.cpp7
-rw-r--r--tests/auto/focus/tst_focus.cpp55
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);