Files
sloposk/qml/Key.qml
T
ericek111andClaude Opus 5.5 ba2f17d665 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>
2026-10-14 21:29:24 +02:00

102 lines
3.7 KiB
QML

import QtQuick
import Sloposk
// One key, drawn from the LayoutModel roles. Purely visual.
Item {
id: key
required property string label
required property string flickLabel
required property string keyType
required property real keyX
required property real keyY
required property real keyWidth
required property real keyHeight
required property bool pressed
required property real flickProgress
required property string modifier
property int shiftMode: KeyboardController.Off
property var modifierModes: ({})
property bool blank: false // labels hidden, e.g. in trackpad mode
readonly property bool isCharacter: keyType === "char" || keyType === "space"
// Off / Once / Locked for shift, caps lock and ctrl/alt/super.
readonly property int latch: {
if (keyType === "shift")
return shiftMode
if (keyType === "capslock")
return shiftMode === KeyboardController.Locked ? KeyboardController.Locked : KeyboardController.Off
if (keyType === "modifier")
return modifierModes[modifier] ?? KeyboardController.Off
return KeyboardController.Off
}
// Shift shows locking with its glyph; the others turn accent-coloured.
readonly property bool locked: latch === KeyboardController.Locked && keyType !== "shift"
// Character keys darken when pressed; function keys lighten (as on iPad).
readonly property bool lit: (isCharacter !== pressed) || latch !== KeyboardController.Off
readonly property var defaultGlyphs: ({
backspace: "⌫",
globe: "🌐",
dismiss: "⌨",
enter: "return",
})
readonly property string displayLabel: {
if (keyType === "shift")
return ["⇧", "⬆", "⇪"][shiftMode]
return label !== "" ? label : (defaultGlyphs[keyType] ?? "")
}
// Count code points, not UTF-16 units, so emoji glyphs aren't treated as words.
readonly property bool isWord: [...displayLabel].length > 1 && !isCharacter
x: keyX
y: keyY
width: keyWidth
height: keyHeight
Rectangle {
anchors { fill: face; topMargin: 1; bottomMargin: -1 }
radius: Theme.keyRadius
color: Theme.keyShadow
}
Rectangle {
id: face
anchors.fill: parent
radius: Theme.keyRadius
color: key.locked ? Theme.accent : key.lit ? Theme.characterKey : Theme.functionKey
}
// Flicking slides the secondary symbol down into the main position.
Text {
id: flick
// Drawn at its final size and scaled down at rest. Animating
// font.pixelSize instead steps in whole pixels and re-lays out the
// text each step, so the symbol jitters.
readonly property real restScale: 0.6
readonly property real restCenterY: key.height * 0.1 + height * restScale / 2
readonly property real centerY: restCenterY + (key.height / 2 - restCenterY) * key.flickProgress
visible: text !== "" && !key.blank
text: key.flickLabel
anchors.horizontalCenter: parent.horizontalCenter
y: centerY - height / 2
scale: restScale + (1 - restScale) * key.flickProgress
font.pixelSize: key.height * 0.4
color: key.flickProgress >= 1 ? Theme.text : Theme.secondaryText
}
Text {
text: key.displayLabel
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
verticalCenterOffset: flick.visible ? key.height * 0.14 : 0
}
font.pixelSize: key.height * (key.isWord ? 0.24 : flick.visible ? 0.34 : 0.4)
color: key.locked ? Theme.accentText : Theme.text
visible: !key.blank
opacity: 1 - key.flickProgress
}
}