Replace bevy_a11y::Focus with InputFocus (#16863)

# Objective

Bevy now has first-class input focus handling! We should use this for
accessibility purpose via accesskit too.

## Solution

- Removed bevy_a11y::Focus.
- Replaced all usages of Focus with InputFocus
- Changed the dependency tree so bevy_a11y relies on bevy_input_focus
- Moved initialization of the focus (starts with the primary window)
from bevy_window to bevy_input_focus to avoid circular dependencies (and
it's cleaner)

## Testing

TODO

## Migration Guide

`bevy_a11y::Focus` has been replaced with `bevy_input_focus::Focus`.
This commit is contained in:
Alice Cecile 2024-12-17 16:16:19 -08:00 committed by GitHub
parent 6ca1e756dc
commit fa6cabd432
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 29 additions and 32 deletions

View File

@ -14,6 +14,7 @@ bevy_app = { path = "../bevy_app", version = "0.15.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" }
bevy_input_focus = { path = "../bevy_input_focus", version = "0.15.0-dev" }
accesskit = "0.17"

View File

@ -22,11 +22,10 @@ use accesskit::Node;
use bevy_app::Plugin;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
prelude::{Component, Entity, Event, ReflectResource},
prelude::{Component, Event},
schedule::SystemSet,
system::Resource,
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
/// Wrapper struct for [`accesskit::ActionRequest`]. Required to allow it to be used as an `Event`.
#[derive(Event, Deref, DerefMut)]
@ -97,11 +96,6 @@ impl From<Node> for AccessibilityNode {
}
}
/// Resource representing which entity has keyboard focus, if any.
#[derive(Resource, Default, Deref, DerefMut, Reflect)]
#[reflect(Resource, Default)]
pub struct Focus(pub Option<Entity>);
/// Set enum for the systems relating to accessibility
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub enum AccessibilitySystem {
@ -115,11 +109,8 @@ pub struct AccessibilityPlugin;
impl Plugin for AccessibilityPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.register_type::<Focus>();
app.init_resource::<AccessibilityRequested>()
.init_resource::<ManageAccessibilityUpdates>()
.init_resource::<Focus>()
.allow_ambiguous_component::<AccessibilityNode>();
}
}

View File

@ -18,13 +18,13 @@
pub mod tab_navigation;
use bevy_app::{App, Plugin, PreUpdate};
use bevy_app::{App, Plugin, PreUpdate, Startup};
use bevy_ecs::{
component::Component,
entity::Entity,
event::{Event, EventReader},
query::{QueryData, With},
system::{Commands, Query, Res, Resource, SystemParam},
system::{Commands, Query, Res, ResMut, Resource, Single, SystemParam},
traversal::Traversal,
world::{Command, DeferredWorld, World},
};
@ -149,12 +149,21 @@ pub struct InputDispatchPlugin;
impl Plugin for InputDispatchPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(InputFocus(None))
app.add_systems(Startup, set_initial_focus)
.insert_resource(InputFocus(None))
.insert_resource(InputFocusVisible(false))
.add_systems(PreUpdate, dispatch_keyboard_input);
}
}
/// Sets the initial focus to the primary window, if any.
pub fn set_initial_focus(
mut input_focus: ResMut<InputFocus>,
window: Single<Entity, With<PrimaryWindow>>,
) {
input_focus.0 = Some(*window);
}
/// System which dispatches keyboard input events to the focused entity, or to the primary window
/// if no entity has focus.
fn dispatch_keyboard_input(
@ -377,6 +386,9 @@ mod tests {
};
app.world_mut().spawn((window, PrimaryWindow));
// Run the world for a single frame to set up the initial focus
app.update();
let entity_a = app
.world_mut()
.spawn((GatherKeyboardEvents::default(), SetFocusOnAdd))

View File

@ -13,7 +13,6 @@ serialize = ["serde", "smol_str/serde", "bevy_ecs/serialize"]
[dependencies]
# bevy
bevy_a11y = { path = "../bevy_a11y", version = "0.15.0-dev" }
bevy_app = { path = "../bevy_app", version = "0.15.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
bevy_input = { path = "../bevy_input", version = "0.15.0-dev" }

View File

@ -16,8 +16,6 @@ extern crate alloc;
use alloc::sync::Arc;
use std::sync::Mutex;
use bevy_a11y::Focus;
mod event;
mod monitor;
mod raw_handle;
@ -118,17 +116,10 @@ impl Plugin for WindowPlugin {
.add_event::<AppLifecycle>();
if let Some(primary_window) = &self.primary_window {
let initial_focus = app
.world_mut()
.spawn(primary_window.clone())
.insert((
PrimaryWindow,
RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
))
.id();
if let Some(mut focus) = app.world_mut().get_resource_mut::<Focus>() {
**focus = Some(initial_focus);
}
app.world_mut().spawn(primary_window.clone()).insert((
PrimaryWindow,
RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
));
}
match self.exit_condition {

View File

@ -29,6 +29,7 @@ bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" }
bevy_input = { path = "../bevy_input", version = "0.15.0-dev" }
bevy_input_focus = { path = "../bevy_input_focus", version = "0.15.0-dev" }
bevy_log = { path = "../bevy_log", version = "0.15.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.15.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" }

View File

@ -1,6 +1,7 @@
//! Helpers for mapping window entities to accessibility types
use alloc::{collections::VecDeque, sync::Arc};
use bevy_input_focus::InputFocus;
use std::sync::Mutex;
use accesskit::{
@ -10,13 +11,14 @@ use accesskit::{
use accesskit_winit::Adapter;
use bevy_a11y::{
AccessibilityNode, AccessibilityRequested, AccessibilitySystem,
ActionRequest as ActionRequestWrapper, Focus, ManageAccessibilityUpdates,
ActionRequest as ActionRequestWrapper, ManageAccessibilityUpdates,
};
use bevy_app::{App, Plugin, PostUpdate};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
change_detection::DetectChanges,
entity::EntityHashMap,
prelude::{DetectChanges, Entity, EventReader, EventWriter},
prelude::{Entity, EventReader, EventWriter},
query::With,
schedule::IntoSystemConfigs,
system::{NonSendMut, Query, Res, ResMut, Resource},
@ -179,7 +181,7 @@ fn should_update_accessibility_nodes(
fn update_accessibility_nodes(
mut adapters: NonSendMut<AccessKitAdapters>,
focus: Res<Focus>,
focus: Res<InputFocus>,
primary_window: Query<(Entity, &Window), With<PrimaryWindow>>,
nodes: Query<(
Entity,
@ -218,7 +220,7 @@ fn update_adapter(
node_entities: Query<Entity, With<AccessibilityNode>>,
primary_window: &Window,
primary_window_id: Entity,
focus: Res<Focus>,
focus: Res<InputFocus>,
) -> TreeUpdate {
let mut to_update = vec![];
let mut window_children = vec![];
@ -241,7 +243,7 @@ fn update_adapter(
TreeUpdate {
nodes: to_update,
tree: None,
focus: NodeId(focus.unwrap_or(primary_window_id).to_bits()),
focus: NodeId(focus.0.unwrap_or(primary_window_id).to_bits()),
}
}