From cf17ae0861d911d63962ca209fe905a6e3bb8e76 Mon Sep 17 00:00:00 2001 From: Arnold Loubriat Date: Sun, 23 Jun 2024 14:52:33 +0200 Subject: [PATCH 1/7] Add the `accessible-placeholder-text` property --- api/cpp/include/slint-testing.h | 7 +++++++ docs/reference/src/language/builtins/elements.md | 1 + examples/todo-mvc/ui/views/create_task_view.slint | 3 ++- examples/todo/cpp/tests/test_todo_basic.cpp | 2 +- internal/backends/testing/search_api.rs | 10 ++++++++++ internal/backends/winit/accesskit.rs | 11 ++++++++++- internal/compiler/typeregister.rs | 1 + internal/compiler/widgets/cosmic-base/lineedit.slint | 2 +- .../compiler/widgets/cupertino-base/lineedit.slint | 2 +- internal/compiler/widgets/fluent-base/lineedit.slint | 2 +- .../compiler/widgets/material-base/lineedit.slint | 2 +- internal/core/accessibility.rs | 1 + 12 files changed, 37 insertions(+), 7 deletions(-) diff --git a/api/cpp/include/slint-testing.h b/api/cpp/include/slint-testing.h index b43cd390d5a..b2db3da1258 100644 --- a/api/cpp/include/slint-testing.h +++ b/api/cpp/include/slint-testing.h @@ -232,6 +232,13 @@ class ElementHandle return get_accessible_string_property(cbindgen_private::AccessibleStringProperty::Value); } + /// Returns the accessible-placeholder-text of that element, if any. + std::optional accessible_placeholder_text() const + { + return get_accessible_string_property( + cbindgen_private::AccessibleStringProperty::PlaceholderText); + } + /// Returns the accessible-description of that element, if any. std::optional accessible_description() const { diff --git a/docs/reference/src/language/builtins/elements.md b/docs/reference/src/language/builtins/elements.md index 5428d965e1d..09225a5af14 100644 --- a/docs/reference/src/language/builtins/elements.md +++ b/docs/reference/src/language/builtins/elements.md @@ -73,6 +73,7 @@ Use the following `accessible-` properties to make your items interact well with - **`accessible-value-minimum`** (_in_ _float_): The minimum value of the item. - **`accessible-value-step`** (_in_ _float_) The smallest increment or decrement by which the current value can change. This corresponds to the step by which a handle on a slider can be dragged. - **`accessible-value`** (_in_ _string_): The current value of the item. +- **`accessible-placeholder-text`** (_in_ _string_): A placeholder text to use when the item's value is empty. Applies to text elements. You can also use the following callbacks that are going to be called by the accessibility framework: diff --git a/examples/todo-mvc/ui/views/create_task_view.slint b/examples/todo-mvc/ui/views/create_task_view.slint index 38e9e644a28..a45c3ead029 100644 --- a/examples/todo-mvc/ui/views/create_task_view.slint +++ b/examples/todo-mvc/ui/views/create_task_view.slint @@ -58,7 +58,7 @@ export component CreateTaskView { VerticalLayout { spacing: SpaceSettings.default-spacing; - Text { + title-label := Text { text: @tr("Task name"); color: TodoPalette.foreground; font-size: FontSettings.body-strong.font-size; @@ -69,6 +69,7 @@ export component CreateTaskView { title-input := LineEdit { placeholder-text: @tr("Describe your task"); + accessible-label: title-label.text; } } diff --git a/examples/todo/cpp/tests/test_todo_basic.cpp b/examples/todo/cpp/tests/test_todo_basic.cpp index 8b1b05c08a4..d67fc4a9701 100644 --- a/examples/todo/cpp/tests/test_todo_basic.cpp +++ b/examples/todo/cpp/tests/test_todo_basic.cpp @@ -18,7 +18,7 @@ SCENARIO("Basic TEST") state.mainWindow, [](slint::testing::ElementHandle element) -> std::optional { - if (element.accessible_label() == "What needs to be done?") { + if (element.accessible_placeholder_text() == "What needs to be done?") { return element; } else { return {}; diff --git a/internal/backends/testing/search_api.rs b/internal/backends/testing/search_api.rs index 7c69cdc92ac..3ccc5dd9bd7 100644 --- a/internal/backends/testing/search_api.rs +++ b/internal/backends/testing/search_api.rs @@ -352,6 +352,16 @@ impl ElementHandle { .and_then(|item| item.accessible_string_property(AccessibleStringProperty::Value)) } + /// Returns the value of the element's `accessible-placeholder-text` property, if present. + pub fn accessible_placeholder_text(&self) -> Option { + if self.element_index != 0 { + return None; + } + self.item.upgrade().and_then(|item| { + item.accessible_string_property(AccessibleStringProperty::PlaceholderText) + }) + } + /// Sets the value of the element's `accessible-value` property. Note that you can only set this /// property if it is declared in your Slint code. pub fn set_accessible_value(&self, value: impl Into) { diff --git a/internal/backends/winit/accesskit.rs b/internal/backends/winit/accesskit.rs index 9013a2fea9a..4378c8cf4f4 100644 --- a/internal/backends/winit/accesskit.rs +++ b/internal/backends/winit/accesskit.rs @@ -437,7 +437,16 @@ impl NodeCollection { builder.set_numeric_value_step(step); } - if let Some(value) = item.accessible_string_property(AccessibleStringProperty::Value) { + let value = item.accessible_string_property(AccessibleStringProperty::Value); + if value.is_none() || value.as_ref().is_some_and(|x| x.is_empty()) { + if let Some(placeholder) = item + .accessible_string_property(AccessibleStringProperty::PlaceholderText) + .filter(|x| !x.is_empty()) + { + builder.set_placeholder(placeholder.to_string()); + } + } + if let Some(value) = value { if let Ok(value) = value.parse() { builder.set_numeric_value(value); } else { diff --git a/internal/compiler/typeregister.rs b/internal/compiler/typeregister.rs index 2d6bfa9b5a0..1e1b262274d 100644 --- a/internal/compiler/typeregister.rs +++ b/internal/compiler/typeregister.rs @@ -111,6 +111,7 @@ pub fn reserved_accessibility_properties() -> impl Iterator base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-label: placeholder-text; + accessible-placeholder-text: placeholder-text; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/compiler/widgets/cupertino-base/lineedit.slint b/internal/compiler/widgets/cupertino-base/lineedit.slint index 24934537758..51d7f73be43 100644 --- a/internal/compiler/widgets/cupertino-base/lineedit.slint +++ b/internal/compiler/widgets/cupertino-base/lineedit.slint @@ -19,7 +19,7 @@ export component LineEdit { callback edited <=> i-base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-label: placeholder-text; + accessible-placeholder-text: placeholder-text; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/compiler/widgets/fluent-base/lineedit.slint b/internal/compiler/widgets/fluent-base/lineedit.slint index aa318b4bf61..593a02c92d7 100644 --- a/internal/compiler/widgets/fluent-base/lineedit.slint +++ b/internal/compiler/widgets/fluent-base/lineedit.slint @@ -18,7 +18,7 @@ export component LineEdit { callback edited <=> i-base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-label: placeholder-text; + accessible-placeholder-text: placeholder-text; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/compiler/widgets/material-base/lineedit.slint b/internal/compiler/widgets/material-base/lineedit.slint index 63a7703b487..4f8f0f98251 100644 --- a/internal/compiler/widgets/material-base/lineedit.slint +++ b/internal/compiler/widgets/material-base/lineedit.slint @@ -19,7 +19,7 @@ export component LineEdit { callback edited <=> i-base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-label: placeholder-text; + accessible-placeholder-text: placeholder-text; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/core/accessibility.rs b/internal/core/accessibility.rs index 7dcf9d286a0..02fe876c7e7 100644 --- a/internal/core/accessibility.rs +++ b/internal/core/accessibility.rs @@ -20,6 +20,7 @@ pub enum AccessibleStringProperty { DelegateFocus, Description, Label, + PlaceholderText, Value, ValueMaximum, ValueMinimum, From 412278fd5429770749ebefc055af01933a6b8553 Mon Sep 17 00:00:00 2001 From: Arnold Loubriat Date: Tue, 25 Jun 2024 20:29:21 +0200 Subject: [PATCH 2/7] Move logic to clear `accessible-placeholder-text` to widgets --- internal/backends/winit/accesskit.rs | 18 ++++++++---------- .../widgets/cosmic-base/lineedit.slint | 2 +- .../widgets/cupertino-base/lineedit.slint | 2 +- .../widgets/fluent-base/lineedit.slint | 2 +- .../widgets/material-base/lineedit.slint | 2 +- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/internal/backends/winit/accesskit.rs b/internal/backends/winit/accesskit.rs index 4378c8cf4f4..b0629e6bd90 100644 --- a/internal/backends/winit/accesskit.rs +++ b/internal/backends/winit/accesskit.rs @@ -437,16 +437,7 @@ impl NodeCollection { builder.set_numeric_value_step(step); } - let value = item.accessible_string_property(AccessibleStringProperty::Value); - if value.is_none() || value.as_ref().is_some_and(|x| x.is_empty()) { - if let Some(placeholder) = item - .accessible_string_property(AccessibleStringProperty::PlaceholderText) - .filter(|x| !x.is_empty()) - { - builder.set_placeholder(placeholder.to_string()); - } - } - if let Some(value) = value { + if let Some(value) = item.accessible_string_property(AccessibleStringProperty::Value) { if let Ok(value) = value.parse() { builder.set_numeric_value(value); } else { @@ -454,6 +445,13 @@ impl NodeCollection { } } + if let Some(placeholder) = item + .accessible_string_property(AccessibleStringProperty::PlaceholderText) + .filter(|x| !x.is_empty()) + { + builder.set_placeholder(placeholder.to_string()); + } + let supported = item.supported_accessibility_actions(); if supported.contains(SupportedAccessibilityAction::Default) { builder.add_action(accesskit::Action::Default); diff --git a/internal/compiler/widgets/cosmic-base/lineedit.slint b/internal/compiler/widgets/cosmic-base/lineedit.slint index f91f413b10e..f2366f35e52 100644 --- a/internal/compiler/widgets/cosmic-base/lineedit.slint +++ b/internal/compiler/widgets/cosmic-base/lineedit.slint @@ -18,7 +18,7 @@ export component LineEdit { callback edited <=> base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-placeholder-text: placeholder-text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/compiler/widgets/cupertino-base/lineedit.slint b/internal/compiler/widgets/cupertino-base/lineedit.slint index 51d7f73be43..1b8aa4405f6 100644 --- a/internal/compiler/widgets/cupertino-base/lineedit.slint +++ b/internal/compiler/widgets/cupertino-base/lineedit.slint @@ -19,7 +19,7 @@ export component LineEdit { callback edited <=> i-base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-placeholder-text: placeholder-text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/compiler/widgets/fluent-base/lineedit.slint b/internal/compiler/widgets/fluent-base/lineedit.slint index 593a02c92d7..7dbe513684c 100644 --- a/internal/compiler/widgets/fluent-base/lineedit.slint +++ b/internal/compiler/widgets/fluent-base/lineedit.slint @@ -18,7 +18,7 @@ export component LineEdit { callback edited <=> i-base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-placeholder-text: placeholder-text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/compiler/widgets/material-base/lineedit.slint b/internal/compiler/widgets/material-base/lineedit.slint index 4f8f0f98251..afc5724cd0a 100644 --- a/internal/compiler/widgets/material-base/lineedit.slint +++ b/internal/compiler/widgets/material-base/lineedit.slint @@ -19,7 +19,7 @@ export component LineEdit { callback edited <=> i-base.edited; accessible-role: text-input; accessible-value <=> text; - accessible-placeholder-text: placeholder-text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { From edfd7a3ac945da5a3aa354d1a870b02a6ff0a446 Mon Sep 17 00:00:00 2001 From: Arnold Loubriat Date: Tue, 25 Jun 2024 20:37:44 +0200 Subject: [PATCH 3/7] Set `accessible-placeholder-text` property on `TextEdit` widgets --- internal/compiler/widgets/cosmic-base/textedit.slint | 1 + internal/compiler/widgets/cupertino-base/textedit.slint | 1 + internal/compiler/widgets/fluent-base/textedit.slint | 1 + internal/compiler/widgets/material-base/textedit.slint | 1 + 4 files changed, 4 insertions(+) diff --git a/internal/compiler/widgets/cosmic-base/textedit.slint b/internal/compiler/widgets/cosmic-base/textedit.slint index 2bfefbd0c64..f1684a0137a 100644 --- a/internal/compiler/widgets/cosmic-base/textedit.slint +++ b/internal/compiler/widgets/cosmic-base/textedit.slint @@ -24,6 +24,7 @@ export component TextEdit { callback edited(/* text */ string); accessible-role: AccessibleRole.text-input; accessible-value <=> text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; public function set-selection-offsets(start: int, end: int) { base.set-selection-offsets(start, end); diff --git a/internal/compiler/widgets/cupertino-base/textedit.slint b/internal/compiler/widgets/cupertino-base/textedit.slint index 0ae0a1c993a..0564b928fc5 100644 --- a/internal/compiler/widgets/cupertino-base/textedit.slint +++ b/internal/compiler/widgets/cupertino-base/textedit.slint @@ -80,6 +80,7 @@ export component TextEdit { callback edited(/* text */ string); accessible-role: AccessibleRole.text-input; accessible-value <=> text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; public function set-selection-offsets(start: int,end: int){ text-input.set-selection-offsets(start, end); diff --git a/internal/compiler/widgets/fluent-base/textedit.slint b/internal/compiler/widgets/fluent-base/textedit.slint index b198a6652aa..16573f52cad 100644 --- a/internal/compiler/widgets/fluent-base/textedit.slint +++ b/internal/compiler/widgets/fluent-base/textedit.slint @@ -24,6 +24,7 @@ export component TextEdit { callback edited(/* text */ string); accessible-role: AccessibleRole.text-input; accessible-value <=> text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; public function set-selection-offsets(start: int,end: int){ base.set-selection-offsets(start, end); diff --git a/internal/compiler/widgets/material-base/textedit.slint b/internal/compiler/widgets/material-base/textedit.slint index 3b5ebe79829..4c3240307b5 100644 --- a/internal/compiler/widgets/material-base/textedit.slint +++ b/internal/compiler/widgets/material-base/textedit.slint @@ -24,6 +24,7 @@ export component TextEdit { callback edited(/* text */ string); accessible-role: AccessibleRole.text-input; accessible-value <=> text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; public function set-selection-offsets(start: int,end: int){ base.set-selection-offsets(start, end); From 3be72c79ee3268516448ad116815a4072720ce3f Mon Sep 17 00:00:00 2001 From: Arnold Loubriat Date: Tue, 25 Jun 2024 21:15:29 +0200 Subject: [PATCH 4/7] Try to fix todo test --- examples/todo/cpp/tests/test_todo_basic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/todo/cpp/tests/test_todo_basic.cpp b/examples/todo/cpp/tests/test_todo_basic.cpp index d67fc4a9701..be3416f94f8 100644 --- a/examples/todo/cpp/tests/test_todo_basic.cpp +++ b/examples/todo/cpp/tests/test_todo_basic.cpp @@ -18,7 +18,7 @@ SCENARIO("Basic TEST") state.mainWindow, [](slint::testing::ElementHandle element) -> std::optional { - if (element.accessible_placeholder_text() == "What needs to be done?") { + if (element.accessible_placeholder_text() == "") { return element; } else { return {}; From ab600350b33e4ac2b72be145305dac6894b61d1a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 26 Jun 2024 08:43:39 +0200 Subject: [PATCH 5/7] Fix failing test_todo_basic.cpp test --- examples/todo/cpp/tests/test_todo_basic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/todo/cpp/tests/test_todo_basic.cpp b/examples/todo/cpp/tests/test_todo_basic.cpp index be3416f94f8..d67fc4a9701 100644 --- a/examples/todo/cpp/tests/test_todo_basic.cpp +++ b/examples/todo/cpp/tests/test_todo_basic.cpp @@ -18,7 +18,7 @@ SCENARIO("Basic TEST") state.mainWindow, [](slint::testing::ElementHandle element) -> std::optional { - if (element.accessible_placeholder_text() == "") { + if (element.accessible_placeholder_text() == "What needs to be done?") { return element; } else { return {}; From 09988787cfd506990862c2ecef52615f8cec190f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 26 Jun 2024 09:21:36 +0200 Subject: [PATCH 6/7] Fix Rust todo test to locate the line edit by placeholder text --- examples/todo/rust/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/todo/rust/lib.rs b/examples/todo/rust/lib.rs index 88602399813..9dd407cca3b 100644 --- a/examples/todo/rust/lib.rs +++ b/examples/todo/rust/lib.rs @@ -174,10 +174,14 @@ fn press_add_adds_one_todo() { use i_slint_backend_testing::ElementHandle; let state = init(); state.todo_model.set_vec(vec![TodoItem { checked: false, title: "first".into() }]); - let line_edit = - ElementHandle::find_by_accessible_label(&state.main_window, "What needs to be done?") - .next() - .unwrap(); + let line_edit = ElementHandle::visit_elements(&state.main_window, |element| { + if element.accessible_placeholder_text().as_deref() == Some("What needs to be done?") { + std::ops::ControlFlow::Break(element) + } else { + std::ops::ControlFlow::Continue(()) + } + }) + .unwrap(); assert_eq!(line_edit.accessible_value().unwrap(), ""); line_edit.set_accessible_value("second"); From aed709b243c6d98037cef50c0100db004dd18533 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 26 Jun 2024 09:28:19 +0200 Subject: [PATCH 7/7] Set accessible-placeholder-text also for the Qt style --- internal/compiler/widgets/qt/lineedit.slint | 2 +- internal/compiler/widgets/qt/textedit.slint | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/compiler/widgets/qt/lineedit.slint b/internal/compiler/widgets/qt/lineedit.slint index 9e8def45ab6..40538b39384 100644 --- a/internal/compiler/widgets/qt/lineedit.slint +++ b/internal/compiler/widgets/qt/lineedit.slint @@ -17,7 +17,7 @@ export component LineEdit { callback edited <=> inner.edited; accessible-role: text-input; accessible-value <=> text; - accessible-label: placeholder-text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; accessible-action-set-value(v) => { text = v; edited(v); } public function set-selection-offsets(start: int, end: int) { diff --git a/internal/compiler/widgets/qt/textedit.slint b/internal/compiler/widgets/qt/textedit.slint index 3f1ca7af50f..e66736bcc9b 100644 --- a/internal/compiler/widgets/qt/textedit.slint +++ b/internal/compiler/widgets/qt/textedit.slint @@ -23,6 +23,7 @@ export component TextEdit { callback edited(/* text */ string); accessible-role: AccessibleRole.text-input; accessible-value <=> text; + accessible-placeholder-text: text == "" ? placeholder-text : ""; public function set-selection-offsets(start: int,end: int){ base.set-selection-offsets(start, end);