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/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"); 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..b0629e6bd90 100644 --- a/internal/backends/winit/accesskit.rs +++ b/internal/backends/winit/accesskit.rs @@ -445,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/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: 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/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/lineedit.slint b/internal/compiler/widgets/cupertino-base/lineedit.slint index 24934537758..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-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/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/lineedit.slint b/internal/compiler/widgets/fluent-base/lineedit.slint index aa318b4bf61..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-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/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/lineedit.slint b/internal/compiler/widgets/material-base/lineedit.slint index 63a7703b487..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-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/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); 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); 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,