Make every settings row reachable, and every accessibility switch honest
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -1,5 +1,15 @@
|
||||
// Accessibility.
|
||||
//
|
||||
// Organised by what a person came here unable to do -- see it, tolerate the
|
||||
// motion, hear it, reach it from the keyboard, have it read aloud -- rather
|
||||
// than by which subsystem happens to implement each control. The old page was
|
||||
// grouped by mechanism (Pointer / Text / Motion / Magnifier / Contrast), which
|
||||
// is the shape of the code and not the shape of the question.
|
||||
//
|
||||
// Everything on this page acts on this session. Two things deliberately do not
|
||||
// ship as switches -- mono audio, and sticky/slow/bounce keys -- and both say
|
||||
// why in place instead of being quietly absent or, worse, present and dead.
|
||||
//
|
||||
// Pointer size and text scale have to agree across three consumers that share
|
||||
// no configuration system -- the compositor, GTK applications, and the shell.
|
||||
// Panama's store is the source of truth and services/Accessibility.qml pushes
|
||||
@@ -13,75 +23,205 @@ SettingsPage {
|
||||
id: root
|
||||
|
||||
title: "Accessibility"
|
||||
lede: "Make the desktop easier to see and easier to hit."
|
||||
lede: "Every switch on this page does something on this desktop — and the ones that cannot yet say why."
|
||||
|
||||
SettingsCard {
|
||||
title: "Pointer"
|
||||
subtitle: "Applied to the compositor and to applications at the same time."
|
||||
// The zoom chords come from the compositor's live keymap, matched on the
|
||||
// descriptions hypr/keybinds.lua gives them, so rebinding a zoom key
|
||||
// changes what this page says instead of quietly making it wrong. The
|
||||
// literals are the shipped chords, standing in only until the keymap has
|
||||
// loaded -- not a second source of truth.
|
||||
function chordFor(needle: string, fallback: string): string {
|
||||
for (const bind of Keybinds.binds) {
|
||||
if (String(bind.description).toLowerCase().indexOf(needle) >= 0)
|
||||
return String(bind.chord);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
SliderRow { setting: "cursorSize" }
|
||||
SliderRow { setting: "cursorInactiveTimeout"; zeroLabel: "Never"; divider: false }
|
||||
readonly property string zoomInChord: root.chordFor("zoom in", "Super + Alt + =")
|
||||
readonly property string zoomOutChord: root.chordFor("zoom out", "Super + Alt + -")
|
||||
readonly property string zoomResetChord: root.chordFor("reset zoom", "Super + Alt + 0")
|
||||
|
||||
// Probed when the page opens rather than polled all session: nothing else
|
||||
// on this desktop needs to know whether Orca is running.
|
||||
Component.onCompleted: Accessibility.refreshScreenReader()
|
||||
|
||||
readonly property string screenReaderDetail: {
|
||||
if (Accessibility.orcaRunning)
|
||||
return "Running — reading the focused application";
|
||||
if (Accessibility.accessibilityBusRunning)
|
||||
return "Not running · the accessibility bus is up, so applications are ready to be read";
|
||||
return "Not running · the accessibility bus is not up, so Orca would start and read nothing";
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Text"
|
||||
subtitle: "Scales text in applications. The shell's own panels are drawn at their design size, so they are unaffected."
|
||||
title: "Vision"
|
||||
subtitle: "Magnification is the compositor's own, so it follows the pointer across every window and every screen."
|
||||
|
||||
SliderRow { setting: "magnifierFactor" }
|
||||
|
||||
// One row per chord rather than three chords crammed into one row's
|
||||
// trailing slot: three keycap chords side by side are wider than the
|
||||
// control column, and squeezing them there costs the label its line.
|
||||
SettingRow {
|
||||
label: "Zoom in"
|
||||
detail: "Works from anywhere — the OSD shows the magnification you land on"
|
||||
controlWidth: 210
|
||||
|
||||
KeycapChord {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
chord: root.zoomInChord
|
||||
}
|
||||
}
|
||||
|
||||
SettingRow {
|
||||
label: "Zoom out"
|
||||
controlWidth: 210
|
||||
|
||||
KeycapChord {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
chord: root.zoomOutChord
|
||||
}
|
||||
}
|
||||
|
||||
SettingRow {
|
||||
label: "Back to 1.00 ×"
|
||||
detail: "Turns the magnifier off without coming back to Settings for it"
|
||||
controlWidth: 210
|
||||
|
||||
KeycapChord {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
chord: root.zoomResetChord
|
||||
}
|
||||
}
|
||||
|
||||
ToggleRow { setting: "magnifierRigid" }
|
||||
SliderRow { setting: "textScale" }
|
||||
// Reaches GTK4 applications through the desktop portal, which
|
||||
// republishes it as org.freedesktop.appearance contrast. No
|
||||
// high-contrast theme is involved, and none is installed here -- older
|
||||
// GTK3 applications will not change.
|
||||
SliderRow { setting: "cursorSize" }
|
||||
ToggleRow { setting: "highContrast"; divider: false }
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Motion"
|
||||
subtitle: "Nothing animates while idle. This affects motion you asked for — windows opening, workspaces sliding, panels appearing."
|
||||
subtitle: "Nothing animates while idle. This is the motion you asked for — windows opening, workspaces sliding, panels appearing."
|
||||
|
||||
ToggleRow { setting: "animationsEnabled"; divider: false }
|
||||
}
|
||||
// The detail says more than the schema's does because on this page the
|
||||
// claim is the point: the shell's own durations now collapse to zero
|
||||
// when this is off, so the bar, the dock and the panels genuinely stop
|
||||
// moving. Until that landed, this switch reached the compositor and
|
||||
// left the shell animating over the top of it.
|
||||
ToggleRow {
|
||||
setting: "animationsEnabled"
|
||||
detail: "Window, workspace and panel motion — including the shell's own bar, dock and panels, which now stop with everything else"
|
||||
}
|
||||
|
||||
// Zoom, done by the compositor rather than handed to GNOME. Hyprland has a
|
||||
// real magnifier (cursor:zoom_factor) that follows the pointer, so there is
|
||||
// no reason to send someone to another application for it.
|
||||
SettingsCard {
|
||||
title: "Magnifier"
|
||||
subtitle: "Magnifies the screen around the pointer. Set the magnification to 1× to turn it off."
|
||||
|
||||
SliderRow { setting: "magnifierFactor"; zeroLabel: "Off" }
|
||||
ToggleRow { setting: "magnifierRigid"; divider: false }
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Contrast"
|
||||
subtitle: "Unfocused windows can be faded or darkened to make the focused one obvious, or left alone if that is harder to read."
|
||||
|
||||
SliderRow { setting: "inactiveOpacity" }
|
||||
ToggleRow { setting: "dimInactive" }
|
||||
SliderRow { setting: "dimStrength"; divider: false }
|
||||
|
||||
// The amount only means anything while dimming is on, so it goes quiet
|
||||
// rather than disappearing: a row that vanishes takes the explanation
|
||||
// of what the switch above it does with it.
|
||||
SliderRow {
|
||||
setting: "dimStrength"
|
||||
enabled: DesktopPreferences.get("dimInactive") === true
|
||||
opacity: enabled ? 1 : 0.4
|
||||
}
|
||||
|
||||
SliderRow { setting: "inactiveOpacity"; divider: false }
|
||||
}
|
||||
|
||||
// What this session genuinely cannot do, said plainly -- and for the
|
||||
// right reason. On Wayland there is no protocol for sticky, slow or
|
||||
// bounce keys: each compositor implements its own (mutter does, which is
|
||||
// how GNOME has them on Wayland), and Hyprland does not yet. An earlier
|
||||
// version blamed "X11 feature with no Wayland equivalent", which sent
|
||||
// anyone who needs sticky keys to the wrong conclusion about the whole
|
||||
// platform. There is also deliberately no handoff to GNOME's
|
||||
// universal-access panel: its toggles are applied by GNOME Shell, and
|
||||
// the few that work through plain gsettings (cursor size, text scale,
|
||||
// high contrast) are owned by the controls above on this very page.
|
||||
SettingsCard {
|
||||
title: "Keyboard accessibility"
|
||||
subtitle: "Sticky, slow and bounce keys are implemented by each Wayland compositor for itself; Hyprland does not implement them yet, so they are unavailable in this session. Offering switches here would store preferences nothing acts on."
|
||||
title: "Hearing"
|
||||
subtitle: "What the desktop does instead of making a sound."
|
||||
|
||||
ToggleRow { setting: "visualAlerts" }
|
||||
|
||||
SettingRow {
|
||||
label: "Mono audio"
|
||||
detail: "Not offered yet — folding stereo into one channel is a real change to the PipeWire graph, and a switch that only looked like it did that would be worse than its absence. Balance lives on the Sound page meanwhile."
|
||||
controlWidth: 90
|
||||
divider: false
|
||||
|
||||
SoundBadge {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Not yet"
|
||||
tone: Theme.fgMuted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Keyboard & pointer"
|
||||
subtitle: "Key behaviour lives with the Keyboard settings; what the pointer does when you stop moving it lives here."
|
||||
|
||||
ActionRow {
|
||||
label: "Screen reader"
|
||||
detail: "Orca reads the screen aloud and works over the accessibility bus, which does run here"
|
||||
action: "Start Orca"
|
||||
label: "Key repeat"
|
||||
detail: "Delay and speed are on the Keyboard page, with the rest of the keymap"
|
||||
action: "Open Keyboard"
|
||||
onTriggered: ShellState.openSettings("shortcuts")
|
||||
}
|
||||
|
||||
SliderRow { setting: "cursorInactiveTimeout"; zeroLabel: "Never"; divider: false }
|
||||
|
||||
// The card's rows sit flush against each other; the note is a
|
||||
// separate thing and needs the gap to read as one.
|
||||
Item { width: 1; height: 10 }
|
||||
|
||||
// The receipts, not a warning. See SettingsNote for why this is quiet.
|
||||
SettingsNote {
|
||||
headline: "Sticky, slow and bounce keys are not offered"
|
||||
body: "Hyprland has no such options — asked of the running compositor rather than assumed — and GNOME's switches for them are applied by a daemon this session does not run, so a switch here would be wired to nothing. On Wayland each compositor implements these for itself; if Hyprland grows them, they land on this page."
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Screen reader"
|
||||
subtitle: "Orca is a separate application and is reported as one: either its process is running or it is not."
|
||||
|
||||
SettingRow {
|
||||
label: "Orca"
|
||||
detail: root.screenReaderDetail
|
||||
controlWidth: 200
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 9
|
||||
|
||||
SoundBadge {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Accessibility.orcaRunning ? "Running" : "Stopped"
|
||||
tone: Accessibility.orcaRunning ? Theme.ok : Theme.fgMuted
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Accessibility.orcaRunning ? "Stop" : "Start"
|
||||
onClicked: {
|
||||
if (Accessibility.orcaRunning)
|
||||
Accessibility.stopOrca();
|
||||
else
|
||||
Accessibility.startOrca();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingRow {
|
||||
label: "This app, read aloud"
|
||||
detail: "Every settings row carries a spoken name and takes keyboard focus — Tab walks the page, Space flips a switch, the arrows move a slider or step a choice"
|
||||
controlWidth: 90
|
||||
divider: false
|
||||
onTriggered: SystemSettings.openApplication("orca")
|
||||
|
||||
SoundBadge {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Built in"
|
||||
tone: Theme.ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user