Skip to content

Commit

Permalink
Support the AccessKit focus and blur actions (#596)
Browse files Browse the repository at this point in the history
I believe this is necessary to activate and deactivate input focus with
TalkBack on Android. Assistive technologies on other platforms can also
request the focus action, though none of the AccessKit backends support
the blur action yet.

---------

Co-authored-by: Olivier FAURE <couteaubleu@gmail.com>
  • Loading branch information
mwcampbell and PoignardAzur committed Sep 19, 2024
1 parent 2521829 commit 12ddfa3
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 10 deletions.
6 changes: 6 additions & 0 deletions masonry/src/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ impl_context_method!(
self.widget_state.has_focus
}

/// Whether this specific widget is in the focus chain.
pub fn is_in_focus_chain(&self) -> bool {
self.widget_state.in_focus_chain
}

/// The disabled state of a widget.
///
/// Returns `true` if this widget or any of its ancestors is explicitly disabled.
Expand Down Expand Up @@ -715,6 +720,7 @@ impl LifeCycleCtx<'_> {
pub fn register_for_focus(&mut self) {
trace!("register_for_focus");
self.widget_state.focus_chain.push(self.widget_id());
self.widget_state.in_focus_chain = true;
}

/// Register this widget as accepting text input.
Expand Down
29 changes: 19 additions & 10 deletions masonry/src/passes/accessibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ fn build_accessibility_tree(
rebuild_all,
scale_factor,
};
set_common_properties(&mut ctx);
widget.item.accessibility(&mut ctx);

let id: NodeId = ctx.widget_state.id.into();
Expand Down Expand Up @@ -96,20 +97,28 @@ fn build_access_node(widget: &dyn Widget, state: &WidgetState, scale_factor: f64
.collect::<Vec<NodeId>>(),
);

if state.is_hot {
node.set_hovered();
node
}

fn set_common_properties(ctx: &mut AccessCtx) {
if ctx.is_hot() {
ctx.current_node().set_hovered();
}
if state.is_disabled {
node.set_disabled();
if ctx.is_disabled() {
ctx.current_node().set_disabled();
}
if state.is_stashed {
node.set_hidden();
if ctx.is_stashed() {
ctx.current_node().set_hidden();
}
if state.clip.is_some() {
node.set_clips_children();
if ctx.widget_state.clip.is_some() {
ctx.current_node().set_clips_children();
}
if ctx.is_in_focus_chain() && !ctx.is_disabled() {
ctx.current_node().add_action(accesskit::Action::Focus);
}
if ctx.is_focused() {
ctx.current_node().add_action(accesskit::Action::Blur);
}

node
}

fn to_accesskit_rect(r: Rect, scale_factor: f64) -> accesskit::Rect {
Expand Down
20 changes: 20 additions & 0 deletions masonry/src/passes/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ pub(crate) fn root_on_access_event(
event,
false,
|widget, ctx, event| {
// TODO - Split into "access_event_focus" pass or something similar.
if event.target == ctx.widget_id() {
match event.action {
accesskit::Action::Focus => {
if ctx.is_in_focus_chain() && !ctx.is_disabled() && !ctx.is_focused() {
ctx.request_focus();
ctx.set_handled();
return;
}
}
accesskit::Action::Blur => {
if ctx.is_focused() {
ctx.resign_focus();
ctx.set_handled();
return;
}
}
_ => {}
}
}
widget.on_access_event(ctx, event);
},
);
Expand Down
1 change: 1 addition & 0 deletions masonry/src/passes/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ fn update_focus_chain_for_widget(
state.item.has_focus = global_state.focused_widget == Some(id);
let had_focus = state.item.has_focus;

state.item.in_focus_chain = false;
state.item.focus_chain.clear();
{
let mut ctx = LifeCycleCtx {
Expand Down
4 changes: 4 additions & 0 deletions masonry/src/widget/widget_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ pub struct WidgetState {
/// Descendants of the focused widget are not in the focused path.
pub(crate) has_focus: bool,

/// Whether this specific widget is in the focus chain.
pub(crate) in_focus_chain: bool,

// TODO - document
pub(crate) is_stashed: bool,

Expand Down Expand Up @@ -179,6 +182,7 @@ impl WidgetState {
request_accessibility: true,
needs_accessibility: true,
has_focus: false,
in_focus_chain: false,
request_anim: true,
needs_anim: true,
needs_update_disabled: true,
Expand Down

0 comments on commit 12ddfa3

Please sign in to comment.