Full keyboard mode, Ctrl/Alt/Super, dead keys, shift levels

- Full (iPad Pro) mode: every typing key from the keymap, number row,
  tab, caps lock, ctrl/super/alt; shifted symbol shown above and flickable
- Dead keys compose with the locale's XKB compose table
- Ctrl/Alt/Super stick like shift and turn keys into shortcuts; a tapped
  or held shift joins shortcuts and non-text keys, auto-shift and caps
  lock don't
- Auto-capitalization and caps lock only capitalize letters; the user's
  shift picks the keymap's shifted level
- Mode switch in settings keeps key size by resizing the window

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 21:29:24 +02:00
co-authored by Claude Opus 5.5
parent 88338fde22
commit ba2f17d665
36 changed files with 948 additions and 202 deletions
+18 -7
View File
@@ -12,12 +12,15 @@ QString keyTypeName(KeyType type)
case KeyType::Backspace: return QStringLiteral("backspace");
case KeyType::Enter: return QStringLiteral("enter");
case KeyType::Shift: return QStringLiteral("shift");
case KeyType::CapsLock: return QStringLiteral("capslock");
case KeyType::Modifier: return QStringLiteral("modifier");
case KeyType::LayerSwitch: return QStringLiteral("layer");
case KeyType::Globe: return QStringLiteral("globe");
case KeyType::Dismiss: return QStringLiteral("dismiss");
case KeyType::Spacer: return QStringLiteral("spacer");
case KeyType::Keysym: return QStringLiteral("keysym");
case KeyType::Letters: break; // expanded before it reaches the model
case KeyType::Letters:
case KeyType::Keys: break; // expanded before it reaches the model
}
return {};
}
@@ -44,8 +47,10 @@ QVariant LayoutModel::data(const QModelIndex &index, int role) const
const KeyDef &key = m_keys.at(index.row());
const QRectF rect = m_rects.value(index.row());
switch (role) {
case LabelRole: return key.text(m_uppercase);
case FlickLabelRole: return key.flick;
case LabelRole: return key.text(m_shiftLevel);
// A shifted key already shows its shifted symbol (! on 1): don't repeat it.
case FlickLabelRole: return m_shiftLevel == ShiftLevel::Shifted && key.flick == key.shifted ? QString() : key.flick;
case ModifierRole: return key.modifier;
case KeyTypeRole: return keyTypeName(key.type);
case KeyXRole: return rect.x();
case KeyYRole: return rect.y();
@@ -69,6 +74,7 @@ QHash<int, QByteArray> LayoutModel::roleNames() const
{KeyHeightRole, "keyHeight"},
{PressedRole, "pressed"},
{FlickProgressRole, "flickProgress"},
{ModifierRole, "modifier"},
};
}
@@ -111,6 +117,11 @@ qreal LayoutModel::totalUnits() const
return ::totalUnits(m_blocks);
}
qsizetype LayoutModel::totalRows() const
{
return ::totalRows(m_blocks);
}
void LayoutModel::setSize(QSizeF size)
{
if (size == m_size)
@@ -121,13 +132,13 @@ void LayoutModel::setSize(QSizeF size)
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {KeyXRole, KeyYRole, KeyWidthRole, KeyHeightRole});
}
void LayoutModel::setUppercase(bool uppercase)
void LayoutModel::setShiftLevel(ShiftLevel level)
{
if (uppercase == m_uppercase)
if (level == m_shiftLevel)
return;
m_uppercase = uppercase;
m_shiftLevel = level;
if (!m_keys.isEmpty())
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {LabelRole});
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {LabelRole, FlickLabelRole});
}
void LayoutModel::setPressed(int row, bool pressed)