Skip to content

Commit df26e01

Browse files
committed
feature: Expose most state aria_properties to UIA
1 parent 0fdcebb commit df26e01

File tree

2 files changed

+177
-4
lines changed

2 files changed

+177
-4
lines changed

consumer/src/node.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
// found in the LICENSE.chromium file.
1010

1111
use accesskit::{
12-
Action, Affine, Live, Node as NodeData, NodeId, Orientation, Point, Rect, Role, TextSelection,
13-
Toggled,
12+
Action, Affine, HasPopup, Invalid, Live, Node as NodeData, NodeId, Orientation, Point, Rect,
13+
Role, TextSelection, Toggled,
1414
};
1515
use alloc::{
1616
string::{String, ToString},
@@ -333,6 +333,14 @@ impl<'a> Node<'a> {
333333
self.data().role_description().is_some()
334334
}
335335

336+
pub fn is_live_atomic(&self) -> bool {
337+
self.data().is_live_atomic()
338+
}
339+
340+
pub fn is_busy(&self) -> bool {
341+
self.data().is_busy()
342+
}
343+
336344
pub fn braille_label(&self) -> Option<&str> {
337345
self.data().braille_label()
338346
}
@@ -349,14 +357,30 @@ impl<'a> Node<'a> {
349357
self.data().braille_role_description().is_some()
350358
}
351359

360+
pub fn has_popup(&self) -> Option<HasPopup> {
361+
self.data().has_popup()
362+
}
363+
352364
pub fn is_hidden(&self) -> bool {
353365
self.data().is_hidden()
354366
}
355367

368+
pub fn invalid(&self) -> Option<Invalid> {
369+
self.data().invalid()
370+
}
371+
372+
pub fn level(&self) -> Option<usize> {
373+
self.data().level()
374+
}
375+
356376
pub fn is_disabled(&self) -> bool {
357377
self.data().is_disabled()
358378
}
359379

380+
pub fn is_expanded(&self) -> Option<bool> {
381+
self.data().is_expanded()
382+
}
383+
360384
pub fn is_read_only(&self) -> bool {
361385
let data = self.data();
362386
if data.is_read_only() {

platforms/windows/src/node.rs

Lines changed: 151 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
#![allow(non_upper_case_globals)]
1212

1313
use accesskit::{
14-
Action, ActionData, ActionRequest, Live, NodeId, NodeIdContent, Orientation, Point, Role,
15-
Toggled,
14+
Action, ActionData, ActionRequest, HasPopup, Live, NodeId, NodeIdContent, Orientation, Point,
15+
Role, Toggled,
1616
};
1717
use accesskit_consumer::{FilterResult, Node, TreeState};
1818
use std::sync::{atomic::Ordering, Arc, Weak};
@@ -292,6 +292,11 @@ impl NodeWrapper<'_> {
292292
let mut result = WideString::default();
293293
let mut properties = AriaProperties::new(&mut result);
294294

295+
// TODO: Atomic, busy, and required flags should include false when explicitly set to that
296+
if self.0.is_live_atomic() {
297+
properties.write_property("atomic", "true").unwrap()
298+
}
299+
295300
if let Some(label) = self.0.braille_label() {
296301
properties.write_property("braillelabel", label).unwrap();
297302
}
@@ -302,6 +307,150 @@ impl NodeWrapper<'_> {
302307
.unwrap();
303308
}
304309

310+
if self.0.is_busy() {
311+
properties.write_property("busy", "true").unwrap()
312+
}
313+
314+
if let Some(toggled) = self.0.toggled() {
315+
let role = self.0.role();
316+
317+
fn val(t: Toggled) -> &'static str {
318+
match t {
319+
Toggled::False => "false",
320+
Toggled::True => "true",
321+
Toggled::Mixed => "mixed",
322+
}
323+
}
324+
325+
match role {
326+
Role::Button => {
327+
properties.write_property("pressed", val(toggled)).unwrap();
328+
}
329+
Role::Switch => {
330+
// Switches don't support the mixed state
331+
if let Toggled::True | Toggled::False = toggled {
332+
let s = val(toggled);
333+
// UIA treats switches as toggle buttons, but for maximum compatability
334+
// we set both pressed and checked states.
335+
properties.write_property("pressed", s).unwrap();
336+
properties.write_property("checked", s).unwrap();
337+
}
338+
}
339+
_ => {
340+
properties.write_property("checked", val(toggled)).unwrap();
341+
}
342+
}
343+
}
344+
345+
if self.0.is_disabled() {
346+
properties.write_property("disabled", "true").unwrap()
347+
}
348+
349+
if let Some(is_expanded) = self.0.is_expanded() {
350+
properties
351+
.write_property("expanded", if is_expanded { "true" } else { "false" })
352+
.unwrap()
353+
}
354+
355+
if let Some(has_popup) = self.0.has_popup() {
356+
fn val(h: HasPopup) -> &'static str {
357+
match h {
358+
HasPopup::Menu => "menu",
359+
HasPopup::Listbox => "listbox",
360+
HasPopup::Tree => "tree",
361+
HasPopup::Grid => "grid",
362+
HasPopup::Dialog => "dialog",
363+
}
364+
}
365+
366+
properties
367+
.write_property("haspopup", val(has_popup))
368+
.unwrap();
369+
}
370+
371+
if self.0.is_hidden() {
372+
properties.write_property("hidden", "true").unwrap()
373+
}
374+
375+
if self.0.invalid() {
376+
// We don't need to distinguish between actual values here.
377+
properties.write_property("invalid", "true").unwrap();
378+
}
379+
380+
if let Some(level) = self.0.level() {
381+
properties
382+
.write_property("level", &*level.to_string())
383+
.unwrap();
384+
}
385+
386+
match self.live_setting() {
387+
Live::Polite => {
388+
properties.write_property("live", "polite").unwrap();
389+
}
390+
Live::Assertive => {
391+
properties.write_property("live", "assertive").unwrap();
392+
}
393+
Live::Off => {}
394+
}
395+
396+
if self.0.is_multiline() {
397+
properties.write_property("multiline", "true").unwrap();
398+
}
399+
400+
if self.is_multiselectable() {
401+
properties
402+
.write_property("multiselectable", "true")
403+
.unwrap();
404+
}
405+
406+
if let Some(posinset) = self.0.position_in_set() {
407+
properties
408+
.write_property("posinset", &*posinset.to_string())
409+
.unwrap();
410+
}
411+
412+
if self.is_read_only() {
413+
properties.write_property("readonly", "true").unwrap();
414+
}
415+
416+
if self.is_required() {
417+
properties.write_property("required", "true").unwrap();
418+
}
419+
420+
if let Some(selected) = self.0.is_selected() {
421+
properties
422+
.write_property("selected", if selected { "true" } else { "false" })
423+
.unwrap()
424+
}
425+
426+
if let Some(setsize) = self.0.size_of_set() {
427+
properties
428+
.write_property("setsize", &*setsize.to_string())
429+
.unwrap();
430+
}
431+
432+
if let Some(valuemax) = self.0.max_numeric_value() {
433+
properties
434+
.write_property("valuemax", &*valuemax.to_string())
435+
.unwrap();
436+
}
437+
438+
if let Some(valuemin) = self.0.min_numeric_value() {
439+
properties
440+
.write_property("valuemin", &*valuemin.to_string())
441+
.unwrap();
442+
}
443+
444+
if let Some(valuenow) = self.0.numeric_value() {
445+
properties
446+
.write_property("valuenow", &*valuenow.to_string())
447+
.unwrap();
448+
449+
if let Some(valuetext) = self.0.value() {
450+
properties.write_property("valuetext", &*valuetext).unwrap();
451+
}
452+
}
453+
305454
if properties.has_properties() {
306455
Some(result)
307456
} else {

0 commit comments

Comments
 (0)