- 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>
102 lines
3.7 KiB
QML
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
|
|
}
|
|
}
|