Gestures: flick, long-press alternates, trackpad, layer slide, rollover
- PointerTracker: per-finger gesture state machine (pure, unit-tested) - AlternatesPicker + AlternatesPopup.qml: long-press choices, finger selects - Space long-press trackpad moves the cursor with arrow keysyms - Slide from .?123 onto a symbol types it and switches back - New finger commits pending taps (iOS rollover) - Flick animation: secondary symbol slides down as the finger moves - Transparent popup margin with an input region that passes clicks through - tst_keyboardsurface: end-to-end gesture tests on the real QML Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
#include "input/PointerTracker.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
using Gesture = PointerTracker::Gesture;
|
||||
|
||||
namespace {
|
||||
|
||||
KeyDef letter(const QString &flick = {}, const QStringList &alternates = {})
|
||||
{
|
||||
KeyDef k;
|
||||
k.label = u"e"_s;
|
||||
k.flick = flick;
|
||||
k.alternates = alternates;
|
||||
return k;
|
||||
}
|
||||
|
||||
KeyDef ofType(KeyType type)
|
||||
{
|
||||
KeyDef k;
|
||||
k.type = type;
|
||||
return k;
|
||||
}
|
||||
|
||||
PointerTracker::Config config()
|
||||
{
|
||||
PointerTracker::Config c;
|
||||
c.slop = 10;
|
||||
c.flickDistance = 30;
|
||||
c.trackpadStep = 10;
|
||||
c.trackpadRowStep = 40;
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class TestPointerTracker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void smallMovementStaysTap()
|
||||
{
|
||||
PointerTracker t(letter(u"3"_s), {0, 0}, config());
|
||||
t.moveTo({5, 8});
|
||||
QCOMPARE(t.gesture(), Gesture::Tap);
|
||||
}
|
||||
|
||||
void downwardMovementFlicks()
|
||||
{
|
||||
PointerTracker t(letter(u"3"_s), {0, 0}, config());
|
||||
t.moveTo({2, 15});
|
||||
QCOMPARE(t.gesture(), Gesture::Flick);
|
||||
QCOMPARE(t.flickProgress(), 0.5);
|
||||
QVERIFY(!t.flickCommitted());
|
||||
t.moveTo({2, 45});
|
||||
QVERIFY(t.flickCommitted());
|
||||
}
|
||||
|
||||
void flickBackUpUncommits()
|
||||
{
|
||||
PointerTracker t(letter(u"3"_s), {0, 0}, config());
|
||||
t.moveTo({0, 40});
|
||||
t.moveTo({0, 5});
|
||||
QCOMPARE(t.gesture(), Gesture::Flick);
|
||||
QVERIFY(!t.flickCommitted());
|
||||
}
|
||||
|
||||
void sidewaysMovementDoesNotFlick()
|
||||
{
|
||||
PointerTracker t(letter(u"3"_s), {0, 0}, config());
|
||||
t.moveTo({30, 15});
|
||||
QCOMPARE(t.gesture(), Gesture::Tap);
|
||||
}
|
||||
|
||||
void keysWithoutFlickNeverFlick()
|
||||
{
|
||||
PointerTracker t(letter(), {0, 0}, config());
|
||||
t.moveTo({0, 40});
|
||||
QCOMPARE(t.gesture(), Gesture::Tap);
|
||||
}
|
||||
|
||||
void holdingStillOpensAlternates()
|
||||
{
|
||||
PointerTracker t(letter({}, {u"é"_s}), {0, 0}, config());
|
||||
QVERIFY(t.wantsLongPress());
|
||||
QVERIFY(t.longPressElapsed());
|
||||
QCOMPARE(t.gesture(), Gesture::LongPress);
|
||||
}
|
||||
|
||||
void movingCancelsLongPress()
|
||||
{
|
||||
PointerTracker t(letter({}, {u"é"_s}), {0, 0}, config());
|
||||
t.moveTo({20, 0});
|
||||
QVERIFY(!t.longPressElapsed());
|
||||
QCOMPARE(t.gesture(), Gesture::Tap);
|
||||
}
|
||||
|
||||
void spaceLongPressBecomesTrackpad()
|
||||
{
|
||||
PointerTracker t(ofType(KeyType::Space), {100, 0}, config());
|
||||
QVERIFY(t.longPressElapsed());
|
||||
QCOMPARE(t.gesture(), Gesture::Trackpad);
|
||||
|
||||
t.moveTo({125, 0});
|
||||
QCOMPARE(t.takeTrackpadSteps(), QPoint(2, 0));
|
||||
t.moveTo({131, 0});
|
||||
QCOMPARE(t.takeTrackpadSteps(), QPoint(1, 0));
|
||||
t.moveTo({95, -45});
|
||||
QCOMPARE(t.takeTrackpadSteps(), QPoint(-3, -1));
|
||||
}
|
||||
|
||||
void layerKeySlides()
|
||||
{
|
||||
PointerTracker t(ofType(KeyType::LayerSwitch), {0, 0}, config());
|
||||
t.moveTo({40, -30});
|
||||
QCOMPARE(t.gesture(), Gesture::Slide);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(TestPointerTracker)
|
||||
#include "tst_pointertracker.moc"
|
||||
Reference in New Issue
Block a user