Skip to content

Commit

Permalink
Replace bevy_a11y::Focus with InputFocus (#16863)
Browse files Browse the repository at this point in the history
# 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`.
  • Loading branch information
alice-i-cecile authored Dec 18, 2024
1 parent 6ca1e75 commit fa6cabd
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 32 deletions.
1 change: 1 addition & 0 deletions crates/bevy_a11y/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
11 changes: 1 addition & 10 deletions crates/bevy_a11y/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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 {
Expand All @@ -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>();
}
}
18 changes: 15 additions & 3 deletions crates/bevy_input_focus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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))
Expand Down
1 change: 0 additions & 1 deletion crates/bevy_window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
17 changes: 4 additions & 13 deletions crates/bevy_window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
12 changes: 7 additions & 5 deletions crates/bevy_winit/src/accessibility.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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},
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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![];
Expand All @@ -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()),
}
}

Expand Down

0 comments on commit fa6cabd

Please sign in to comment.