As on phone keyboards: a sideways move on space moves the cursor right away, along the line only, without blanking the keys. Long-press space still opens the full trackpad. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
151 lines
3.8 KiB
C++
151 lines
3.8 KiB
C++
#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 spaceSlideSidewaysMovesAlongTheLine()
|
|
{
|
|
PointerTracker t(ofType(KeyType::Space), {100, 0}, config());
|
|
t.moveTo({112, 5});
|
|
QCOMPARE(t.gesture(), Gesture::Trackpad);
|
|
QCOMPARE(t.takeTrackpadSteps(), QPoint(1, 0)); // the slop counts
|
|
t.moveTo({75, -50});
|
|
QCOMPARE(t.takeTrackpadSteps(), QPoint(-3, 0)); // no line changes
|
|
QVERIFY(!t.longPressElapsed());
|
|
}
|
|
|
|
void spaceSlideVerticallyStaysTap()
|
|
{
|
|
PointerTracker t(ofType(KeyType::Space), {100, 0}, config());
|
|
t.moveTo({105, -20});
|
|
QCOMPARE(t.gesture(), Gesture::Tap);
|
|
}
|
|
|
|
void spaceSlideNeedsGestures()
|
|
{
|
|
PointerTracker::Config c = config();
|
|
c.gestures = false;
|
|
PointerTracker t(ofType(KeyType::Space), {100, 0}, c);
|
|
t.moveTo({140, 0});
|
|
QCOMPARE(t.gesture(), Gesture::Tap);
|
|
}
|
|
|
|
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"
|