Make every settings row reachable, and every accessibility switch honest

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 21:03:36 -04:00
parent e1ff25fc66
commit 9ffaf45a4d
33 changed files with 2384 additions and 76 deletions
@@ -18,12 +18,61 @@ SettingRow {
controlWidth: Math.max(150, root.options.length * 92)
readonly property var currentOption:
root.options.find(option => option.value === root.value) ?? null
// One segment along, never wrapping -- the same neighbour a click would
// have picked, asked for in the same way.
function stepChoice(delta: int): void {
if (!root.enabled || root.options.length === 0)
return;
const at = root.options.findIndex(option => option.value === root.value);
const from = at < 0 ? 0 : at;
const next = Math.max(0, Math.min(root.options.length - 1, from + delta));
if (next === at)
return;
root.selected(root.options[next].value);
}
// Sibling of the strip rather than a child of it: a Rectangle inside a Row
// would be laid out as one more segment.
Rectangle {
anchors.fill: strip
anchors.margins: -3
radius: 12
z: -1
color: "transparent"
visible: strip.activeFocus
border.width: 2
border.color: Theme.accentSecondary
}
Row {
id: strip
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 6
opacity: root.enabled ? 1 : 0.45
activeFocusOnTab: root.enabled
Accessible.role: Accessible.ComboBox
Accessible.name: root.label
Accessible.description: {
const chosen = root.currentOption
? String(root.currentOption.label ?? "")
: "nothing selected";
return root.detail === "" ? chosen : `${root.detail} ${chosen}`;
}
Accessible.focusable: root.enabled
Accessible.focused: strip.activeFocus
Keys.onLeftPressed: root.stepChoice(-1)
Keys.onUpPressed: root.stepChoice(-1)
Keys.onRightPressed: root.stepChoice(1)
Keys.onDownPressed: root.stepChoice(1)
Repeater {
model: root.options
@@ -45,6 +94,12 @@ SettingRow {
? Theme.alpha(Theme.accent, 0.5)
: Theme.alpha(Theme.fg, 0.08)
// Named individually so the reader says which option it is on.
// Not a Tab stop: the strip is one stop and the arrows walk it.
Accessible.role: Accessible.RadioButton
Accessible.name: String(segment.modelData.label ?? "")
Accessible.checked: segment.current
Text {
id: segmentLabel
anchors.centerIn: parent