Skip to main content

bevy_ui/
auto_directional_navigation.rs

1//! An automatic directional navigation system, powered by the [`AutoDirectionalNavigation`]
2//! component.
3//!
4//! Unlike the navigation system provided by `bevy_input_focus`, the automatic directional
5//! navigation system does not require specifying navigation edges. Just simply add the
6//! [`AutoDirectionalNavigation`] component to your entities, and the system will automatically
7//! calculate the navigation edges between entities based on screen position.
8//!
9//! [`AutoDirectionalNavigator`] replaces the manual directional navigation system
10//! provided by the [`DirectionalNavigation`] system parameter from `bevy_input_focus`. The
11//! [`AutoDirectionalNavigator`] will first navigate using manual override edges defined in the
12//! [`DirectionalNavigationMap`](bevy_input_focus::directional_navigation::DirectionalNavigationMap).
13//! If no manual overrides are defined, automatic navigation will occur between entities based on
14//! screen position.
15//!
16//! If any resulting navigation behavior is undesired, [`AutoNavigationConfig`] can be tweaked or
17//! manual overrides can be specified using the
18//! [`DirectionalNavigationMap`](bevy_input_focus::directional_navigation::DirectionalNavigationMap).
19
20use crate::{ComputedNode, ComputedUiTargetCamera, UiGlobalTransform};
21use bevy_camera::visibility::InheritedVisibility;
22use bevy_ecs::{prelude::*, system::SystemParam};
23use bevy_math::{ops, CompassOctant, Vec2};
24
25use bevy_input_focus::{
26    directional_navigation::{
27        AutoNavigationConfig, DirectionalNavigation, DirectionalNavigationError, FocusableArea,
28    },
29    navigator::find_best_candidate,
30    FocusCause,
31};
32
33use bevy_reflect::{prelude::*, Reflect};
34
35/// Marker component to enable automatic directional navigation to and from the entity.
36///
37/// Simply add this component to your UI entities so that the navigation algorithm will
38/// consider this entity in its calculations:
39///
40/// ```rust
41/// # use bevy_ecs::prelude::*;
42/// # use bevy_ui::auto_directional_navigation::AutoDirectionalNavigation;
43/// fn spawn_auto_nav_button(mut commands: Commands) {
44///     commands.spawn((
45///         // ... Button, Node, etc. ...
46///         AutoDirectionalNavigation::default(), // That's it!
47///     ));
48/// }
49/// ```
50///
51/// # Multi-Layer UIs and Z-Index
52///
53/// **Important**: Automatic navigation is currently **z-index agnostic** and treats
54/// all entities with `AutoDirectionalNavigation` as a flat set, regardless of which UI layer
55/// or z-index they belong to. This means navigation may jump between different layers (e.g.,
56/// from a background menu to an overlay popup).
57///
58/// **Workarounds** for multi-layer UIs:
59///
60/// 1. **Per-layer manual edge generation**: Query entities by layer and call
61///    [`auto_generate_navigation_edges()`](bevy_input_focus::directional_navigation::auto_generate_navigation_edges)
62///    separately for each layer:
63///    ```rust,ignore
64///    for layer in &layers {
65///        let nodes: Vec<FocusableArea> = query_layer(layer).collect();
66///        auto_generate_navigation_edges(&mut nav_map, &nodes, &config);
67///    }
68///    ```
69///
70/// 2. **Manual cross-layer navigation**: Use
71///    [`DirectionalNavigationMap::add_edge()`](bevy_input_focus::directional_navigation::DirectionalNavigationMap::add_edge)
72///    to define explicit connections between layers (e.g., "Back" button to main menu).
73///
74/// 3. **Remove component when layer is hidden**: Dynamically add/remove
75///    [`AutoDirectionalNavigation`] based on which layers are currently active.
76///
77/// See issue [#21679](https://github.com/bevyengine/bevy/issues/21679) for planned
78/// improvements to layer-aware automatic navigation.
79///
80/// # Opting Out
81///
82/// To disable automatic navigation for specific entities:
83///
84/// - **Remove the component**: Simply don't add [`AutoDirectionalNavigation`] to entities
85///   that should only use manual navigation edges.
86/// - **Dynamically toggle**: Remove/insert the component at runtime to enable/disable
87///   automatic navigation as needed.
88///
89/// Manual edges defined via [`DirectionalNavigationMap`](bevy_input_focus::directional_navigation::DirectionalNavigationMap)
90/// will override any automatically calculated edges.
91///
92/// # Additional Requirements
93///
94/// Entities must also have:
95/// - [`ComputedNode`] - for size information
96/// - [`UiGlobalTransform`] - for position information
97///
98/// These are automatically added by `bevy_ui` when you spawn UI entities.
99///
100/// # Custom UI Systems
101///
102/// For custom UI frameworks, you can call
103/// [`auto_generate_navigation_edges`](bevy_input_focus::directional_navigation::auto_generate_navigation_edges)
104/// directly in your own system instead of using this component.
105#[derive(impl bevy_ecs::component::Component for AutoDirectionalNavigation where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::Table;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {}
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}Component, #[automatically_derived]
impl ::core::default::Default for AutoDirectionalNavigation {
    #[inline]
    fn default() -> AutoDirectionalNavigation {
        AutoDirectionalNavigation {
            respect_tab_order: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
impl ::core::fmt::Debug for AutoDirectionalNavigation {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "AutoDirectionalNavigation", "respect_tab_order",
            &&self.respect_tab_order)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for AutoDirectionalNavigation {
    #[inline]
    fn clone(&self) -> AutoDirectionalNavigation {
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for AutoDirectionalNavigation { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for AutoDirectionalNavigation {
    #[inline]
    fn eq(&self, other: &AutoDirectionalNavigation) -> bool {
        self.respect_tab_order == other.respect_tab_order
    }
}PartialEq, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for AutoDirectionalNavigation
            where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert::<bevy_reflect::ReflectFromPtr>(bevy_reflect::FromType::<Self>::from_type());
                registration.insert::<bevy_reflect::ReflectFromReflect>(bevy_reflect::FromType::<Self>::from_type());
                registration.register_type_data::<ReflectComponent, Self>();
                registration.register_type_data::<ReflectDefault, Self>();
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <bool as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for AutoDirectionalNavigation where  {
            #[inline]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<bool>("respect_tab_order")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for AutoDirectionalNavigation where  {
            fn type_path() -> &'static str {
                "bevy_ui::auto_directional_navigation::AutoDirectionalNavigation"
            }
            fn short_type_path() -> &'static str {
                "AutoDirectionalNavigation"
            }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("AutoDirectionalNavigation")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui::auto_directional_navigation".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui::auto_directional_navigation")
            }
        }
        impl bevy_reflect::Reflect for AutoDirectionalNavigation where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        #[allow(non_upper_case_globals)]
        const _: () =
            {
                static __INVENTORY: ::inventory::Node =
                    ::inventory::Node {
                        value: &{
                                bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<AutoDirectionalNavigation
                                        as
                                        bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
                            },
                        next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
                    };
                #[link_section = ".text.startup"]
                unsafe extern "C" fn __ctor() {
                    unsafe {
                        ::inventory::ErasedNode::submit(__INVENTORY.value,
                            &__INVENTORY)
                    }
                }
                #[used]
                #[link_section = ".init_array"]
                static __CTOR: unsafe extern "C" fn() = __ctor;
            };
        impl bevy_reflect::structs::Struct for AutoDirectionalNavigation where
             {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "respect_tab_order" =>
                        ::core::option::Option::Some(&self.respect_tab_order),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "respect_tab_order" =>
                        ::core::option::Option::Some(&mut self.respect_tab_order),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize =>
                        ::core::option::Option::Some(&self.respect_tab_order),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize =>
                        ::core::option::Option::Some(&mut self.respect_tab_order),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("respect_tab_order"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "respect_tab_order" => ::core::option::Option::Some(0usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 1usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                -> bevy_reflect::structs::DynamicStruct {
                let mut dynamic: bevy_reflect::structs::DynamicStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed("respect_tab_order",
                    bevy_reflect::PartialReflect::to_dynamic(&self.respect_tab_order));
                dynamic
            }
        }
        impl bevy_reflect::PartialReflect for AutoDirectionalNavigation where
            {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Struct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (name, value) in
                        bevy_reflect::structs::Struct::iter_fields(struct_value) {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::structs::Struct::field_mut(self, name) {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::Struct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Struct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Struct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Struct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Struct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                let value =
                    <dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
                if let ::core::option::Option::Some(value) = value {
                    ::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
                            value))
                } else { ::core::option::Option::Some(false) }
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::structs::struct_partial_cmp)(self, value)
            }
            fn debug(&self, f: &mut ::core::fmt::Formatter<'_>)
                -> ::core::fmt::Result {
                ::core::fmt::Debug::fmt(self, f)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl bevy_reflect::FromReflect for AutoDirectionalNavigation where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let mut __this =
                        <Self as ::core::default::Default>::default();
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <bool as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "respect_tab_order")?))() {
                        __this.respect_tab_order = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect)]
106#[reflect(Component, Default, Debug, PartialEq, Clone)]
107pub struct AutoDirectionalNavigation {
108    /// Whether to also consider `TabIndex` for navigation order hints.
109    /// Currently unused but reserved for future functionality.
110    pub respect_tab_order: bool,
111}
112
113/// A system parameter for combining manual and auto navigation between focusable entities in a directional way.
114/// This wraps the [`DirectionalNavigation`] system parameter provided by `bevy_input_focus` and
115/// augments it with auto directional navigation.
116/// To use, the [`DirectionalNavigationPlugin`](bevy_input_focus::directional_navigation::DirectionalNavigationPlugin)
117/// must be added to the app.
118#[derive(const _: () =
    {
        type __StructFieldsAlias<'w, 's> =
            (DirectionalNavigation<'w>, Res<'w, AutoNavigationConfig>,
            Query<'w, 's,
            (Entity, &'static ComputedUiTargetCamera, &'static ComputedNode,
            &'static UiGlobalTransform, &'static InheritedVisibility),
            With<AutoDirectionalNavigation>>,
            Query<'w, 's,
            (Entity, &'static ComputedUiTargetCamera, &'static ComputedNode,
            &'static UiGlobalTransform), With<AutoDirectionalNavigation>>);
        #[doc(hidden)]
        pub struct FetchState {
            state: <__StructFieldsAlias<'static, 'static> as
            bevy_ecs::system::SystemParam>::State,
        }
        unsafe impl bevy_ecs::system::SystemParam for
            AutoDirectionalNavigator<'_, '_> {
            type State = FetchState<>;
            type Item<'w, 's> = AutoDirectionalNavigator<'w, 's>;
            fn init_state(world: &mut bevy_ecs::world::World) -> Self::State {
                FetchState {
                    state: <__StructFieldsAlias<'_, '_> as
                            bevy_ecs::system::SystemParam>::init_state(world),
                }
            }
            fn init_access(state: &Self::State,
                system_meta: &mut bevy_ecs::system::SystemMeta,
                component_access_set: &mut bevy_ecs::query::FilteredAccessSet,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::init_access(&state.state,
                    system_meta, component_access_set, world);
            }
            fn apply(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::apply(&mut state.state,
                    system_meta, world);
            }
            fn queue(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: bevy_ecs::world::DeferredWorld) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::queue(&mut state.state,
                    system_meta, world);
            }
            #[inline]
            unsafe fn get_param<'w,
                's>(state: &'s mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world:
                    bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell<'w>,
                change_tick: bevy_ecs::change_detection::Tick)
                ->
                    ::core::result::Result<Self::Item<'w, 's>,
                    bevy_ecs::system::SystemParamValidationError> {
                let (fieldmanual_directional_navigation, fieldconfig,
                        fieldnavigable_entities_query,
                        fieldcamera_and_focusable_area_query) = &mut state.state;
                let fieldmanual_directional_navigation =
                    unsafe {
                                <DirectionalNavigation<'w> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldmanual_directional_navigation,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::manual_directional_navigation"))?;
                let fieldconfig =
                    unsafe {
                                <Res<'w, AutoNavigationConfig> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldconfig,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::config"))?;
                let fieldnavigable_entities_query =
                    unsafe {
                                <Query<'w, 's,
                                        (Entity, &'static ComputedUiTargetCamera,
                                        &'static ComputedNode, &'static UiGlobalTransform,
                                        &'static InheritedVisibility),
                                        With<AutoDirectionalNavigation>> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldnavigable_entities_query,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::navigable_entities_query"))?;
                let fieldcamera_and_focusable_area_query =
                    unsafe {
                                <Query<'w, 's,
                                        (Entity, &'static ComputedUiTargetCamera,
                                        &'static ComputedNode, &'static UiGlobalTransform),
                                        With<AutoDirectionalNavigation>> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldcamera_and_focusable_area_query,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::camera_and_focusable_area_query"))?;
                ::core::result::Result::Ok(AutoDirectionalNavigator {
                        manual_directional_navigation: fieldmanual_directional_navigation,
                        config: fieldconfig,
                        navigable_entities_query: fieldnavigable_entities_query,
                        camera_and_focusable_area_query: fieldcamera_and_focusable_area_query,
                    })
            }
        }
        unsafe impl<'w, 's> bevy_ecs::system::ReadOnlySystemParam for
            AutoDirectionalNavigator<'w, 's> where
            DirectionalNavigation<'w>: bevy_ecs::system::ReadOnlySystemParam,
            Res<'w,
            AutoNavigationConfig>: bevy_ecs::system::ReadOnlySystemParam,
            Query<'w, 's,
            (Entity, &'static ComputedUiTargetCamera, &'static ComputedNode,
            &'static UiGlobalTransform, &'static InheritedVisibility),
            With<AutoDirectionalNavigation>>: bevy_ecs::system::ReadOnlySystemParam,
            Query<'w, 's,
            (Entity, &'static ComputedUiTargetCamera, &'static ComputedNode,
            &'static UiGlobalTransform),
            With<AutoDirectionalNavigation>>: bevy_ecs::system::ReadOnlySystemParam
            {}
    };SystemParam, #[automatically_derived]
impl<'w, 's> ::core::fmt::Debug for AutoDirectionalNavigator<'w, 's> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f,
            "AutoDirectionalNavigator", "manual_directional_navigation",
            &self.manual_directional_navigation, "config", &self.config,
            "navigable_entities_query", &self.navigable_entities_query,
            "camera_and_focusable_area_query",
            &&self.camera_and_focusable_area_query)
    }
}Debug)]
119pub struct AutoDirectionalNavigator<'w, 's> {
120    /// A system parameter for the manual directional navigation system provided by `bevy_input_focus`
121    pub manual_directional_navigation: DirectionalNavigation<'w>,
122    /// Configuration for the automated portion of the navigation algorithm.
123    pub config: Res<'w, AutoNavigationConfig>,
124    /// The entities which can possibly be navigated to automatically.
125    navigable_entities_query: Query<
126        'w,
127        's,
128        (
129            Entity,
130            &'static ComputedUiTargetCamera,
131            &'static ComputedNode,
132            &'static UiGlobalTransform,
133            &'static InheritedVisibility,
134        ),
135        With<AutoDirectionalNavigation>,
136    >,
137    /// A query used to get the target camera and the [`FocusableArea`] for a given entity to be used in automatic navigation.
138    camera_and_focusable_area_query: Query<
139        'w,
140        's,
141        (
142            Entity,
143            &'static ComputedUiTargetCamera,
144            &'static ComputedNode,
145            &'static UiGlobalTransform,
146        ),
147        With<AutoDirectionalNavigation>,
148    >,
149}
150
151impl<'w, 's> AutoDirectionalNavigator<'w, 's> {
152    /// Returns the current input focus
153    pub fn input_focus(&mut self) -> Option<Entity> {
154        self.manual_directional_navigation.focus.get()
155    }
156
157    /// Tries to find the neighbor in a given direction from the given entity. Assumes the entity is valid.
158    ///
159    /// Returns a neighbor if successful.
160    /// Returns None if there is no neighbor in the requested direction.
161    pub fn navigate(
162        &mut self,
163        direction: CompassOctant,
164    ) -> Result<Entity, DirectionalNavigationError> {
165        if let Some(current_focus) = self.input_focus() {
166            // Respect manual edges first
167            match self.manual_directional_navigation.navigate(direction) {
168                Ok(new_focus) => {
169                    self.manual_directional_navigation
170                        .focus
171                        .set(new_focus, FocusCause::Navigated);
172                    Ok(new_focus)
173                }
174                Err(DirectionalNavigationError::NoNeighborInDirection { .. }) => {
175                    if let Some((target_camera, origin)) =
176                        self.entity_to_camera_and_focusable_area(current_focus)
177                        && let Some(new_focus) = find_best_candidate(
178                            &origin,
179                            direction,
180                            &self.get_navigable_nodes(target_camera),
181                            &self.config,
182                        )
183                    {
184                        self.manual_directional_navigation
185                            .focus
186                            .set(new_focus, FocusCause::Navigated);
187                        Ok(new_focus)
188                    } else {
189                        Err(DirectionalNavigationError::NoNeighborInDirection {
190                            current_focus,
191                            direction,
192                        })
193                    }
194                }
195                err => err,
196            }
197        } else {
198            Err(DirectionalNavigationError::NoFocus)
199        }
200    }
201
202    /// Returns a vec of [`FocusableArea`] representing nodes that are eligible to be automatically navigated to.
203    /// The camera of any navigable nodes will equal the desired `target_camera`.
204    fn get_navigable_nodes(&self, target_camera: Entity) -> Vec<FocusableArea> {
205        self.navigable_entities_query
206            .iter()
207            .filter_map(
208                |(entity, computed_target_camera, computed, transform, inherited_visibility)| {
209                    // Skip hidden or zero-size nodes
210                    if computed.is_empty() || !inherited_visibility.get() {
211                        return None;
212                    }
213                    // Accept nodes that have the same target camera as the desired target camera
214                    if let Some(tc) = computed_target_camera.get()
215                        && tc == target_camera
216                    {
217                        let (scale, rotation, translation) = transform.to_scale_angle_translation();
218                        let scaled_size = computed.size() * computed.inverse_scale_factor() * scale;
219                        let rotated_size = get_rotated_bounds(scaled_size, rotation);
220                        Some(FocusableArea {
221                            entity,
222                            position: translation * computed.inverse_scale_factor(),
223                            size: rotated_size,
224                        })
225                    } else {
226                        // The node either does not have a target camera or it is not the same as the desired one.
227                        None
228                    }
229                },
230            )
231            .collect()
232    }
233
234    /// Gets the target camera and the [`FocusableArea`] of the provided entity, if it exists.
235    ///
236    /// Returns None if there was a [`QueryEntityError`](bevy_ecs::query::QueryEntityError) or
237    /// if the entity does not have a target camera.
238    fn entity_to_camera_and_focusable_area(
239        &self,
240        entity: Entity,
241    ) -> Option<(Entity, FocusableArea)> {
242        self.camera_and_focusable_area_query.get(entity).map_or(
243            None,
244            |(entity, computed_target_camera, computed, transform)| {
245                if let Some(target_camera) = computed_target_camera.get() {
246                    let (scale, rotation, translation) = transform.to_scale_angle_translation();
247                    let scaled_size = computed.size() * computed.inverse_scale_factor() * scale;
248                    let rotated_size = get_rotated_bounds(scaled_size, rotation);
249                    Some((
250                        target_camera,
251                        FocusableArea {
252                            entity,
253                            position: translation * computed.inverse_scale_factor(),
254                            size: rotated_size,
255                        },
256                    ))
257                } else {
258                    None
259                }
260            },
261        )
262    }
263}
264
265/// Util used to get the resulting bounds of a UI entity after applying its rotation.
266///
267/// This is necessary to apply because navigation should only use the final screen position
268/// of an entity in automatic navigation calculations. These bounds are used as the entity's size in
269/// [`FocusableArea`].
270fn get_rotated_bounds(size: Vec2, rotation: f32) -> Vec2 {
271    if rotation == 0.0 {
272        return size;
273    }
274    let cos_r = ops::cos(rotation).abs();
275    let sin_r = ops::sin(rotation).abs();
276    Vec2::new(
277        size.x * cos_r + size.y * sin_r,
278        size.x * sin_r + size.y * cos_r,
279    )
280}