Optional PC key blocks: function row, navigation, numeric keypad

- Template sections placed above or to the right of the typing keys;
  the user picks any mix in settings, typing keys included
- keysym keys send XKB keysyms on press, optionally repeating
- Geometry lays blocks side by side with one key unit; stretch keys are
  resolved at layout time so composed blocks line up
- Toggling a block resizes the window so keys keep their size

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 21:18:10 +02:00
co-authored by Claude Opus 5.5
parent e532f5c072
commit 88338fde22
22 changed files with 521 additions and 101 deletions
+28
View File
@@ -14,14 +14,18 @@ static_assert(int(KeyboardController::ShiftMode::Locked) == int(ShiftState::Mode
namespace {
// Enough history for the text rules; fields may report megabytes.
constexpr qsizetype MaxTextBefore = 256;
// Like a hardware key: no word mode, a faster rate than Backspace.
constexpr KeyRepeater::Timing KeysymRepeat{.intervalMs = 50, .wordModeAfterMs = std::numeric_limits<int>::max()};
}
KeyboardController::KeyboardController(InputEngine &engine, LayoutModel &model, QObject *parent)
: QObject(parent)
, m_engine(engine)
, m_model(model)
, m_keysymRepeater(KeysymRepeat)
{
connect(&m_backspaceRepeater, &KeyRepeater::repeat, this, &KeyboardController::deleteBackward);
connect(&m_keysymRepeater, &KeyRepeater::repeat, this, &KeyboardController::sendRepeatedKeysym);
}
KeyboardController::ShiftMode KeyboardController::shiftMode() const
@@ -43,6 +47,13 @@ void KeyboardController::keyDown(const KeyDef &key, qint64 timeMs)
m_layerBeforeSwitch = m_model.layerName();
m_model.setLayer(key.target);
break;
case KeyType::Keysym:
m_engine.sendKeysym(key.keysym);
if (key.repeat) {
m_repeatingKeysym = key.keysym;
m_keysymRepeater.start();
}
break;
default:
break; // everything else acts on release
}
@@ -57,6 +68,9 @@ void KeyboardController::keyUp(const KeyDef &key, KeyOutput output, const QStrin
case KeyType::Backspace:
m_backspaceRepeater.stop();
break;
case KeyType::Keysym:
stopRepeating(key);
break;
case KeyType::Character:
if (output == KeyOutput::Alternate && !alternate.isEmpty())
typeText(alternate);
@@ -90,6 +104,8 @@ void KeyboardController::keyCancelled(const KeyDef &key)
changeShift([&] { m_shift.release(); });
else if (key.type == KeyType::Backspace)
m_backspaceRepeater.stop();
else if (key.type == KeyType::Keysym)
stopRepeating(key);
}
void KeyboardController::slideReleased(const KeyDef &target, qint64 timeMs)
@@ -156,6 +172,18 @@ void KeyboardController::deleteBackward(bool wholeWord)
autoCapitalize();
}
// Only the key that started the repeat stops it; another finger's key doesn't.
void KeyboardController::stopRepeating(const KeyDef &key)
{
if (key.keysym == m_repeatingKeysym)
m_keysymRepeater.stop();
}
void KeyboardController::sendRepeatedKeysym()
{
m_engine.sendKeysym(m_repeatingKeysym);
}
void KeyboardController::autoCapitalize()
{
const bool capitalize = m_options.autoCapitalize && m_context.allowsAutoCapitalization()