It is the system settings app for this desktop, so it is named the way one is. The window is "Settings", the titlebar is "Settings", the wordmark above the search field is gone, the sidebar's last row is "About", and the status line reads "Desktop is healthy". Prose followed the same rule. Forty-odd strings explained what "Panama" does -- "Panama never animates while idle", "Restore Panama defaults", "Panama looks in ~/Pictures/Wallpapers" -- which is how a product describes itself, not how a settings panel describes a setting. They now say what happens. No user-visible "Panama" remains anywhere in the app. Two consequences worth naming: The SUPER+I shortcut's description is user-visible, because the Shortcuts page is generated from it, so that is renamed too. Rebindings are keyed by shipped chord rather than description, so no existing override is orphaned by this. Three contracts matched the window by title and one matched that shortcut by description; all four are updated. There are no Hyprland window rules keyed on the title, so nothing about the window's placement changes. The desktop entry is now Name=Settings, but the FILE keeps its panama-settings name, as does the icon: the dock pins applications by desktop id, and renaming the file would silently unpin it. dock-pins-contract covers exactly that. The dated design docs under docs/superpowers keep the old name. They are a record of what was decided when, and editing them to agree with the present would make them lie about the past. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
180 lines
7.0 KiB
QML
180 lines
7.0 KiB
QML
// Input & Shortcuts.
|
|
//
|
|
// The shortcut list is generated from `hyprctl binds -j` rather than typed out
|
|
// here. The previous version was a hand-maintained array of nineteen entries
|
|
// against a real keymap of a hundred and thirteen: it could not show the rest,
|
|
// and it went stale the moment a bind changed. Every bind now carries its own
|
|
// description in hypr/keybinds.lua, and this page just groups and renders them.
|
|
//
|
|
// The hardware settings above the list are real controls. Keyboard layout,
|
|
// repeat behaviour, and pointer response are Hyprland's, so Panama owns them;
|
|
// device-specific configuration stays with GNOME.
|
|
|
|
import QtQuick
|
|
import qs.config
|
|
import qs.services
|
|
|
|
SettingsPage {
|
|
id: root
|
|
|
|
// The chord of the bind currently being re-recorded, in the Lua form; empty
|
|
// when nothing is being captured. Held here rather than per row so that
|
|
// starting a new capture cancels any other.
|
|
property string capturingChord: ""
|
|
|
|
title: "Input & Shortcuts"
|
|
lede: "The Forge mental model, carried forward into native tiling."
|
|
|
|
SettingsCard {
|
|
title: "Keyboard"
|
|
|
|
// These were read-only text, on the grounds that a layout change needed
|
|
// a compositor reload. It does not: setting input:kb_variant through
|
|
// hl.config re-keymaps attached keyboards immediately -- verified by
|
|
// watching active_keymap on a real keyboard change and change back. So
|
|
// they are real controls.
|
|
TextEntryRow { setting: "keyboardLayout"; placeholder: "us" }
|
|
TextEntryRow { setting: "keyboardVariant"; placeholder: "none" }
|
|
TextEntryRow { setting: "keyboardOptions"; placeholder: "compose:ralt" }
|
|
SliderRow { setting: "keyRepeatDelay" }
|
|
SliderRow { setting: "keyRepeatRate" }
|
|
ToggleRow { setting: "numlockByDefault"; divider: false }
|
|
}
|
|
|
|
SettingsCard {
|
|
title: "Hardware input"
|
|
|
|
ActionRow {
|
|
label: "Mouse, touchpad, and keyboard devices"
|
|
detail: "Device-specific settings stay with Fedora's hardware-backed panels"
|
|
action: "Open keyboard"
|
|
divider: false
|
|
onTriggered: SystemSettings.openGnomePanel("keyboard")
|
|
}
|
|
}
|
|
|
|
// One card per group, built from what the compositor actually has bound.
|
|
//
|
|
// Both Repeaters address their model through an explicit id. Inside a
|
|
// SettingsCard the surrounding `parent` is the card's internal Column, not
|
|
// the card, so `parent.modelData` is undefined there and the rows silently
|
|
// never appear -- the cards render with their heading and nothing under it.
|
|
Repeater {
|
|
model: Keybinds.grouped()
|
|
|
|
SettingsCard {
|
|
id: groupCard
|
|
|
|
required property var modelData
|
|
|
|
title: groupCard.modelData.name
|
|
subtitle: groupCard.modelData.binds.length === 1
|
|
? "1 shortcut"
|
|
: `${groupCard.modelData.binds.length} shortcuts`
|
|
|
|
Repeater {
|
|
id: bindRows
|
|
|
|
model: groupCard.modelData.binds
|
|
|
|
SettingRow {
|
|
id: bindRow
|
|
|
|
required property var modelData
|
|
required property int index
|
|
|
|
readonly property bool capturing: root.capturingChord === bindRow.modelData.luaChord
|
|
readonly property bool overridden: Keybinds.isOverridden(bindRow.modelData.luaChord)
|
|
|
|
label: bindRow.modelData.description
|
|
detail: bindRow.overridden
|
|
? "Moved from " + Keybinds.shippedChordFor(bindRow.modelData.luaChord)
|
|
: ""
|
|
controlWidth: 300
|
|
divider: bindRow.index < bindRows.count - 1
|
|
|
|
Item {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 300
|
|
height: 30
|
|
|
|
ShortcutCapture {
|
|
anchors.right: parent.right
|
|
width: 230
|
|
height: 30
|
|
visible: bindRow.capturing
|
|
focus: bindRow.capturing
|
|
onCaptured: chord => {
|
|
Keybinds.rebind(bindRow.modelData.luaChord, chord);
|
|
root.capturingChord = "";
|
|
}
|
|
onCancelled: root.capturingChord = ""
|
|
}
|
|
|
|
Row {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: !bindRow.capturing
|
|
spacing: 8
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: bindRow.modelData.chord
|
|
color: bindRow.overridden ? Theme.accent : Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
}
|
|
|
|
SettingsButton {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Change"
|
|
enabled: !Keybinds.reloading && !bindRow.modelData.mouse
|
|
onClicked: root.capturingChord = bindRow.modelData.luaChord
|
|
}
|
|
|
|
SettingsButton {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: bindRow.overridden
|
|
text: "Reset"
|
|
enabled: !Keybinds.reloading
|
|
onClicked: Keybinds.resetBind(bindRow.modelData.luaChord)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
visible: Object.keys(Keybinds.overrides).length > 0
|
|
title: "Changed shortcuts"
|
|
subtitle: "Rebinding stores only the new chord; what a shortcut does always comes from the desktop's configuration."
|
|
|
|
ActionRow {
|
|
label: "Restore every shipped shortcut"
|
|
detail: Object.keys(Keybinds.overrides).length
|
|
+ (Object.keys(Keybinds.overrides).length === 1 ? " shortcut moved" : " shortcuts moved")
|
|
action: "Restore all"
|
|
divider: false
|
|
enabled: !Keybinds.reloading
|
|
onTriggered: Keybinds.resetAll()
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
visible: Keybinds.lastError !== ""
|
|
title: "Shortcuts unavailable"
|
|
subtitle: Keybinds.lastError
|
|
|
|
ActionRow {
|
|
label: "Read the keymap again"
|
|
detail: "Shortcuts are read from the running compositor"
|
|
action: "Retry"
|
|
divider: false
|
|
onTriggered: Keybinds.refresh()
|
|
}
|
|
}
|
|
}
|