Slide sideways on space to move the cursor

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>
This commit is contained in:
2026-10-25 17:51:09 +01:00
co-authored by Claude Opus 5.5
parent b745e9da1b
commit 441c1d615b
5 changed files with 51 additions and 4 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ iPad-style floating on-screen keyboard for Plasma Wayland. Qt 6, C++ core, QML U
- "Hold Keys" (⚙): keys stay down while touched, like a hardware keyboard (games, Shift+click, system key repeat); modifiers are plain keys, dead keys compose in the app; touch conveniences off - "Hold Keys" (⚙): keys stay down while touched, like a hardware keyboard (games, Shift+click, system key repeat); modifiers are plain keys, dead keys compose in the app; touch conveniences off
- Letters follow the system keyboard layout; the globe key switches it - Letters follow the system keyboard layout; the globe key switches it
- Auto-capitalization, double-space → ". " (both optional, ⚙ on the bottom bar), backspace repeat → word delete - Auto-capitalization, double-space → ". " (both optional, ⚙ on the bottom bar), backspace repeat → word delete
- Long-press space = trackpad; slide from `.?123` onto a symbol and release - Slide sideways on space to move the cursor; long-press space = trackpad (lines too); slide from `.?123` onto a symbol and release
- Compact (iPad) or full (iPad Pro) keyboard: number row, punctuation, tab, caps lock, ctrl/alt/super, dead keys - Compact (iPad) or full (iPad Pro) keyboard: number row, punctuation, tab, caps lock, ctrl/alt/super, dead keys
- Ctrl/Alt/Super stick like shift (tap, double-tap, hold) and turn keys into shortcuts; a tapped or held shift joins them (Shift+F5, Shift+Tab, Shift+arrows), auto-shift and caps lock don't - Ctrl/Alt/Super stick like shift (tap, double-tap, hold) and turn keys into shortcuts; a tapped or held shift joins them (Shift+F5, Shift+Tab, Shift+arrows), auto-shift and caps lock don't
- Optional PC key blocks: function row, navigation (ins/home/pg up, del/end/pg dn, arrows), numeric keypad; any mix, typing keys included (⚙) - Optional PC key blocks: function row, navigation (ins/home/pg up, del/end/pg dn, arrows), numeric keypad; any mix, typing keys included (⚙)
+9 -2
View File
@@ -26,8 +26,14 @@ void PointerTracker::moveTo(QPointF pos)
return; return;
m_moved = true; m_moved = true;
if (m_key.type == KeyType::LayerSwitch) if (m_key.type == KeyType::LayerSwitch) {
m_gesture = Gesture::Slide; m_gesture = Gesture::Slide;
} else if (m_config.gestures && m_key.type == KeyType::Space && qAbs(delta.x()) > qAbs(delta.y())) {
// From the press point: the slop counts towards the first step.
m_gesture = Gesture::Trackpad;
m_trackpadOrigin = m_start;
m_trackpadLines = false;
}
else if (m_config.gestures && !m_key.flick.isEmpty() && delta.y() > qAbs(delta.x())) else if (m_config.gestures && !m_key.flick.isEmpty() && delta.y() > qAbs(delta.x()))
m_gesture = Gesture::Flick; m_gesture = Gesture::Flick;
} }
@@ -63,7 +69,8 @@ QPoint PointerTracker::takeTrackpadSteps()
const QPointF travel = m_position - m_trackpadOrigin; const QPointF travel = m_position - m_trackpadOrigin;
// Truncate towards zero so jitter around the origin doesn't step. // Truncate towards zero so jitter around the origin doesn't step.
const QPoint total(int(travel.x() / m_config.trackpadStep), int(travel.y() / m_config.trackpadRowStep)); const int rows = m_trackpadLines ? int(travel.y() / m_config.trackpadRowStep) : 0;
const QPoint total(int(travel.x() / m_config.trackpadStep), rows);
const QPoint steps = total - m_trackpadStepsTaken; const QPoint steps = total - m_trackpadStepsTaken;
m_trackpadStepsTaken = total; m_trackpadStepsTaken = total;
return steps; return steps;
+3 -1
View File
@@ -11,7 +11,8 @@
// Tap pressed, not (yet) anything else // Tap pressed, not (yet) anything else
// Flick moved down on a key with a flick symbol // Flick moved down on a key with a flick symbol
// LongPress held still on a key with alternates // LongPress held still on a key with alternates
// Trackpad held still on space; movement moves the text cursor // Trackpad held still on space; movement moves the text cursor. Sliding
// sideways on space moves it too, along the line only.
// Slide moved off a layer key (type a symbol, then switch back) // Slide moved off a layer key (type a symbol, then switch back)
class PointerTracker class PointerTracker
{ {
@@ -56,5 +57,6 @@ private:
Gesture m_gesture = Gesture::Tap; Gesture m_gesture = Gesture::Tap;
bool m_moved = false; bool m_moved = false;
QPointF m_trackpadOrigin; QPointF m_trackpadOrigin;
bool m_trackpadLines = true; // vertical travel moves between lines
QPoint m_trackpadStepsTaken; QPoint m_trackpadStepsTaken;
}; };
+11
View File
@@ -208,6 +208,17 @@ private Q_SLOTS:
QCOMPARE(engine.output, QString()); QCOMPARE(engine.output, QString());
QCOMPARE(engine.keysyms, (QList<quint32>{XKB_KEY_Right, XKB_KEY_Right, XKB_KEY_Right})); QCOMPARE(engine.keysyms, (QList<quint32>{XKB_KEY_Right, XKB_KEY_Right, XKB_KEY_Right}));
} }
void spaceSlideMovesCursorAtOnce()
{
const QPoint space = keyCenter({}, u"space"_s);
QTest::touchEvent(&window, touchscreen).press(0, space);
QTest::touchEvent(&window, touchscreen).move(0, space - QPoint(30, 0));
QVERIFY(!qobject_cast<KeyboardSurface *>(keyboard)->isTrackpadActive()); // keys keep their labels
QTest::touchEvent(&window, touchscreen).release(0, space - QPoint(30, 0));
QCOMPARE(engine.output, QString());
QCOMPARE(engine.keysyms, (QList<quint32>{XKB_KEY_Left, XKB_KEY_Left}));
}
}; };
QTEST_MAIN(TestKeyboardSurface) QTEST_MAIN(TestKeyboardSurface)
+27
View File
@@ -111,6 +111,33 @@ private Q_SLOTS:
QCOMPARE(t.takeTrackpadSteps(), QPoint(-3, -1)); 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() void layerKeySlides()
{ {
PointerTracker t(ofType(KeyType::LayerSwitch), {0, 0}, config()); PointerTracker t(ofType(KeyType::LayerSwitch), {0, 0}, config());