From 80298403f7821e133351224d69a589161b8622b0 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 30 Aug 2026 10:25:58 +0200 Subject: [PATCH] Name the launch screen's entries, and let its folder rows be pressed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A heading above a text field names it on screen and names nothing to the platform: Slint associates the two only if something says so, and nothing did. So the four entries a user signs in through — server, username, app password, folder — reached AT-SPI as unnamed boxes with a separate piece of static text floating above each. The word is now written twice, deliberately, and the second copy is the one attached to the control being typed into. FolderRow is the more consequential half. It is a Rectangle with a TouchArea over it, which is a button to the user and a decorated box to the platform — its label got through, because the `Value` inside is a Text, while the fact that the row could be entered at all did not. The folder picker was therefore readable and not navigable, which for the screen that chooses where the whole library lives is the difference between using the application and not. Co-Authored-By: Claude Opus 5 (1M context) --- ui/dr-ui/ui/launch.slint | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ui/dr-ui/ui/launch.slint b/ui/dr-ui/ui/launch.slint index cae83d0..7c18cdd 100644 --- a/ui/dr-ui/ui/launch.slint +++ b/ui/dr-ui/ui/launch.slint @@ -41,11 +41,21 @@ component FormButton inherits Button { } // One folder in the picker. The whole row is the target, not just the text. +// +// A `Rectangle` with a `TouchArea` over it is a button as far as the user is +// concerned and a decorated box as far as the platform is, so the name reaches +// a screen reader — the `Value` below is a `Text` — while the fact that it can +// be entered does not. Saying so is what makes the picker navigable rather +// than merely readable (NFR-A11Y-2). component FolderRow inherits Rectangle { in property label; in property is-parent: false; callback clicked(); + accessible-role: button; + accessible-label: root.label; + accessible-action-default => { root.clicked(); } + height: Theme.touch-target; background: touch.has-hover ? Theme.surface-raised : transparent; border-radius: 3px; @@ -180,6 +190,13 @@ export component LaunchScreen inherits Rectangle { PanelHeading { text: @tr("SERVER"); } server-input := Field { + // The `PanelHeading` above each of these entries names + // it on screen and names nothing at all as far as the + // accessibility tree is concerned — Slint associates a + // heading with the control below it only if something + // says so. `Field.label` is that something, so the word + // is written twice on purpose. + label: @tr("Server address"); text: root.server-url; placeholder: "https://cloud.example.com"; accepted(url) => { root.sign-in(url); } @@ -227,12 +244,14 @@ export component LaunchScreen inherits Rectangle { PanelHeading { text: @tr("USERNAME"); } user-input := Field { + label: @tr("Username"); text: ""; placeholder: @tr("your Nextcloud username"); } PanelHeading { text: @tr("APP PASSWORD"); } pass-input := Field { + label: @tr("App password"); text: ""; placeholder: "xxxxx-xxxxx-xxxxx-xxxxx-xxxxx"; secret: true; @@ -285,6 +304,7 @@ export component LaunchScreen inherits Rectangle { PanelHeading { text: @tr("FOLDER"); } folder-input := Field { + label: @tr("Library folder"); text: root.folder-path; placeholder: "/home/you/Pictures"; accepted(path) => { root.use-folder(path); }