Fix double-space period, shift double-tap; settings; letters from system keymap

- Double-space replaced the space via a Backspace keysym, which clients
  apply after the commit: use delete_surrounding_text with the commit
- Tests use a shared FakeEngine that applies ops to a simulated field
- Double-tap shift locks at a sentence start; typing between taps doesn't
- Organization name set: config lives in ~/.config/sloposk/
- Settings (auto-capitalization, double-space period) with a ⚙ panel
- Letter rows come from the compositor's XKB keymap; other letters of the
  layout become long-press alternates; globe switches the system layout
  over org.kde.keyboard
- One JSON template for non-letter layers; stretch keys even out rows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 20:28:39 +02:00
co-authored by Claude Opus 5.5
parent c6c977eb4c
commit 5277ab16ff
49 changed files with 1162 additions and 359 deletions
+15
View File
@@ -6,7 +6,9 @@ Window {
required property LayoutModel layoutModel
required property KeyboardController controller
required property Settings settings
property var placement: null // FloatingWindow, set from C++
property bool settingsOpen: false
// Transparent room above the keyboard for long-press popups on the top row.
readonly property real popupMargin: 90
@@ -35,16 +37,29 @@ Window {
id: keyboard
anchors { left: parent.left; right: parent.right; top: parent.top; bottom: handle.top }
anchors.margins: 2
visible: !root.settingsOpen
model: root.layoutModel
controller: root.controller
}
SettingsPanel {
anchors.fill: keyboard
visible: root.settingsOpen
settings: root.settings
}
DragHandle {
id: handle
anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
onMoved: (dx, dy) => root.placement?.moveBy(dx, dy)
}
SettingsButton {
anchors { left: parent.left; leftMargin: 8; bottom: parent.bottom }
checked: root.settingsOpen
onClicked: root.settingsOpen = !root.settingsOpen
}
ResizeHandle {
anchors { right: parent.right; bottom: parent.bottom }
onResized: (dw, dh) => root.placement?.resizeBy(dw, dh)
+24
View File
@@ -0,0 +1,24 @@
import QtQuick
// Gear in the grab bar; opens and closes the settings panel.
Item {
id: button
property bool checked: false
signal clicked()
implicitWidth: 36
implicitHeight: 22
Text {
anchors.centerIn: parent
text: button.checked ? "⌨" : "⚙"
font.pixelSize: 15
color: tap.pressed ? Theme.text : Theme.handle
}
TapHandler {
id: tap
onTapped: button.clicked()
}
}
+33
View File
@@ -0,0 +1,33 @@
import QtQuick
import Sloposk
// Replaces the keys while open. Binds straight to Settings, which persists.
Rectangle {
id: panel
required property Settings settings
color: Theme.background
radius: Theme.windowRadius
Column {
anchors { fill: parent; margins: 16 }
spacing: 4
ToggleRow {
width: parent.width
text: qsTr("Auto-Capitalization")
detail: qsTr("Shift at the start of a sentence")
checked: panel.settings.autoCapitalize
onToggled: checked => panel.settings.autoCapitalize = checked
}
ToggleRow {
width: parent.width
text: qsTr("“.” Shortcut")
detail: qsTr("Double-tap space for a period and a space")
checked: panel.settings.doubleSpacePeriod
onToggled: checked => panel.settings.doubleSpacePeriod = checked
}
}
}
+1
View File
@@ -15,6 +15,7 @@ QtObject {
readonly property color handle: dark ? "#8e8e93" : "#7c7c80"
readonly property color accent: dark ? "#0a84ff" : "#007aff"
readonly property color accentText: "#ffffff"
readonly property color toggleOn: dark ? "#30d158" : "#34c759"
readonly property real windowRadius: 14
readonly property real keyRadius: 6
+57
View File
@@ -0,0 +1,57 @@
import QtQuick
// A labelled on/off switch in the iOS style.
Item {
id: row
required property string text
property string detail: ""
property bool checked: false
signal toggled(bool checked)
implicitHeight: 52
Column {
anchors { left: parent.left; right: track.left; rightMargin: 12; verticalCenter: parent.verticalCenter }
spacing: 2
Text {
width: parent.width
text: row.text
color: Theme.text
font.pixelSize: 16
elide: Text.ElideRight
}
Text {
width: parent.width
visible: text !== ""
text: row.detail
color: Theme.secondaryText
font.pixelSize: 12
elide: Text.ElideRight
}
}
Rectangle {
id: track
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
width: 50
height: 30
radius: height / 2
color: row.checked ? Theme.toggleOn : Theme.functionKey
Rectangle {
x: row.checked ? parent.width - width - 2 : 2
anchors.verticalCenter: parent.verticalCenter
width: parent.height - 4
height: width
radius: width / 2
color: "#ffffff"
Behavior on x { NumberAnimation { duration: 120 } }
}
}
TapHandler {
onTapped: row.toggled(!row.checked)
}
}