diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index d72c44245ee..9f820c67f6e 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -32,6 +32,9 @@ use winit::{ window::{CursorGrabMode, Window, WindowButtons, WindowLevel}, }; +use std::sync::Arc; +use std::sync::RwLock; + pub fn screen_size_in_pixels(window: &Window) -> egui::Vec2 { let size = if cfg!(target_os = "ios") { // `outer_size` Includes the area behind the "dynamic island". @@ -102,7 +105,7 @@ pub struct State { has_sent_ime_enabled: bool, #[cfg(feature = "accesskit")] - accesskit: Option, + accesskit: Option>>, allow_ime: bool, ime_rect_px: Option, @@ -172,11 +175,14 @@ impl State { ) { profiling::function_scope!(); - self.accesskit = Some(accesskit_winit::Adapter::with_event_loop_proxy( - event_loop, - window, - event_loop_proxy, - )); + self.accesskit = Some(Arc::new(RwLock::new( + accesskit_winit::Adapter::with_event_loop_proxy(event_loop, window, event_loop_proxy), + ))); + } + + #[cfg(feature = "accesskit")] + pub fn init_accesskit_with_adapter(&mut self, adapter: Arc>) { + self.accesskit = Some(adapter); } /// Call this once a graphics context has been created to update the maximum texture dimensions @@ -270,7 +276,8 @@ impl State { profiling::function_scope!(short_window_event_description(event)); #[cfg(feature = "accesskit")] - if let Some(accesskit) = self.accesskit.as_mut() { + if let Some(accesskit) = self.accesskit.as_ref() { + let mut accesskit = accesskit.write().unwrap(); accesskit.process_event(window, event); } @@ -941,9 +948,10 @@ impl State { } #[cfg(feature = "accesskit")] - if let Some(accesskit) = self.accesskit.as_mut() + if let Some(accesskit) = self.accesskit.as_ref() && let Some(update) = accesskit_update { + let mut accesskit = accesskit.write().unwrap(); profiling::scope!("accesskit"); accesskit.update_if_active(|| update); } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 5a868dd7566..702b6718c19 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -622,6 +622,36 @@ impl ContextImpl { builders.get_mut(&id).unwrap() } + #[cfg(feature = "accesskit")] + fn add_accesskit_node(&mut self, id: Id, node: accesskit::Node) { + let state = self.viewport().this_pass.accesskit_state.as_mut().unwrap(); + let nodes = &mut state.nodes; + assert_eq!(nodes.insert(id, node), None); + + /// Find the first ancestor that already has an accesskit node. + fn find_accesskit_parent( + parent_map: &IdMap, + node_map: &IdMap, + id: Id, + ) -> Option { + if let Some(parent_id) = parent_map.get(&id) { + if node_map.contains_key(parent_id) { + Some(*parent_id) + } else { + find_accesskit_parent(parent_map, node_map, *parent_id) + } + } else { + None + } + } + + let parent_id = find_accesskit_parent(&state.parent_map, nodes, id) + .unwrap_or(crate::accesskit_root_id()); + + let parent_builder = nodes.get_mut(&parent_id).unwrap(); + parent_builder.push_child(id.accesskit_id()); + } + fn pixels_per_point(&mut self) -> f32 { self.viewport().input.pixels_per_point } @@ -3515,6 +3545,31 @@ impl Context { }) } + /// If AccessKit support is active for the current frame, build a new node + /// with the given ID using `writer`. The writer receives the node and a struct + /// that allows it to insert nodes for other descendants as needed. + /// + /// Returns `None` if acesskit is off. + // TODO(emilk): consider making both read-only and read-write versions + #[cfg(feature = "accesskit")] + pub fn accesskit_subtree_builder( + &self, + id: Id, + writer: impl FnOnce(&mut accesskit::Node, &mut crate::pass_state::AccessKitPassState) -> R, + ) -> Option { + self.write(|ctx| { + let mut node = accesskit::Node::default(); + let accesskit_state = &mut ctx.viewport().this_pass.accesskit_state; + let result = accesskit_state + .as_mut() + .map(|state| writer(&mut node, state)); + if accesskit_state.is_some() { + ctx.add_accesskit_node(id, node); + } + result + }) + } + #[cfg(feature = "accesskit")] pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) { self.write(|ctx| {