use crate::{ComputedNode, ComputedUiTargetCamera, UiGlobalTransform};
use bevy_camera::visibility::InheritedVisibility;
use bevy_ecs::{prelude::*, system::SystemParam};
use bevy_math::{ops, CompassOctant, Vec2};
use bevy_input_focus::{
directional_navigation::{
AutoNavigationConfig, DirectionalNavigation, DirectionalNavigationError, FocusableArea,
},
navigator::find_best_candidate,
};
use bevy_reflect::{prelude::*, Reflect};
#[derive(Component, Default, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct AutoDirectionalNavigation {
pub respect_tab_order: bool,
}
#[derive(SystemParam, Debug)]
pub struct AutoDirectionalNavigator<'w, 's> {
pub manual_directional_navigation: DirectionalNavigation<'w>,
pub config: Res<'w, AutoNavigationConfig>,
navigable_entities_query: Query<
'w,
's,
(
Entity,
&'static ComputedUiTargetCamera,
&'static ComputedNode,
&'static UiGlobalTransform,
&'static InheritedVisibility,
),
With<AutoDirectionalNavigation>,
>,
camera_and_focusable_area_query: Query<
'w,
's,
(
Entity,
&'static ComputedUiTargetCamera,
&'static ComputedNode,
&'static UiGlobalTransform,
),
With<AutoDirectionalNavigation>,
>,
}
impl<'w, 's> AutoDirectionalNavigator<'w, 's> {
pub fn input_focus(&mut self) -> Option<Entity> {
self.manual_directional_navigation.focus.0
}
pub fn navigate(
&mut self,
direction: CompassOctant,
) -> Result<Entity, DirectionalNavigationError> {
if let Some(current_focus) = self.input_focus() {
if let Ok(new_focus) = self.manual_directional_navigation.navigate(direction) {
self.manual_directional_navigation.focus.set(new_focus);
Ok(new_focus)
} else if let Some((target_camera, origin)) =
self.entity_to_camera_and_focusable_area(current_focus)
&& let Some(new_focus) = find_best_candidate(
&origin,
direction,
&self.get_navigable_nodes(target_camera),
&self.config,
)
{
self.manual_directional_navigation.focus.set(new_focus);
Ok(new_focus)
} else {
Err(DirectionalNavigationError::NoNeighborInDirection {
current_focus,
direction,
})
}
} else {
Err(DirectionalNavigationError::NoFocus)
}
}
fn get_navigable_nodes(&self, target_camera: Entity) -> Vec<FocusableArea> {
self.navigable_entities_query
.iter()
.filter_map(
|(entity, computed_target_camera, computed, transform, inherited_visibility)| {
if computed.is_empty() || !inherited_visibility.get() {
return None;
}
if let Some(tc) = computed_target_camera.get()
&& tc == target_camera
{
let (scale, rotation, translation) = transform.to_scale_angle_translation();
let scaled_size = computed.size() * computed.inverse_scale_factor() * scale;
let rotated_size = get_rotated_bounds(scaled_size, rotation);
Some(FocusableArea {
entity,
position: translation * computed.inverse_scale_factor(),
size: rotated_size,
})
} else {
None
}
},
)
.collect()
}
fn entity_to_camera_and_focusable_area(
&self,
entity: Entity,
) -> Option<(Entity, FocusableArea)> {
self.camera_and_focusable_area_query.get(entity).map_or(
None,
|(entity, computed_target_camera, computed, transform)| {
if let Some(target_camera) = computed_target_camera.get() {
let (scale, rotation, translation) = transform.to_scale_angle_translation();
let scaled_size = computed.size() * computed.inverse_scale_factor() * scale;
let rotated_size = get_rotated_bounds(scaled_size, rotation);
Some((
target_camera,
FocusableArea {
entity,
position: translation * computed.inverse_scale_factor(),
size: rotated_size,
},
))
} else {
None
}
},
)
}
}
fn get_rotated_bounds(size: Vec2, rotation: f32) -> Vec2 {
if rotation == 0.0 {
return size;
}
let cos_r = ops::cos(rotation).abs();
let sin_r = ops::sin(rotation).abs();
Vec2::new(
size.x * cos_r + size.y * sin_r,
size.x * sin_r + size.y * cos_r,
)
}