From 544f16c604998478ec6ff650d0e53d717f1c4007 Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Sat, 4 Nov 2023 20:38:55 -0500 Subject: [PATCH 1/2] Update to AccessKit 0.12 --- Cargo.toml | 6 ++--- examples/accesskit.rs | 43 ++++++++++------------------------- src/backend/mac/window.rs | 26 +++++++++++++++++++-- src/backend/windows/window.rs | 32 +++++++++++++++++++++++++- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5af3875e..ee9ad8d9 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,13 +56,13 @@ memchr = "2.5" # Optional dependencies raw-window-handle = { version = "0.5.0", default_features = false } -accesskit = { version = "0.11.0", optional = true } +accesskit = { version = "0.12.0", optional = true } once_cell = { version = "1", optional = true } [target.'cfg(target_os="windows")'.dependencies] scopeguard = "1.1.0" wio = "0.2.2" -accesskit_windows = { version = "0.14.0", optional = true } +accesskit_windows = { version = "0.15.0", optional = true } once_cell = "1" [target.'cfg(target_os="windows")'.dependencies.winapi] @@ -93,7 +93,7 @@ cocoa = "0.25.0" objc = "0.2.7" core-graphics = "0.23.0" bitflags = "2.0.0" -accesskit_macos = { version = "0.9.0", optional = true } +accesskit_macos = { version = "0.10.0", optional = true } [target.'cfg(any(target_os = "freebsd", target_os="linux", target_os="openbsd"))'.dependencies] ashpd = { version = "0.5", optional = true } diff --git a/examples/accesskit.rs b/examples/accesskit.rs index c0800226..c53f3609 100644 --- a/examples/accesskit.rs +++ b/examples/accesskit.rs @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{any::Any, num::NonZeroU128}; +use std::any::Any; use accesskit::{ - Action, ActionRequest, CheckedState, DefaultActionVerb, Node, NodeBuilder, NodeClassSet, - NodeId, Rect, Role, Tree, TreeUpdate, + Action, ActionRequest, Checked, DefaultActionVerb, Node, NodeBuilder, NodeClassSet, NodeId, + Rect, Role, Tree, TreeUpdate, }; use glazier::kurbo::Size; @@ -25,9 +25,9 @@ use glazier::{Application, KbKey, KeyEvent, Region, WinHandler, WindowBuilder, W const WINDOW_TITLE: &str = "Hello world"; -const WINDOW_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(1) }); -const CHECKBOX_1_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(2) }); -const CHECKBOX_2_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(3) }); +const WINDOW_ID: NodeId = NodeId(0); +const CHECKBOX_1_ID: NodeId = NodeId(1); +const CHECKBOX_2_ID: NodeId = NodeId(2); const INITIAL_FOCUS: NodeId = CHECKBOX_1_ID; const CHECKBOX_1_NAME: &str = "Checkbox 1"; @@ -59,10 +59,10 @@ fn build_checkbox(id: NodeId, checked: bool, classes: &mut NodeClassSet) -> Node builder.set_name(name); builder.add_action(Action::Focus); builder.set_default_action_verb(DefaultActionVerb::Click); - builder.set_checked_state(if checked { - CheckedState::True + builder.set_checked(if checked { + Checked::True } else { - CheckedState::False + Checked::False }); builder.build(classes) } @@ -70,7 +70,6 @@ fn build_checkbox(id: NodeId, checked: bool, classes: &mut NodeClassSet) -> Node struct HelloState { size: Size, focus: NodeId, - is_window_focused: bool, checkbox_1_checked: bool, checkbox_2_checked: bool, handle: WindowHandle, @@ -82,7 +81,6 @@ impl HelloState { Self { size: Default::default(), focus: INITIAL_FOCUS, - is_window_focused: false, checkbox_1_checked: false, checkbox_2_checked: false, handle: Default::default(), @@ -90,15 +88,11 @@ impl HelloState { } } - fn accesskit_focus(&self) -> Option { - self.is_window_focused.then_some(self.focus) - } - fn update_accesskit_focus(&self) { self.handle.update_accesskit_if_active(|| TreeUpdate { nodes: vec![], tree: None, - focus: self.accesskit_focus(), + focus: self.focus, }); } @@ -114,10 +108,7 @@ impl HelloState { } _ => unreachable!(), }; - // We have to be slightly less lazy here than we'd like because we can't - // borrow self immutably inside the closure while we have a mutable - // borrow of self.node_classes. TBD: Does this indicate a design flaw? - let focus = self.accesskit_focus(); + let focus = self.focus; let node_classes = &mut self.node_classes; self.handle.update_accesskit_if_active(|| { let node = build_checkbox(id, checked, node_classes); @@ -163,7 +154,7 @@ impl WinHandler for HelloState { (CHECKBOX_2_ID, checkbox_2), ], tree: Some(Tree::new(WINDOW_ID)), - focus: self.accesskit_focus(), + focus: self.focus, } } @@ -188,16 +179,6 @@ impl WinHandler for HelloState { self.size = size; } - fn got_focus(&mut self) { - self.is_window_focused = true; - self.update_accesskit_focus(); - } - - fn lost_focus(&mut self) { - self.is_window_focused = false; - self.update_accesskit_focus(); - } - fn accesskit_action(&mut self, request: ActionRequest) { if let ActionRequest { action, diff --git a/src/backend/mac/window.rs b/src/backend/mac/window.rs index e8ddd8b0..1d207d78 100644 --- a/src/backend/mac/window.rs +++ b/src/backend/mac/window.rs @@ -194,6 +194,8 @@ struct ViewState { parent: Option, #[cfg(feature = "accesskit")] accesskit_adapter: OnceCell, + #[cfg(feature = "accesskit")] + is_focused: bool, } #[derive(Clone, PartialEq, Eq)] @@ -650,6 +652,8 @@ fn make_view(handler: Box) -> (id, Weak>>) { parent: None, #[cfg(feature = "accesskit")] accesskit_adapter: OnceCell::new(), + #[cfg(feature = "accesskit")] + is_focused: false, }; let state_ptr = Box::into_raw(Box::new(state)); (*view).set_ivar("viewState", state_ptr as *mut c_void); @@ -1105,6 +1109,14 @@ extern "C" fn window_did_become_key(this: &mut Object, _: Sel, _notification: id let view_state: *mut c_void = *this.get_ivar("viewState"); let view_state = &mut *(view_state as *mut ViewState); view_state.handler.got_focus(); + #[cfg(feature = "accesskit")] + { + view_state.is_focused = true; + if let Some(adapter) = view_state.accesskit_adapter.get() { + let events = adapter.update_view_focus_state(true); + events.raise(); + } + } } } @@ -1113,6 +1125,14 @@ extern "C" fn window_did_resign_key(this: &mut Object, _: Sel, _notification: id let view_state: *mut c_void = *this.get_ivar("viewState"); let view_state = &mut *(view_state as *mut ViewState); view_state.handler.lost_focus(); + #[cfg(feature = "accesskit")] + { + view_state.is_focused = false; + if let Some(adapter) = view_state.accesskit_adapter.get() { + let events = adapter.update_view_focus_state(false); + events.raise(); + } + } } } @@ -1596,7 +1616,7 @@ impl IdleHandle { #[cfg(feature = "accesskit")] impl accesskit::ActionHandler for AccessKitActionHandler { - fn do_action(&self, request: accesskit::ActionRequest) { + fn do_action(&mut self, request: accesskit::ActionRequest) { self.idle_handle.add_idle_callback(move |handler| { handler.accesskit_action(request); }); @@ -1616,6 +1636,7 @@ impl ViewState { } let view = view as *mut Object as *mut c_void; let initial_state = self.handler.accesskit_tree(); + let is_focused = self.is_focused; let idle_handle = IdleHandle { nsview: self.nsview.clone(), idle_queue: Arc::downgrade(&self.idle_queue), @@ -1623,7 +1644,8 @@ impl ViewState { let action_handler = Box::new(AccessKitActionHandler { idle_handle }); // SAFETY: The view pointer is based on a valid borrowed reference // to the view. - let adapter = unsafe { AccessKitAdapter::new(view, initial_state, action_handler) }; + let adapter = + unsafe { AccessKitAdapter::new(view, initial_state, is_focused, action_handler) }; match self.accesskit_adapter.try_insert(adapter) { Ok(adapter) => adapter, Err((old_adapter, _)) => { diff --git a/src/backend/windows/window.rs b/src/backend/windows/window.rs index 756e2193..c3d1aa28 100644 --- a/src/backend/windows/window.rs +++ b/src/backend/windows/window.rs @@ -245,6 +245,8 @@ struct WindowState { uia_init_marker: UiaInitMarker, // zero size #[cfg(feature = "accesskit")] accesskit_adapter: OnceCell, + #[cfg(feature = "accesskit")] + is_focused: Cell, } impl std::fmt::Debug for WindowState { @@ -737,10 +739,34 @@ impl WndProc for MyWndProc { WM_ERASEBKGND => Some(0), WM_SETFOCUS => { self.with_wnd_state(|s| s.handler.got_focus()); + #[cfg(feature = "accesskit")] + { + let handle = self.handle.borrow(); + if let Some(state) = handle.state.upgrade() { + state.is_focused.set(true); + if let Some(adapter) = state.accesskit_adapter.get() { + let events = adapter.update_window_focus_state(true); + drop(handle); + events.raise(); + } + } + } Some(0) } WM_KILLFOCUS => { self.with_wnd_state(|s| s.handler.lost_focus()); + #[cfg(feature = "accesskit")] + { + let handle = self.handle.borrow(); + if let Some(state) = handle.state.upgrade() { + state.is_focused.set(false); + if let Some(adapter) = state.accesskit_adapter.get() { + let events = adapter.update_window_focus_state(false); + drop(handle); + events.raise(); + } + } + } Some(0) } WM_PAINT => unsafe { @@ -1187,6 +1213,7 @@ impl WndProc for MyWndProc { let wparam = accesskit_windows::WPARAM(wparam); let lparam = accesskit_windows::LPARAM(lparam); let idle_queue = &state.idle_queue; + let is_focused = state.is_focused.get(); let uia_init_marker = state.uia_init_marker; // zero size and Copy state .accesskit_adapter @@ -1202,6 +1229,7 @@ impl WndProc for MyWndProc { AccessKitAdapter::new( hwnd, initial_tree_state, + is_focused, action_handler, uia_init_marker, ) @@ -1391,6 +1419,8 @@ impl WindowBuilder { uia_init_marker: UiaInitMarker::new(), #[cfg(feature = "accesskit")] accesskit_adapter: OnceCell::new(), + #[cfg(feature = "accesskit")] + is_focused: Cell::new(false), }; let win = Rc::new(window); let handle = WindowHandle { @@ -2087,7 +2117,7 @@ impl IdleHandle { #[cfg(feature = "accesskit")] impl accesskit::ActionHandler for AccessKitActionHandler { - fn do_action(&self, request: accesskit::ActionRequest) { + fn do_action(&mut self, request: accesskit::ActionRequest) { self.idle_handle.add_idle_callback(move |handler| { handler.accesskit_action(request); }); From fd52d2cf81df2432fe2fdcf5f5ebd310027d8ac2 Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Sat, 4 Nov 2023 21:06:16 -0500 Subject: [PATCH 2/2] Add boilerplate in the other examples for constructing a valid, but empty, TreeUpdate, since TreeUpdate::default is no longer an option --- examples/edit_text.rs | 11 ++++++++++- examples/pen.rs | 11 ++++++++++- examples/shello.rs | 11 ++++++++++- examples/wgpu-triangle/main.rs | 11 ++++++++++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/examples/edit_text.rs b/examples/edit_text.rs index 76b6810a..688fa35d 100644 --- a/examples/edit_text.rs +++ b/examples/edit_text.rs @@ -327,7 +327,16 @@ impl WinHandler for WindowState { #[cfg(feature = "accesskit")] fn accesskit_tree(&mut self) -> TreeUpdate { // TODO: Construct a real TreeUpdate - TreeUpdate::default() + use accesskit::{NodeBuilder, NodeClassSet, NodeId, Role, Tree}; + let builder = NodeBuilder::new(Role::Window); + let mut node_classes = NodeClassSet::new(); + let node = builder.build(&mut node_classes); + const WINDOW_ID: NodeId = NodeId(0); + TreeUpdate { + nodes: vec![(WINDOW_ID, node)], + tree: Some(Tree::new(WINDOW_ID)), + focus: WINDOW_ID, + } } fn size(&mut self, size: Size) { diff --git a/examples/pen.rs b/examples/pen.rs index ad6ff3b0..400cc714 100644 --- a/examples/pen.rs +++ b/examples/pen.rs @@ -144,7 +144,16 @@ impl WinHandler for WindowState { #[cfg(feature = "accesskit")] fn accesskit_tree(&mut self) -> TreeUpdate { // TODO: Construct a real TreeUpdate - TreeUpdate::default() + use accesskit::{NodeBuilder, NodeClassSet, NodeId, Role, Tree}; + let builder = NodeBuilder::new(Role::Window); + let mut node_classes = NodeClassSet::new(); + let node = builder.build(&mut node_classes); + const WINDOW_ID: NodeId = NodeId(0); + TreeUpdate { + nodes: vec![(WINDOW_ID, node)], + tree: Some(Tree::new(WINDOW_ID)), + focus: WINDOW_ID, + } } fn idle(&mut self, _: IdleToken) { diff --git a/examples/shello.rs b/examples/shello.rs index e1dc244b..a7f35da0 100644 --- a/examples/shello.rs +++ b/examples/shello.rs @@ -132,7 +132,16 @@ impl WinHandler for WindowState { #[cfg(feature = "accesskit")] fn accesskit_tree(&mut self) -> TreeUpdate { // TODO: Construct a real TreeUpdate - TreeUpdate::default() + use accesskit::{NodeBuilder, NodeClassSet, NodeId, Role, Tree}; + let builder = NodeBuilder::new(Role::Window); + let mut node_classes = NodeClassSet::new(); + let node = builder.build(&mut node_classes); + const WINDOW_ID: NodeId = NodeId(0); + TreeUpdate { + nodes: vec![(WINDOW_ID, node)], + tree: Some(Tree::new(WINDOW_ID)), + focus: WINDOW_ID, + } } fn idle(&mut self, _: IdleToken) {} diff --git a/examples/wgpu-triangle/main.rs b/examples/wgpu-triangle/main.rs index 89d07eb6..694dbaac 100644 --- a/examples/wgpu-triangle/main.rs +++ b/examples/wgpu-triangle/main.rs @@ -188,7 +188,16 @@ impl WinHandler for WindowState { #[cfg(feature = "accesskit")] fn accesskit_tree(&mut self) -> TreeUpdate { // TODO: Construct a real TreeUpdate - TreeUpdate::default() + use accesskit::{NodeBuilder, NodeClassSet, NodeId, Role, Tree}; + let builder = NodeBuilder::new(Role::Window); + let mut node_classes = NodeClassSet::new(); + let node = builder.build(&mut node_classes); + const WINDOW_ID: NodeId = NodeId(0); + TreeUpdate { + nodes: vec![(WINDOW_ID, node)], + tree: Some(Tree::new(WINDOW_ID)), + focus: WINDOW_ID, + } } fn idle(&mut self, _: IdleToken) {}