use std::any::TypeId;
use bevy_ecs::entity::Entities;
use bevy_ecs::prelude::{Component, Entity, Resource};
use bevy_input_focus::{FocusCause, InputFocus};
#[derive(Debug)]
struct Taken {
owner: TypeId,
held: Option<Entity>,
}
#[derive(Resource, Default, Debug)]
pub struct Focus {
taken: Vec<Taken>,
}
impl Focus {
pub fn take<Marker: Component>(&mut self, focus: &InputFocus) -> usize {
if let Some(at) = self.frame_of::<Marker>() {
return at;
}
self.taken.push(Taken {
owner: TypeId::of::<Marker>(),
held: focus.get(),
});
self.taken.len() - 1
}
#[must_use]
pub fn is_clear(&self) -> bool {
self.taken.is_empty()
}
pub fn base(&self, focus: &InputFocus) -> Option<Entity> {
self.taken
.first()
.map_or_else(|| focus.get(), |frame| frame.held)
}
pub fn rebase(&mut self, holder: Option<Entity>, focus: &mut InputFocus) {
if let Some(lowest) = self.taken.first_mut() {
lowest.held = holder;
return;
}
match holder {
Some(entity) if focus.get() != Some(entity) => {
focus.set(entity, FocusCause::Navigated);
}
Some(_) => {}
None => focus.clear(),
}
}
fn frame_of<Marker: Component>(&self) -> Option<usize> {
let owner = TypeId::of::<Marker>();
self.taken.iter().position(|frame| frame.owner == owner)
}
pub fn forget<Marker: Component>(&mut self) -> Option<Entity> {
let at = self.frame_of::<Marker>()?;
let closed = self.taken.remove(at);
match self.taken.get_mut(at) {
Some(above) => {
above.held = closed.held;
None
}
None => closed.held,
}
}
pub fn restore<Marker: Component>(&mut self, focus: &mut InputFocus, entities: &Entities) {
if let Some(previous) = self.forget::<Marker>()
&& entities.contains(previous)
{
focus.set(previous, FocusCause::Navigated);
}
}
}
#[cfg(test)]
mod tests {
use bevy_ecs::prelude::World;
use super::*;
#[derive(Component)]
struct Under;
#[derive(Component)]
struct Above;
fn holding(entity: Entity) -> InputFocus {
InputFocus::from_entity(entity)
}
#[test]
fn overlays_give_the_keyboard_back_in_the_order_they_took_it() {
let mut world = World::new();
let [view, field] = [world.spawn_empty().id(), world.spawn_empty().id()];
let mut stack = Focus::default();
let mut focus = holding(view);
assert_eq!(stack.take::<Under>(&focus), 0);
focus.set(field, FocusCause::Navigated);
assert_eq!(stack.take::<Above>(&focus), 1);
assert_eq!(stack.take::<Above>(&focus), 1, "standing keeps its place");
stack.restore::<Above>(&mut focus, world.entities());
assert_eq!(focus.get(), Some(field));
stack.restore::<Under>(&mut focus, world.entities());
assert_eq!(focus.get(), Some(view));
assert_eq!(stack.take::<Under>(&focus), 0, "its frame is gone");
}
#[test]
fn an_overlay_closing_beneath_another_leaves_it_the_keyboard() {
let mut world = World::new();
let [view, field, button] = [(); 3].map(|()| world.spawn_empty().id());
let mut stack = Focus::default();
let mut focus = holding(view);
stack.take::<Under>(&focus);
focus.set(field, FocusCause::Navigated);
stack.take::<Above>(&focus);
focus.set(button, FocusCause::Navigated);
stack.restore::<Under>(&mut focus, world.entities());
assert_eq!(focus.get(), Some(button), "the one above still holds it");
stack.restore::<Above>(&mut focus, world.entities());
assert_eq!(
focus.get(),
Some(view),
"and is owed what the closed one was"
);
}
#[test]
fn a_holder_that_is_gone_is_not_given_the_keyboard() {
let mut world = World::new();
let [view, button] = [world.spawn_empty().id(), world.spawn_empty().id()];
let mut stack = Focus::default();
let mut focus = holding(view);
stack.take::<Under>(&focus);
focus.set(button, FocusCause::Navigated);
world.despawn(view);
stack.restore::<Under>(&mut focus, world.entities());
assert_eq!(focus.get(), Some(button));
}
}