use std::{convert::TryInto, num::NonZeroU128, sync::Arc};
use accesskit::{ActionHandler, ActionRequest, Node, NodeId, Role, Tree, TreeUpdate};
use windows::{core::*, Win32::UI::Accessibility::*};
use super::*;
const WINDOW_TITLE: &str = "Simple test";
const WINDOW_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(1) });
const BUTTON_1_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(2) });
const BUTTON_2_ID: NodeId = NodeId(unsafe { NonZeroU128::new_unchecked(3) });
fn make_button(name: &str) -> Arc<Node> {
Arc::new(Node {
role: Role::Button,
name: Some(name.into()),
focusable: true,
..Default::default()
})
}
fn get_initial_state() -> TreeUpdate {
let root = Arc::new(Node {
role: Role::Window,
children: vec![BUTTON_1_ID, BUTTON_2_ID],
..Default::default()
});
let button_1 = make_button("Button 1");
let button_2 = make_button("Button 2");
TreeUpdate {
nodes: vec![
(WINDOW_ID, root),
(BUTTON_1_ID, button_1),
(BUTTON_2_ID, button_2),
],
tree: Some(Tree::new(WINDOW_ID)),
focus: None,
}
}
pub struct NullActionHandler;
impl ActionHandler for NullActionHandler {
fn do_action(&self, _request: ActionRequest) {}
}
fn scope<F>(f: F) -> Result<()>
where
F: FnOnce(&Scope) -> Result<()>,
{
super::scope(
WINDOW_TITLE,
get_initial_state(),
BUTTON_1_ID,
Box::new(NullActionHandler {}),
f,
)
}
#[test]
fn has_native_uia() -> Result<()> {
scope(|s| {
let has_native_uia: bool = unsafe { UiaHasServerSideProvider(s.window) }.into();
assert!(has_native_uia);
Ok(())
})
}
fn is_button_named(element: &IUIAutomationElement, expected_name: &str) -> bool {
let control_type = unsafe { element.CurrentControlType() }.unwrap();
let name = unsafe { element.CurrentName() }.unwrap();
let name: String = name.try_into().unwrap();
control_type == (UIA_ButtonControlTypeId.0 as i32) && name == expected_name
}
fn is_button_1(element: &IUIAutomationElement) -> bool {
is_button_named(element, "Button 1")
}
fn is_button_2(element: &IUIAutomationElement) -> bool {
is_button_named(element, "Button 2")
}
#[test]
fn navigation() -> Result<()> {
scope(|s| {
let root = unsafe { s.uia.ElementFromHandle(s.window) }?;
let walker = unsafe { s.uia.ControlViewWalker() }?;
let mut button_1_forward: Option<IUIAutomationElement> = None;
let mut wrapped_child = unsafe { walker.GetFirstChildElement(&root) };
while let Ok(child) = wrapped_child {
if is_button_1(&child) {
button_1_forward = Some(child);
break;
}
wrapped_child = unsafe { walker.GetNextSiblingElement(&child) };
}
let button_1_forward = button_1_forward.unwrap();
let mut button_2_forward: Option<IUIAutomationElement> = None;
let wrapped_child = unsafe { walker.GetNextSiblingElement(&button_1_forward) };
if let Ok(child) = wrapped_child {
if is_button_2(&child) {
button_2_forward = Some(child);
}
}
let _button_2_forward = button_2_forward.unwrap();
let mut button_2_backward: Option<IUIAutomationElement> = None;
let mut wrapped_child = unsafe { walker.GetLastChildElement(&root) };
while let Ok(child) = wrapped_child {
if is_button_2(&child) {
button_2_backward = Some(child);
break;
}
wrapped_child = unsafe { walker.GetPreviousSiblingElement(&child) };
}
let button_2_backward = button_2_backward.unwrap();
let mut button_1_backward: Option<IUIAutomationElement> = None;
let wrapped_child = unsafe { walker.GetPreviousSiblingElement(&button_2_backward) };
if let Ok(child) = wrapped_child {
if is_button_1(&child) {
button_1_backward = Some(child);
}
}
let button_1_backward = button_1_backward.unwrap();
let equal: bool =
unsafe { s.uia.CompareElements(&button_1_forward, &button_1_backward) }?.into();
assert!(equal);
let parent = unsafe { walker.GetParentElement(&button_1_forward) }?;
let equal: bool = unsafe { s.uia.CompareElements(&parent, &root) }?.into();
assert!(equal);
let desktop_root = unsafe { s.uia.GetRootElement() }?;
let parent = unsafe { walker.GetParentElement(&root) }?;
let equal: bool = unsafe { s.uia.CompareElements(&parent, &desktop_root) }?.into();
assert!(equal);
let wrapped_child = unsafe { walker.GetFirstChildElement(&button_1_forward) };
assert_eq!(Err(Error::OK), wrapped_child);
let wrapped_child = unsafe { walker.GetLastChildElement(&button_1_forward) };
assert_eq!(Err(Error::OK), wrapped_child);
Ok(())
})
}
#[test]
fn focus() -> Result<()> {
scope(|s| {
let (focus_event_handler, received_focus_event) = FocusEventHandler::new();
unsafe {
s.uia
.AddFocusChangedEventHandler(None, &focus_event_handler)
}?;
s.show_and_focus_window();
let focus_from_event = received_focus_event.wait(is_button_1);
let has_focus: bool = unsafe { focus_from_event.CurrentHasKeyboardFocus() }?.into();
assert!(has_focus);
let is_focusable: bool = unsafe { focus_from_event.CurrentIsKeyboardFocusable() }?.into();
assert!(is_focusable);
let focus_on_demand = unsafe { s.uia.GetFocusedElement() }?;
let equal: bool =
unsafe { s.uia.CompareElements(&focus_from_event, &focus_on_demand) }?.into();
assert!(equal);
Ok(())
})
}