use bevy::{
ecs::{
entity::Entity,
event::EventReader,
query::{ReadOnlyWorldQuery, WorldQuery},
system::Query,
},
math::Vec2,
};
use non_empty_vec::NonEmpty;
use crate::resolve::LockReason;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum NavRequest {
Move(Direction),
ScopeMove(ScopeDirection),
Action,
Cancel,
FocusOn(Entity),
Lock,
Unlock,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ScopeDirection {
Next,
Previous,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Direction {
South,
North,
East,
West,
}
impl Direction {
pub fn is_in(&self, reference: Vec2, other: Vec2) -> bool {
let coord = other - reference;
use Direction::*;
match self {
North => coord.y < coord.x && coord.y < -coord.x,
South => coord.y > coord.x && coord.y > -coord.x,
East => coord.y < coord.x && coord.y > -coord.x,
West => coord.y > coord.x && coord.y < -coord.x,
}
}
}
#[derive(Debug, Clone)]
pub enum NavEvent {
InitiallyFocused(Entity),
FocusChanged {
to: NonEmpty<Entity>,
from: NonEmpty<Entity>,
},
NoChanges {
from: NonEmpty<Entity>,
request: NavRequest,
},
Locked(LockReason),
Unlocked(LockReason),
}
impl NavEvent {
pub(crate) fn focus_changed(to: Entity, from: NonEmpty<Entity>) -> NavEvent {
NavEvent::FocusChanged {
from,
to: NonEmpty::new(to),
}
}
pub fn is_activated(&self, entity: Entity) -> bool {
matches!(self, NavEvent::NoChanges { from, request: NavRequest::Action } if *from.first() == entity)
}
}
pub trait NavEventReaderExt<'w, 's> {
fn nav_iter(&mut self) -> NavEventReader<'w, 's, '_>;
}
impl<'w, 's> NavEventReaderExt<'w, 's> for EventReader<'w, 's, NavEvent> {
fn nav_iter(&mut self) -> NavEventReader<'w, 's, '_> {
NavEventReader { event_reader: self }
}
}
pub struct NavEventReader<'w, 's, 'a> {
event_reader: &'a mut EventReader<'w, 's, NavEvent>,
}
impl<'w, 's, 'a> NavEventReader<'w, 's, 'a> {
pub fn with_request(&mut self, request: NavRequest) -> impl Iterator<Item = Entity> + '_ {
self.event_reader
.iter()
.filter_map(move |nav_event| match nav_event {
NavEvent::NoChanges {
from,
request: event_request,
} if *event_request == request => Some(*from.first()),
_ => None,
})
}
pub fn activated(&mut self) -> impl Iterator<Item = Entity> + '_ {
self.with_request(NavRequest::Action)
}
pub fn types(&mut self) -> impl Iterator<Item = (&NavEvent, Entity)> + '_ {
use NavEvent::{FocusChanged, InitiallyFocused, Locked, NoChanges, Unlocked};
self.event_reader.iter().filter_map(|event| {
let entity = match event {
NoChanges { from, .. } => Some(*from.first()),
InitiallyFocused(initial) => Some(*initial),
FocusChanged { from, .. } => Some(*from.first()),
Locked(LockReason::Focusable(from)) => Some(*from),
Unlocked(LockReason::Focusable(from)) => Some(*from),
_ => None,
};
entity.map(|e| (event, e))
})
}
pub fn activated_in_query<'b, 'c: 'b, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery>(
&'b mut self,
query: &'c Query<Q, F>,
) -> impl Iterator<Item = Q::Item<'c>> + 'b {
query.iter_many(self.activated())
}
pub fn activated_in_query_foreach_mut<Q: WorldQuery, F: ReadOnlyWorldQuery>(
&mut self,
query: &mut Query<Q, F>,
mut for_each: impl FnMut(Q::Item<'_>),
) {
for entity in self.activated() {
if let Ok(item) = query.get_mut(entity) {
for_each(item);
}
}
}
}