Hold Keys mode: keys stay down while touched, like a hardware keyboard

- HardwareKeys presses a key's unshifted keysym on touch and releases it
  on lift, through fake input; held keys repeat and combine as on a
  physical keyboard (games, Shift+click)
- Shift/Ctrl/Alt/Super are plain held keys, caps lock is the system's,
  dead keys compose in the app
- No long-press, flick, trackpad or rollover in this mode; nothing is
  left held when the mode is turned off or touches are cancelled
- Settings switch; default stays the touch (virtual keyboard) mode

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 22:17:11 +02:00
co-authored by Claude Opus 5.5
parent f816135162
commit e02b1a8237
24 changed files with 319 additions and 6 deletions
+36
View File
@@ -22,6 +22,7 @@ KeyboardController::KeyboardController(InputEngine &engine, LayoutModel &model,
: QObject(parent)
, m_engine(engine)
, m_model(model)
, m_hardware(engine)
, m_keysymRepeater(KeysymRepeat)
{
connect(&m_backspaceRepeater, &KeyRepeater::repeat, this, &KeyboardController::deleteBackward);
@@ -30,11 +31,34 @@ KeyboardController::KeyboardController(InputEngine &engine, LayoutModel &model,
KeyboardController::ShiftMode KeyboardController::shiftMode() const
{
if (m_options.holdKeys) {
if (m_hardware.isCapsLockOn())
return ShiftMode::Locked;
return m_hardware.isShiftHeld() ? ShiftMode::Once : ShiftMode::Off;
}
return ShiftMode(m_shift.mode());
}
QVariantMap KeyboardController::modifierModes() const
{
return m_options.holdKeys ? m_hardware.modifierModes() : m_modifiers.modes();
}
bool KeyboardController::holdKey(const KeyDef &key, bool pressed)
{
if (!m_options.holdKeys || !HardwareKeys::handles(key))
return false;
pressed ? m_hardware.press(key) : m_hardware.release(key);
m_model.setShiftLevel(shiftLevel());
Q_EMIT shiftChanged();
Q_EMIT modifiersChanged();
return true;
}
void KeyboardController::keyDown(const KeyDef &key, qint64 timeMs)
{
if (holdKey(key, true))
return;
switch (key.type) {
case KeyType::Shift:
changeShift([&] { m_shift.press(timeMs); });
@@ -72,6 +96,8 @@ void KeyboardController::keyDown(const KeyDef &key, qint64 timeMs)
void KeyboardController::keyUp(const KeyDef &key, KeyOutput output, const QString &alternate, qint64)
{
if (holdKey(key, false))
return;
switch (key.type) {
case KeyType::Shift:
changeShift([&] { m_shift.release(); });
@@ -112,6 +138,8 @@ void KeyboardController::keyUp(const KeyDef &key, KeyOutput output, const QStrin
void KeyboardController::keyCancelled(const KeyDef &key)
{
if (holdKey(key, false))
return;
if (key.type == KeyType::Shift)
changeShift([&] { m_shift.release(); });
else if (key.type == KeyType::Modifier) {
@@ -125,6 +153,7 @@ void KeyboardController::keyCancelled(const KeyDef &key)
void KeyboardController::slideReleased(const KeyDef &target, qint64 timeMs)
{
holdKey(target, true); // only in hold-keys mode: the slide never pressed it
keyUp(target, KeyOutput::Primary, {}, timeMs);
m_model.setLayer(m_layerBeforeSwitch);
}
@@ -158,7 +187,12 @@ void KeyboardController::setContext(const TextContext &context)
void KeyboardController::setOptions(const TypingOptions &options)
{
if (m_options.holdKeys && !options.holdKeys)
m_hardware.releaseAll();
m_options = options;
m_model.setShiftLevel(shiftLevel());
Q_EMIT shiftChanged();
Q_EMIT modifiersChanged();
autoCapitalize();
}
@@ -254,6 +288,8 @@ void KeyboardController::autoCapitalize()
ShiftLevel KeyboardController::shiftLevel() const
{
if (m_options.holdKeys)
return m_hardware.shiftLevel();
if (m_shift.appliesToShortcuts())
return ShiftLevel::Shifted;
return m_shift.isActive() ? ShiftLevel::Capitals : ShiftLevel::None;