From 853d49272471cc930532798840f3101ae4bca81f Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Sun, 5 Feb 2023 12:10:40 -0600 Subject: [PATCH] Update for AccessKit refactor that drastically reduces memory usage (#2678) * Update for AccessKit refactor that drastically reduces memory usage * changelog entry * satisfy clippy --- CHANGELOG.md | 1 + Cargo.lock | 87 +++++--------------- crates/eframe/src/native/epi_integration.rs | 2 +- crates/egui-winit/Cargo.toml | 2 +- crates/egui/Cargo.toml | 2 +- crates/egui/src/context.rs | 81 +++++++++++------- crates/egui/src/frame_state.rs | 2 +- crates/egui/src/lib.rs | 22 ----- crates/egui/src/response.rs | 39 ++++----- crates/egui/src/widgets/drag_value.rs | 18 ++-- crates/egui/src/widgets/slider.rs | 16 ++-- crates/egui/src/widgets/text_edit/builder.rs | 28 ++++--- 12 files changed, 131 insertions(+), 169 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67d7b874c..9daf9a3e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG * Default `ComboBox` is now controlled with `Spacing::combo_width` ([#2621](https://github.com/emilk/egui/pull/2621)). * `DragValue` and `Slider` now use the proportional font ([#2638](https://github.com/emilk/egui/pull/2638)). * `ScrollArea` is less aggressive about clipping its contents ([#2665](https://github.com/emilk/egui/pull/2665)). +* Updated to be compatible with a major breaking change in AccessKit that drastically reduces memory usage when accessibility is enabled ([#2678](https://github.com/emilk/egui/pull/2678)). ### Fixed 🐛 * Trigger `PointerEvent::Released` for drags ([#2507](https://github.com/emilk/egui/pull/2507)). diff --git a/Cargo.lock b/Cargo.lock index 6a7cb99dc..5bf9c2c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,20 +20,19 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3083ac5a97521e35388ca80cf365b6be5210962cc59f11ee238cd92ac2fa9524" +checksum = "4803cf8c252f374ae6bfbb341e49e5a37f7601f2ce74a105927a663eba952c67" dependencies = [ - "enumset", - "kurbo", + "enumn", "serde", ] [[package]] name = "accesskit_consumer" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f47393f706a2d2f9d1ebd109351f886afd256a09d2308861a6dec0853a625e2" +checksum = "cee8cf1202a4f94d31837f1902ab0a75c77b65bf59719e093703abe83efd74ec" dependencies = [ "accesskit", "parking_lot", @@ -41,9 +40,9 @@ dependencies = [ [[package]] name = "accesskit_macos" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabafb94d8a4dd6b20fe4112f943756ff8dc9778e3d742fb5478bf7f000a3282" +checksum = "10be25f2b27bc33aa1647072e86b948b41596f1af1ae43a2b4b9be5d2011cbda" dependencies = [ "accesskit", "accesskit_consumer", @@ -54,9 +53,9 @@ dependencies = [ [[package]] name = "accesskit_unix" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf322ecf51ac3fb9d3016382e5515122650d3fe70afe544322215e9a54f68f" +checksum = "630e7ee8f93c6246478bf0df6760db899b28d9ad54353a5f2d3157138ba817fc" dependencies = [ "accesskit", "accesskit_consumer", @@ -70,9 +69,9 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "620160ad7d0aec2b4ae487bc8fbf0367982651d27bf8908a49d03548cfce73ec" +checksum = "a13c462fabdd950ef14308a9390b07fa2e2e3aabccba1f3ea36ea2231bb942ab" dependencies = [ "accesskit", "accesskit_consumer", @@ -85,9 +84,9 @@ dependencies = [ [[package]] name = "accesskit_winit" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad401ccee3adde31edbbf7e8c7dd3fcb3fb916f7e519c135608b6b5231d633d4" +checksum = "17727888757ec027ec221db33070e226ee07df44425b583bc67684204d35eff9" dependencies = [ "accesskit", "accesskit_macos", @@ -1071,18 +1070,8 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" -dependencies = [ - "darling_core 0.14.2", - "darling_macro 0.14.2", + "darling_core", + "darling_macro", ] [[package]] @@ -1099,37 +1088,13 @@ dependencies = [ "syn", ] -[[package]] -name = "darling_core" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core 0.13.4", - "quote", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" -dependencies = [ - "darling_core 0.14.2", + "darling_core", "quote", "syn", ] @@ -1519,22 +1484,11 @@ dependencies = [ ] [[package]] -name = "enumset" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" -dependencies = [ - "enumset_derive", - "serde", -] - -[[package]] -name = "enumset_derive" -version = "0.6.1" +name = "enumn" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" dependencies = [ - "darling 0.14.2", "proc-macro2", "quote", "syn", @@ -2223,7 +2177,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" dependencies = [ "arrayvec", - "serde", ] [[package]] @@ -2451,7 +2404,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro-crate", "proc-macro2", "quote", diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index f38766cb7..9a844e3cb 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -380,7 +380,7 @@ impl EpiIntegration { egui_ctx.enable_accesskit(); // Enqueue a repaint so we'll receive a full tree update soon. egui_ctx.request_repaint(); - egui::accesskit_placeholder_tree_update() + egui_ctx.accesskit_placeholder_tree_update() }); } diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 576f3dafc..906c7635e 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -55,7 +55,7 @@ winit = { version = "0.28", default-features = false } #! ### Optional dependencies # feature accesskit -accesskit_winit = { version = "0.9.0", optional = true } +accesskit_winit = { version = "0.10.0", optional = true } ## Enable this when generating docs. document-features = { version = "0.2", optional = true } diff --git a/crates/egui/Cargo.toml b/crates/egui/Cargo.toml index 552fe18e1..ec5ed7096 100644 --- a/crates/egui/Cargo.toml +++ b/crates/egui/Cargo.toml @@ -70,7 +70,7 @@ nohash-hasher = "0.2" #! ### Optional dependencies ## Exposes detailed accessibility implementation required by platform ## accessibility APIs. Also requires support in the egui integration. -accesskit = { version = "0.8.1", optional = true } +accesskit = { version = "0.9.0", optional = true } ## Enable this when generating docs. document-features = { version = "0.2", optional = true } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 99cb9f050..e4d5de5dc 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -70,6 +70,8 @@ struct ContextImpl { #[cfg(feature = "accesskit")] is_accesskit_enabled: bool, + #[cfg(feature = "accesskit")] + accesskit_node_classes: accesskit::NodeClassSet, } impl ContextImpl { @@ -113,17 +115,14 @@ impl ContextImpl { if self.is_accesskit_enabled { use crate::frame_state::AccessKitFrameState; let id = crate::accesskit_root_id(); - let node = Box::new(accesskit::Node { - role: accesskit::Role::Window, - transform: Some( - accesskit::kurbo::Affine::scale(self.input.pixels_per_point().into()).into(), - ), - ..Default::default() - }); - let mut nodes = IdMap::default(); - nodes.insert(id, node); + let mut builder = accesskit::NodeBuilder::new(accesskit::Role::Window); + builder.set_transform(accesskit::Affine::scale( + self.input.pixels_per_point().into(), + )); + let mut node_builders = IdMap::default(); + node_builders.insert(id, builder); self.frame_state.accesskit_state = Some(AccessKitFrameState { - nodes, + node_builders, parent_stack: vec![id], }); } @@ -156,16 +155,16 @@ impl ContextImpl { } #[cfg(feature = "accesskit")] - fn accesskit_node(&mut self, id: Id) -> &mut accesskit::Node { + fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::NodeBuilder { let state = self.frame_state.accesskit_state.as_mut().unwrap(); - let nodes = &mut state.nodes; - if let std::collections::hash_map::Entry::Vacant(entry) = nodes.entry(id) { + let builders = &mut state.node_builders; + if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) { entry.insert(Default::default()); let parent_id = state.parent_stack.last().unwrap(); - let parent = nodes.get_mut(parent_id).unwrap(); - parent.children.push(id.accesskit_id()); + let parent_builder = builders.get_mut(parent_id).unwrap(); + parent_builder.push_child(id.accesskit_id()); } - nodes.get_mut(&id).unwrap() + builders.get_mut(&id).unwrap() } } @@ -655,7 +654,7 @@ impl Context { // Make sure anything that can receive focus has an AccessKit node. // TODO(mwcampbell): For nodes that are filled from widget info, // some information is written to the node twice. - self.accesskit_node(id, |node| response.fill_accesskit_node_common(node)); + self.accesskit_node_builder(id, |builder| response.fill_accesskit_node_common(builder)); } let clicked_elsewhere = response.clicked_elsewhere(); @@ -1128,12 +1127,20 @@ impl Context { if let Some(state) = state { let has_focus = self.input(|i| i.raw.has_focus); let root_id = crate::accesskit_root_id().accesskit_id(); - platform_output.accesskit_update = Some(accesskit::TreeUpdate { - nodes: state - .nodes + let nodes = self.write(|ctx| { + state + .node_builders .into_iter() - .map(|(id, node)| (id.accesskit_id(), Arc::from(node))) - .collect(), + .map(|(id, builder)| { + ( + id.accesskit_id(), + builder.build(&mut ctx.accesskit_node_classes), + ) + }) + .collect() + }); + platform_output.accesskit_update = Some(accesskit::TreeUpdate { + nodes, tree: Some(accesskit::Tree::new(root_id)), focus: has_focus.then(|| { let focus_id = self.memory(|mem| mem.interaction.focus.id); @@ -1720,8 +1727,8 @@ impl Context { } /// If AccessKit support is active for the current frame, get or create - /// a node with the specified ID and return a mutable reference to it. - /// For newly crated nodes, the parent is the node with the ID at the top + /// a node builder with the specified ID and return a mutable reference to it. + /// For newly created nodes, the parent is the node with the ID at the top /// of the stack managed by [`Context::with_accessibility_parent`]. /// /// The `Context` lock is held while the given closure is called! @@ -1729,16 +1736,16 @@ impl Context { /// Returns `None` if acesskit is off. // TODO: consider making both RO and RW versions #[cfg(feature = "accesskit")] - pub fn accesskit_node( + pub fn accesskit_node_builder( &self, id: Id, - writer: impl FnOnce(&mut accesskit::Node) -> R, + writer: impl FnOnce(&mut accesskit::NodeBuilder) -> R, ) -> Option { self.write(|ctx| { ctx.frame_state .accesskit_state .is_some() - .then(|| ctx.accesskit_node(id)) + .then(|| ctx.accesskit_node_builder(id)) .map(writer) }) } @@ -1750,12 +1757,30 @@ impl Context { /// being called by the AccessKit adapter to provide the initial tree update, /// then it should do so, to provide a complete AccessKit tree to the adapter /// immediately. Otherwise, it should enqueue a repaint and use the - /// placeholder tree update from [`crate::accesskit_placeholder_tree_update`] + /// placeholder tree update from [`Context::accesskit_placeholder_tree_update`] /// in the meantime. #[cfg(feature = "accesskit")] pub fn enable_accesskit(&self) { self.write(|ctx| ctx.is_accesskit_enabled = true); } + + /// Return a tree update that the egui integration should provide to the + /// AccessKit adapter if it cannot immediately run the egui application + /// to get a full tree update after running [`Context::enable_accesskit`]. + #[cfg(feature = "accesskit")] + pub fn accesskit_placeholder_tree_update(&self) -> accesskit::TreeUpdate { + use accesskit::{NodeBuilder, Role, Tree, TreeUpdate}; + + let root_id = crate::accesskit_root_id().accesskit_id(); + self.write(|ctx| TreeUpdate { + nodes: vec![( + root_id, + NodeBuilder::new(Role::Window).build(&mut ctx.accesskit_node_classes), + )], + tree: Some(Tree::new(root_id)), + focus: None, + }) + } } #[test] diff --git a/crates/egui/src/frame_state.rs b/crates/egui/src/frame_state.rs index 715e82620..be61dbe93 100644 --- a/crates/egui/src/frame_state.rs +++ b/crates/egui/src/frame_state.rs @@ -12,7 +12,7 @@ pub(crate) struct TooltipFrameState { #[cfg(feature = "accesskit")] #[derive(Clone)] pub(crate) struct AccessKitFrameState { - pub(crate) nodes: IdMap>, + pub(crate) node_builders: IdMap, pub(crate) parent_stack: Vec, } diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 0e4d97cd5..929526bbd 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -559,25 +559,3 @@ pub fn __run_test_ui(mut add_contents: impl FnMut(&mut Ui)) { pub fn accesskit_root_id() -> Id { Id::new("accesskit_root") } - -/// Return a tree update that the egui integration should provide to the -/// AccessKit adapter if it cannot immediately run the egui application -/// to get a full tree update after running [`Context::enable_accesskit`]. -#[cfg(feature = "accesskit")] -pub fn accesskit_placeholder_tree_update() -> accesskit::TreeUpdate { - use accesskit::{Node, Role, Tree, TreeUpdate}; - use std::sync::Arc; - - let root_id = accesskit_root_id().accesskit_id(); - TreeUpdate { - nodes: vec![( - root_id, - Arc::new(Node { - role: Role::Window, - ..Default::default() - }), - )], - tree: Some(Tree::new(root_id)), - focus: None, - } -} diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index 161707fd3..ddab8d4e0 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -573,47 +573,47 @@ impl Response { self.output_event(event); } else { #[cfg(feature = "accesskit")] - self.ctx.accesskit_node(self.id, |node| { - self.fill_accesskit_node_from_widget_info(node, make_info()); + self.ctx.accesskit_node_builder(self.id, |builder| { + self.fill_accesskit_node_from_widget_info(builder, make_info()); }); } } pub fn output_event(&self, event: crate::output::OutputEvent) { #[cfg(feature = "accesskit")] - self.ctx.accesskit_node(self.id, |node| { - self.fill_accesskit_node_from_widget_info(node, event.widget_info().clone()); + self.ctx.accesskit_node_builder(self.id, |builder| { + self.fill_accesskit_node_from_widget_info(builder, event.widget_info().clone()); }); self.ctx.output_mut(|o| o.events.push(event)); } #[cfg(feature = "accesskit")] - pub(crate) fn fill_accesskit_node_common(&self, node: &mut accesskit::Node) { - node.bounds = Some(accesskit::kurbo::Rect { + pub(crate) fn fill_accesskit_node_common(&self, builder: &mut accesskit::NodeBuilder) { + builder.set_bounds(accesskit::Rect { x0: self.rect.min.x.into(), y0: self.rect.min.y.into(), x1: self.rect.max.x.into(), y1: self.rect.max.y.into(), }); if self.sense.focusable { - node.focusable = true; + builder.add_action(accesskit::Action::Focus); } - if self.sense.click && node.default_action_verb.is_none() { - node.default_action_verb = Some(accesskit::DefaultActionVerb::Click); + if self.sense.click && builder.default_action_verb().is_none() { + builder.set_default_action_verb(accesskit::DefaultActionVerb::Click); } } #[cfg(feature = "accesskit")] fn fill_accesskit_node_from_widget_info( &self, - node: &mut accesskit::Node, + builder: &mut accesskit::NodeBuilder, info: crate::WidgetInfo, ) { use crate::WidgetType; use accesskit::{CheckedState, Role}; - self.fill_accesskit_node_common(node); - node.role = match info.typ { + self.fill_accesskit_node_common(builder); + builder.set_role(match info.typ { WidgetType::Label => Role::StaticText, WidgetType::Link => Role::Link, WidgetType::TextEdit => Role::TextField, @@ -628,18 +628,18 @@ impl Response { WidgetType::DragValue => Role::SpinButton, WidgetType::ColorButton => Role::ColorWell, WidgetType::Other => Role::Unknown, - }; + }); if let Some(label) = info.label { - node.name = Some(label.into()); + builder.set_name(label); } if let Some(value) = info.current_text_value { - node.value = Some(value.into()); + builder.set_value(value); } if let Some(value) = info.value { - node.numeric_value = Some(value); + builder.set_numeric_value(value); } if let Some(selected) = info.selected { - node.checked_state = Some(if selected { + builder.set_checked_state(if selected { CheckedState::True } else { CheckedState::False @@ -662,8 +662,9 @@ impl Response { /// ``` pub fn labelled_by(self, id: Id) -> Self { #[cfg(feature = "accesskit")] - self.ctx - .accesskit_node(self.id, |node| node.labelled_by.push(id.accesskit_id())); + self.ctx.accesskit_node_builder(self.id, |builder| { + builder.push_labelled_by(id.accesskit_id()); + }); #[cfg(not(feature = "accesskit"))] { let _ = id; diff --git a/crates/egui/src/widgets/drag_value.rs b/crates/egui/src/widgets/drag_value.rs index d00516889..2892538ac 100644 --- a/crates/egui/src/widgets/drag_value.rs +++ b/crates/egui/src/widgets/drag_value.rs @@ -557,28 +557,28 @@ impl<'a> Widget for DragValue<'a> { response.widget_info(|| WidgetInfo::drag_value(value)); #[cfg(feature = "accesskit")] - ui.ctx().accesskit_node(response.id, |node| { + ui.ctx().accesskit_node_builder(response.id, |builder| { use accesskit::Action; // If either end of the range is unbounded, it's better // to leave the corresponding AccessKit field set to None, // to allow for platform-specific default behavior. if clamp_range.start().is_finite() { - node.min_numeric_value = Some(*clamp_range.start()); + builder.set_min_numeric_value(*clamp_range.start()); } if clamp_range.end().is_finite() { - node.max_numeric_value = Some(*clamp_range.end()); + builder.set_max_numeric_value(*clamp_range.end()); } - node.numeric_value_step = Some(speed); - node.actions |= Action::SetValue; + builder.set_numeric_value_step(speed); + builder.add_action(Action::SetValue); if value < *clamp_range.end() { - node.actions |= Action::Increment; + builder.add_action(Action::Increment); } if value > *clamp_range.start() { - node.actions |= Action::Decrement; + builder.add_action(Action::Decrement); } // The name field is set to the current value by the button, // but we don't want it set that way on this widget type. - node.name = None; + builder.clear_name(); // Always expose the value as a string. This makes the widget // more stable to accessibility users as it switches // between edit and button modes. This is particularly important @@ -599,7 +599,7 @@ impl<'a> Widget for DragValue<'a> { // when in edit mode. if !is_kb_editing { let value_text = format!("{}{}{}", prefix, value_text, suffix); - node.value = Some(value_text.into()); + builder.set_value(value_text); } }); diff --git a/crates/egui/src/widgets/slider.rs b/crates/egui/src/widgets/slider.rs index a72ee08e0..697b10a82 100644 --- a/crates/egui/src/widgets/slider.rs +++ b/crates/egui/src/widgets/slider.rs @@ -792,18 +792,20 @@ impl<'a> Slider<'a> { response.widget_info(|| WidgetInfo::slider(value, self.text.text())); #[cfg(feature = "accesskit")] - ui.ctx().accesskit_node(response.id, |node| { + ui.ctx().accesskit_node_builder(response.id, |builder| { use accesskit::Action; - node.min_numeric_value = Some(*self.range.start()); - node.max_numeric_value = Some(*self.range.end()); - node.numeric_value_step = self.step; - node.actions |= Action::SetValue; + builder.set_min_numeric_value(*self.range.start()); + builder.set_max_numeric_value(*self.range.end()); + if let Some(step) = self.step { + builder.set_numeric_value_step(step); + } + builder.add_action(Action::SetValue); let clamp_range = self.clamp_range(); if value < *clamp_range.end() { - node.actions |= Action::Increment; + builder.add_action(Action::Increment); } if value > *clamp_range.start() { - node.actions |= Action::Decrement; + builder.add_action(Action::Decrement); } }); diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 456f85fa5..68cc2aae8 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -666,7 +666,7 @@ impl<'t> TextEdit<'t> { #[cfg(feature = "accesskit")] { - let parent_id = ui.ctx().accesskit_node(response.id, |node| { + let parent_id = ui.ctx().accesskit_node_builder(response.id, |builder| { use accesskit::{TextPosition, TextSelection}; let parent_id = response.id; @@ -674,7 +674,7 @@ impl<'t> TextEdit<'t> { if let Some(cursor_range) = &cursor_range { let anchor = &cursor_range.secondary.rcursor; let focus = &cursor_range.primary.rcursor; - node.text_selection = Some(TextSelection { + builder.set_text_selection(TextSelection { anchor: TextPosition { node: parent_id.with(anchor.row).accesskit_id(), character_index: anchor.column, @@ -686,8 +686,10 @@ impl<'t> TextEdit<'t> { }); } - node.default_action_verb = Some(accesskit::DefaultActionVerb::Focus); - node.multiline = self.multiline; + builder.set_default_action_verb(accesskit::DefaultActionVerb::Focus); + if self.multiline { + builder.set_multiline(); + } parent_id }); @@ -699,16 +701,16 @@ impl<'t> TextEdit<'t> { ui.ctx().with_accessibility_parent(parent_id, || { for (i, row) in galley.rows.iter().enumerate() { let id = parent_id.with(i); - ui.ctx().accesskit_node(id, |node| { - node.role = Role::InlineTextBox; + ui.ctx().accesskit_node_builder(id, |builder| { + builder.set_role(Role::InlineTextBox); let rect = row.rect.translate(text_draw_pos.to_vec2()); - node.bounds = Some(accesskit::kurbo::Rect { + builder.set_bounds(accesskit::Rect { x0: rect.min.x.into(), y0: rect.min.y.into(), x1: rect.max.x.into(), y1: rect.max.y.into(), }); - node.text_direction = Some(TextDirection::LeftToRight); + builder.set_text_direction(TextDirection::LeftToRight); // TODO(mwcampbell): Set more node fields for the row // once AccessKit adapters expose text formatting info. @@ -748,11 +750,11 @@ impl<'t> TextEdit<'t> { } word_lengths.push((character_lengths.len() - last_word_start) as _); - node.value = Some(value.into()); - node.character_lengths = character_lengths.into(); - node.character_positions = Some(character_positions.into()); - node.character_widths = Some(character_widths.into()); - node.word_lengths = word_lengths.into(); + builder.set_value(value); + builder.set_character_lengths(character_lengths); + builder.set_character_positions(character_positions); + builder.set_character_widths(character_widths); + builder.set_word_lengths(word_lengths); }); } });