AccessKit / accesskit

UI accessibility infrastructure across platforms and programming languages
BSD 3-Clause "New" or "Revised" License
991 stars 48 forks source link

feat: Expose the `orientation` property #421

Closed DataTriny closed 1 month ago

DataTriny commented 1 month ago

These changes were tested on all platforms with the example below:

platforms/winit/examples/orientation.rs

```rust use accesskit::{ Action, DefaultActionVerb, Node, NodeBuilder, NodeId, Orientation, Rect, Role, Tree, TreeUpdate, }; use accesskit_winit::{Adapter, Event as AccessKitEvent, WindowEvent as AccessKitWindowEvent}; use std::error::Error; use winit::{ application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}, window::{Window, WindowId}, }; const WINDOW_TITLE: &str = "Orientation test"; const WINDOW_ID: NodeId = NodeId(0); const HORIZONTAL_SCROLLBAR_ID: NodeId = NodeId(1); const VERTICAL_SCROLLBAR_ID: NodeId = NodeId(2); const INITIAL_FOCUS: NodeId = WINDOW_ID; const HORIZONTAL_SCROLLBAR_RECT: Rect = Rect { x0: 0.0, y0: 380.0, x1: 380.0, y1: 400.0, }; const VERTICAL_SCROLLBAR_RECT: Rect = Rect { x0: 380.0, y0: 0.0, x1: 400.0, y1: 380.0, }; fn build_scrollbar(bounds: Rect, orientation: Orientation, value: f64) -> Node { let mut builder = NodeBuilder::new(Role::ScrollBar); builder.set_bounds(bounds); builder.set_orientation(orientation); builder.set_min_numeric_value(0.0); builder.set_max_numeric_value(100.0); builder.set_numeric_value(value); builder.add_action(Action::Focus); builder.set_default_action_verb(DefaultActionVerb::Click); builder.build() } struct UiState; impl UiState { fn new() -> Self { Self {} } fn build_root(&mut self) -> Node { let mut builder = NodeBuilder::new(Role::Window); builder.set_children(&[HORIZONTAL_SCROLLBAR_ID, VERTICAL_SCROLLBAR_ID]); builder.set_name(WINDOW_TITLE); builder.build() } fn build_initial_tree(&mut self) -> TreeUpdate { let root = self.build_root(); let hbar = build_scrollbar(HORIZONTAL_SCROLLBAR_RECT, Orientation::Horizontal, 0.0); let vbar = build_scrollbar(VERTICAL_SCROLLBAR_RECT, Orientation::Vertical, 50.0); let mut tree = Tree::new(WINDOW_ID); tree.app_name = Some("orientation_example".to_string()); TreeUpdate { nodes: vec![ (WINDOW_ID, root), (HORIZONTAL_SCROLLBAR_ID, hbar), (VERTICAL_SCROLLBAR_ID, vbar), ], tree: Some(tree), focus: INITIAL_FOCUS, } } } struct WindowState { window: Window, adapter: Adapter, ui: UiState, } impl WindowState { fn new(window: Window, adapter: Adapter, ui: UiState) -> Self { Self { window, adapter, ui, } } } struct Application { event_loop_proxy: EventLoopProxy, window: Option, } impl Application { fn new(event_loop_proxy: EventLoopProxy) -> Self { Self { event_loop_proxy, window: None, } } fn create_window(&mut self, event_loop: &ActiveEventLoop) -> Result<(), Box> { let window_attributes = Window::default_attributes() .with_title(WINDOW_TITLE) .with_visible(false); let window = event_loop.create_window(window_attributes)?; let adapter = Adapter::with_event_loop_proxy(&window, self.event_loop_proxy.clone()); window.set_visible(true); self.window = Some(WindowState::new(window, adapter, UiState::new())); Ok(()) } } impl ApplicationHandler for Application { fn window_event(&mut self, _: &ActiveEventLoop, _: WindowId, event: WindowEvent) { let window = match &mut self.window { Some(window) => window, None => return, }; let adapter = &mut window.adapter; adapter.process_event(&window.window, &event); match event { WindowEvent::CloseRequested => { self.window = None; } _ => (), } } fn user_event(&mut self, _: &ActiveEventLoop, user_event: AccessKitEvent) { let window = match &mut self.window { Some(window) => window, None => return, }; let adapter = &mut window.adapter; let state = &mut window.ui; match user_event.window_event { AccessKitWindowEvent::InitialTreeRequested => { adapter.update_if_active(|| state.build_initial_tree()); } _ => (), } } fn resumed(&mut self, event_loop: &ActiveEventLoop) { self.create_window(event_loop) .expect("failed to create initial window"); } fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { if self.window.is_none() { event_loop.exit(); } } } fn main() -> Result<(), Box> { let event_loop = EventLoop::with_user_event().build()?; let mut state = Application::new(event_loop.create_proxy()); event_loop.run_app(&mut state).map_err(Into::into) } ```