feat(hotkeys): lay the shortcuts cheatsheet out in three columns

The read-only shortcuts modal listed every category in one tall column that
scrolled. Split the groups round-robin into three column models (Rust) and
render them side by side; the panel widened to fit. The editable customize
editor still uses the full single-column list.

cc
This commit is contained in:
vicrodh committed 2026-06-30 14:07:50 -06:00
1 parent e44aa07acf
commit 1a9c132760
3 files changed
+101 -66

No files matched your search

@@ -10,9 +10,71 @@
import { Theme } from "../foundation/semantic-colors.slint";
import { Typography } from "../foundation/typography.slint";
import { Radius } from "../foundation/radius.slint";
import { KeyboardShortcutsState, KeybindingsState } from "../state.slint";
import { KeyboardShortcutsState, KeybindingsState, KeybindingCategoryGroup } from "../state.slint";
import { QbzIcon } from "../primitives/QbzIcon.slint";
// One category block (uppercase header + its binding rows). Extracted so the
// body can lay the groups out across THREE columns instead of one tall
// scrolling column.
component KbGroupBlock inherits VerticalLayout {
in property <KeybindingCategoryGroup> group;
spacing: 12px;
VerticalLayout {
spacing: 8px;
Text {
text: group.label;
color: Theme.text-muted;
font-size: Typography.legal;
font-weight: Typography.semibold;
}
Rectangle {
height: 1px;
background: Theme.border-subtle;
}
}
for entry in group.rows: Rectangle {
height: entry-row.preferred-height + 16px;
border-radius: Radius.sm;
background: Theme.surface-elevated;
entry-row := HorizontalLayout {
padding-left: 12px;
padding-right: 12px;
padding-top: 8px;
padding-bottom: 8px;
spacing: 12px;
Text {
text: entry.label;
color: Theme.text-primary;
font-size: Typography.body;
horizontal-stretch: 1;
vertical-alignment: center;
}
// Keycap — a subtle key-like chip (Radius.sm, NOT a pill, ADR-008).
Rectangle {
width: keycap-label.preferred-width + 16px;
height: keycap-label.preferred-height + 8px;
border-radius: Radius.sm;
background: Theme.surface-card;
border-width: 1px;
border-color: Theme.border-muted;
keycap-label := Text {
text: entry.shortcut;
color: Theme.text-secondary;
font-size: Typography.legal;
font-family: "monospace";
x: Math.round((parent.width - self.width) / 2 / 1px) * 1px;
y: Math.round((parent.height - self.height) / 2 / 1px) * 1px;
}
}
}
}
}
export component KeyboardShortcutsModal inherits Rectangle {
visible: KeyboardShortcutsState.open;
@@ -37,7 +99,7 @@ export component KeyboardShortcutsModal inherits Rectangle {
+ footer-row.preferred-height + 2px;
property <length> content-height: chrome-height + body.preferred-height + 40px;
width: Math.min(root.width - 80px, 660px);
width: Math.min(root.width - 80px, 980px);
height: Math.min(self.content-height, root.height - 80px);
x: Math.round((parent.width - self.width) / 2 / 1px) * 1px;
y: Math.round((parent.height - self.height) / 2 / 1px) * 1px;
@@ -131,70 +193,26 @@ export component KeyboardShortcutsModal inherits Rectangle {
viewport-width: self.width;
viewport-height: body.preferred-height;
body := VerticalLayout {
// Groups laid out across THREE columns (round-robin by
// index) so the cheatsheet fits without a tall scroll.
body := HorizontalLayout {
padding: 20px;
spacing: 24px;
for group in KeybindingsState.groups: VerticalLayout {
spacing: 12px;
// Uppercase category header + underline.
VerticalLayout {
spacing: 8px;
Text {
// Already localized in Rust; render as-is.
text: group.label;
color: Theme.text-muted;
font-size: Typography.legal;
font-weight: Typography.semibold;
}
Rectangle {
height: 1px;
background: Theme.border-subtle;
}
}
// One row per binding: label LEFT, keycap RIGHT.
for entry in group.rows: Rectangle {
height: entry-row.preferred-height + 16px;
border-radius: Radius.sm;
background: Theme.surface-elevated;
entry-row := HorizontalLayout {
padding-left: 12px;
padding-right: 12px;
padding-top: 8px;
padding-bottom: 8px;
spacing: 12px;
Text {
text: entry.label;
color: Theme.text-primary;
font-size: Typography.body;
horizontal-stretch: 1;
vertical-alignment: center;
}
// Keycap — a subtle key-like chip
// (Radius.sm, NOT a pill per ADR-008).
Rectangle {
width: keycap-label.preferred-width + 16px;
height: keycap-label.preferred-height + 8px;
border-radius: Radius.sm;
background: Theme.surface-card;
border-width: 1px;
border-color: Theme.border-muted;
keycap-label := Text {
text: entry.shortcut;
color: Theme.text-secondary;
font-size: Typography.legal;
font-family: "monospace";
x: Math.round((parent.width - self.width) / 2 / 1px) * 1px;
y: Math.round((parent.height - self.height) / 2 / 1px) * 1px;
}
}
}
}
VerticalLayout {
alignment: start;
spacing: 24px;
for group in KeybindingsState.groups-col1: KbGroupBlock { group: group; }
}
VerticalLayout {
alignment: start;
spacing: 24px;
for group in KeybindingsState.groups-col2: KbGroupBlock { group: group; }
}
VerticalLayout {
alignment: start;
spacing: 24px;
for group in KeybindingsState.groups-col3: KbGroupBlock { group: group; }
}
}
}
+5
View File
@@ -5254,6 +5254,11 @@ export struct KeybindingCategoryGroup {
export global KeybindingsState {
// Ordered category groups, rebuilt in Rust whenever a binding changes.
in property <[KeybindingCategoryGroup]> groups: [];
// Same groups split round-robin into three columns, for the read-only
// cheatsheet's 3-column layout (the editor still uses the full `groups`).
in property <[KeybindingCategoryGroup]> groups-col1: [];
in property <[KeybindingCategoryGroup]> groups-col2: [];
in property <[KeybindingCategoryGroup]> groups-col3: [];
// Count of actions whose binding differs from default ("N modified").
in property <int> modified-count: 0;