Skip to main content

bevy_input_focus/
tab_navigation.rs

1//! This module provides a framework for handling linear tab-key navigation in Bevy applications.
2//!
3//! The rules of tabbing are derived from the HTML specification, and are as follows:
4//!
5//! * An index >= 0 means that the entity is tabbable via sequential navigation.
6//!   The order of tabbing is determined by the index, with lower indices being tabbed first.
7//!   If two entities have the same index, then the order is determined by the order of
8//!   the entities in the ECS hierarchy (as determined by Parent/Child).
9//! * An index < 0 means that the entity is not focusable via sequential navigation, but
10//!   can still be focused via direct selection.
11//!
12//! Tabbable entities must be descendants of a [`TabGroup`] entity, which is a component that
13//! marks a tree of entities as containing tabbable elements. The order of tab groups
14//! is determined by the [`TabGroup::order`] field, with lower orders being tabbed first. Modal tab groups
15//! are used for UI elements that should only tab within themselves, such as modal dialog boxes.
16//!
17//! To enable automatic tabbing, add the
18//! [`TabNavigationPlugin`] and [`InputDispatchPlugin`](crate::InputDispatchPlugin) to your app.
19//! This will install a keyboard event observer on the primary window which automatically handles
20//! tab navigation for you.
21//!
22//! Alternatively, if you want to have more control over tab navigation, or are using an input-action-mapping framework,
23//! you can use the [`TabNavigation`] system parameter directly instead.
24//! This object can be injected into your systems, and provides a [`navigate`](`TabNavigation::navigate`) method which can be
25//! used to navigate between focusable entities.
26
27use alloc::vec::Vec;
28use bevy_app::{App, Plugin, Startup};
29use bevy_camera::visibility::InheritedVisibility;
30use bevy_ecs::{
31    component::Component,
32    entity::Entity,
33    hierarchy::{ChildOf, Children},
34    observer::On,
35    query::{With, Without},
36    system::{Commands, Query, Res, ResMut, SystemParam},
37};
38use bevy_input::{
39    keyboard::{KeyCode, KeyboardInput},
40    ButtonInput, ButtonState,
41};
42use bevy_window::{PrimaryWindow, Window};
43use log::warn;
44use thiserror::Error;
45
46use crate::{AcquireFocus, FocusCause, FocusedInput, InputFocus, InputFocusVisible};
47
48#[cfg(feature = "bevy_reflect")]
49use {
50    bevy_ecs::prelude::ReflectComponent,
51    bevy_reflect::{prelude::*, Reflect},
52};
53
54/// A component which indicates that an entity wants to participate in tab navigation.
55///
56/// Note that you must also add the [`TabGroup`] component to the entity's ancestor in order
57/// for this component to have any effect.
58#[derive(#[automatically_derived]impl ::core::fmt::Debug for TabIndex {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "TabIndex",
            &&self.0)
    }
}Debug, #[automatically_derived]impl ::core::default::Default for TabIndex {
    #[inline]
    fn default() -> Self { TabIndex(::core::default::Default::default()) }
}Default, impl bevy_ecs::component::Component for TabIndex 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::marker::Copy for TabIndex { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for TabIndex {
}
#[automatically_derived]impl ::core::clone::Clone for TabIndex {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<i32>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::StructuralPartialEq for TabIndex
    {
}
#[automatically_derived]impl ::core::cmp::PartialEq for TabIndex {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for TabIndex {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<i32>;
    }
}Eq, #[automatically_derived]impl ::core::cmp::PartialOrd for TabIndex {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]impl ::core::cmp::Ord for TabIndex {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord)]
59#[cfg_attr(
60    feature = "bevy_reflect",
61    derive(const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for TabIndex where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration.register_type_data_with::<ReflectComponent, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <i32 as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for TabIndex where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::TupleStruct(bevy_reflect::tuple_struct::TupleStructInfo::new::<Self>(&[bevy_reflect::UnnamedField::new::<i32>(0usize)]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for TabIndex where  {
            fn type_path() -> &'static str {
                "bevy_input_focus::tab_navigation::TabIndex"
            }
            fn short_type_path() -> &'static str { "TabIndex" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("TabIndex")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_input_focus::tab_navigation".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_input_focus::tab_navigation")
            }
        }
        impl bevy_reflect::Reflect for TabIndex 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(<TabIndex
                                        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::tuple_struct::TupleStruct for TabIndex where  {
            fn field(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.0),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.0),
                    _ => ::core::option::Option::None,
                }
            }
            #[inline]
            fn field_len(&self) -> usize { 1usize }
            #[inline]
            fn iter_fields(&self)
                -> bevy_reflect::tuple_struct::TupleStructFieldIter {
                bevy_reflect::tuple_struct::TupleStructFieldIter::new(self)
            }
            fn to_dynamic_tuple_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::tuple_struct::DynamicTupleStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic:
                        bevy_reflect::tuple_struct::DynamicTupleStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed(bevy_reflect::PartialReflect::to_dynamic(&self.0)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for TabIndex 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::TupleStruct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (i, value) in
                        ::core::iter::Iterator::enumerate(bevy_reflect::tuple_struct::TupleStruct::iter_fields(struct_value))
                        {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::tuple_struct::TupleStruct::field_mut(self, i)
                            {
                            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::TupleStruct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::TupleStruct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::TupleStruct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::TupleStruct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::TupleStruct(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::tuple_struct::tuple_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 TabIndex where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::TupleStruct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let mut __this =
                        <Self as ::core::default::Default>::default();
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <i32 as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::tuple_struct::TupleStruct::field(__ref_struct,
                                                    0)?))() {
                        __this.0 = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect),
62    reflect(Debug, Default, Component, PartialEq, Clone)
63)]
64pub struct TabIndex(pub i32);
65
66/// A component used to mark a tree of entities as containing tabbable elements.
67#[derive(#[automatically_derived]impl ::core::fmt::Debug for TabGroup {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "TabGroup",
            "order", &self.order, "modal", &&self.modal)
    }
}Debug, #[automatically_derived]impl ::core::default::Default for TabGroup {
    #[inline]
    fn default() -> Self {
        TabGroup {
            order: ::core::default::Default::default(),
            modal: ::core::default::Default::default(),
        }
    }
}Default, impl bevy_ecs::component::Component for TabGroup 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::marker::Copy for TabGroup { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for TabGroup {
}
#[automatically_derived]impl ::core::clone::Clone for TabGroup {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<i32>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone)]
68#[cfg_attr(
69    feature = "bevy_reflect",
70    derive(const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for TabGroup where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration.register_type_data_with::<ReflectComponent, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <i32 as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <bool as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for TabGroup where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<i32>("order"),
                                                bevy_reflect::NamedField::new::<bool>("modal")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for TabGroup where  {
            fn type_path() -> &'static str {
                "bevy_input_focus::tab_navigation::TabGroup"
            }
            fn short_type_path() -> &'static str { "TabGroup" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("TabGroup")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_input_focus::tab_navigation".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_input_focus::tab_navigation")
            }
        }
        impl bevy_reflect::Reflect for TabGroup 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(<TabGroup
                                        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 TabGroup where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "order" => ::core::option::Option::Some(&self.order),
                    "modal" => ::core::option::Option::Some(&self.modal),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "order" => ::core::option::Option::Some(&mut self.order),
                    "modal" => ::core::option::Option::Some(&mut self.modal),
                    _ => ::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.order),
                    1usize => ::core::option::Option::Some(&self.modal),
                    _ => ::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.order),
                    1usize => ::core::option::Option::Some(&mut self.modal),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("order"),
                    1usize => ::core::option::Option::Some("modal"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "order" => ::core::option::Option::Some(0usize),
                    "modal" => ::core::option::Option::Some(1usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 2usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::structs::DynamicStruct,
                    bevy_reflect::ReflectCloneError> {
                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("order",
                    bevy_reflect::PartialReflect::to_dynamic(&self.order)?);
                dynamic.insert_boxed("modal",
                    bevy_reflect::PartialReflect::to_dynamic(&self.modal)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for TabGroup 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> {
                (bevy_reflect::structs::struct_partial_eq)(self, value)
            }
            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 TabGroup 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) =
                            (||
                                        <i32 as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "order")?))() {
                        __this.order = __field;
                    }
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <bool as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "modal")?))() {
                        __this.modal = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect),
71    reflect(Debug, Default, Component, Clone)
72)]
73pub struct TabGroup {
74    /// The order of the tab group relative to other tab groups.
75    pub order: i32,
76
77    /// Whether this is a 'modal' group. If true, then tabbing within the group (that is,
78    /// if the current focus entity is a child of this group) will cycle through the children
79    /// of this group. If false, then tabbing within the group will cycle through all non-modal
80    /// tab groups.
81    pub modal: bool,
82}
83
84impl TabGroup {
85    /// Create a new tab group with the given order.
86    pub fn new(order: i32) -> Self {
87        Self {
88            order,
89            modal: false,
90        }
91    }
92
93    /// Create a modal tab group.
94    pub fn modal() -> Self {
95        Self {
96            order: 0,
97            modal: true,
98        }
99    }
100}
101
102/// A navigation action that users might take to navigate your user interface in a cyclic fashion.
103///
104/// These values are consumed by the [`TabNavigation`] system param.
105#[derive(#[automatically_derived]impl ::core::default::Default for NavAction {
    #[inline]
    fn default() -> Self { Self::Next }
}Default, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for NavAction {
}
#[automatically_derived]impl ::core::clone::Clone for NavAction {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for NavAction { }Copy, #[automatically_derived]impl ::core::fmt::Debug for NavAction {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                NavAction::Next => "Next",
                NavAction::Previous => "Previous",
                NavAction::First => "First",
                NavAction::Last => "Last",
            })
    }
}Debug, #[automatically_derived]impl ::core::marker::StructuralPartialEq for NavAction
    {
}
#[automatically_derived]impl ::core::cmp::PartialEq for NavAction {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq)]
106#[cfg_attr(
107    feature = "bevy_reflect",
108    derive(const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for NavAction where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {}
        }
        impl bevy_reflect::Typed for NavAction where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Enum(bevy_reflect::enums::EnumInfo::new::<Self>(&[bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("Next")),
                                                bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("Previous")),
                                                bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("First")),
                                                bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("Last"))]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for NavAction where  {
            fn type_path() -> &'static str {
                "bevy_input_focus::tab_navigation::NavAction"
            }
            fn short_type_path() -> &'static str { "NavAction" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("NavAction")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_input_focus::tab_navigation".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_input_focus::tab_navigation")
            }
        }
        impl bevy_reflect::Reflect for NavAction 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(<NavAction
                                        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::enums::Enum for NavAction where  {
            fn field(&self, __name_param: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match self { _ => ::core::option::Option::None, }
            }
            fn field_at(&self, __index_param: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match self { _ => ::core::option::Option::None, }
            }
            fn field_mut(&mut self, __name_param: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match self { _ => ::core::option::Option::None, }
            }
            fn field_at_mut(&mut self, __index_param: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match self { _ => ::core::option::Option::None, }
            }
            fn index_of(&self, __name_param: &str)
                -> ::core::option::Option<usize> {
                match self { _ => ::core::option::Option::None, }
            }
            fn name_at(&self, __index_param: usize)
                -> ::core::option::Option<&str> {
                match self { _ => ::core::option::Option::None, }
            }
            fn iter_fields(&self) -> bevy_reflect::enums::VariantFieldIter {
                bevy_reflect::enums::VariantFieldIter::new(self)
            }
            #[inline]
            fn field_len(&self) -> usize {
                match self {
                    NavAction::Next { .. } => 0usize,
                    NavAction::Previous { .. } => 0usize,
                    NavAction::First { .. } => 0usize,
                    NavAction::Last { .. } => 0usize,
                    _ => 0,
                }
            }
            #[inline]
            fn variant_name(&self) -> &str {
                match self {
                    NavAction::Next { .. } => "Next",
                    NavAction::Previous { .. } => "Previous",
                    NavAction::First { .. } => "First",
                    NavAction::Last { .. } => "Last",
                    _ => ::core::panicking::unreachable_default_msg(),
                }
            }
            #[inline]
            fn variant_index(&self) -> usize {
                match self {
                    NavAction::Next { .. } => 0usize,
                    NavAction::Previous { .. } => 1usize,
                    NavAction::First { .. } => 2usize,
                    NavAction::Last { .. } => 3usize,
                    _ => ::core::panicking::unreachable_default_msg(),
                }
            }
            #[inline]
            fn variant_type(&self) -> bevy_reflect::enums::VariantType {
                match self {
                    NavAction::Next { .. } =>
                        bevy_reflect::enums::VariantType::Unit,
                    NavAction::Previous { .. } =>
                        bevy_reflect::enums::VariantType::Unit,
                    NavAction::First { .. } =>
                        bevy_reflect::enums::VariantType::Unit,
                    NavAction::Last { .. } =>
                        bevy_reflect::enums::VariantType::Unit,
                    _ => ::core::panicking::unreachable_default_msg(),
                }
            }
            fn to_dynamic_enum(&self)
                ->
                    ::core::result::Result<bevy_reflect::enums::DynamicEnum,
                    bevy_reflect::ReflectCloneError> {
                bevy_reflect::enums::DynamicEnum::try_from_ref::<Self>(self)
            }
        }
        impl bevy_reflect::PartialReflect for NavAction 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_param: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Enum(__value_param) =
                        bevy_reflect::PartialReflect::reflect_ref(__value_param) {
                    if bevy_reflect::enums::Enum::variant_name(self) ==
                            bevy_reflect::enums::Enum::variant_name(__value_param) {
                        match bevy_reflect::enums::Enum::variant_type(__value_param)
                            {
                            bevy_reflect::enums::VariantType::Struct => {
                                for field in
                                    bevy_reflect::enums::Enum::iter_fields(__value_param) {
                                    let name = field.name().unwrap();
                                    if let ::core::option::Option::Some(v) =
                                            bevy_reflect::enums::Enum::field_mut(self, name) {
                                        bevy_reflect::PartialReflect::try_apply(v, field.value())?;
                                    }
                                }
                            }
                            bevy_reflect::enums::VariantType::Tuple => {
                                for (index, field) in
                                    ::core::iter::Iterator::enumerate(bevy_reflect::enums::Enum::iter_fields(__value_param))
                                    {
                                    if let ::core::option::Option::Some(v) =
                                            bevy_reflect::enums::Enum::field_at_mut(self, index) {
                                        bevy_reflect::PartialReflect::try_apply(v, field.value())?;
                                    }
                                }
                            }
                            _ => {}
                        }
                    } else {
                        match bevy_reflect::enums::Enum::variant_name(__value_param)
                            {
                            "Next" => { *self = NavAction::Next {} }
                            "Previous" => { *self = NavAction::Previous {} }
                            "First" => { *self = NavAction::First {} }
                            "Last" => { *self = NavAction::Last {} }
                            name => {
                                return ::core::result::Result::Err(bevy_reflect::ApplyError::UnknownVariant {
                                            enum_name: ::core::convert::Into::into(bevy_reflect::DynamicTypePath::reflect_type_path(self)),
                                            variant_name: ::core::convert::Into::into(name),
                                        });
                            }
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(__value_param),
                                to_kind: bevy_reflect::ReflectKind::Enum,
                            });
                }
                ::core::result::Result::Ok(())
            }
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Enum
            }
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Enum(self)
            }
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Enum(self)
            }
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Enum(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_hash(&self) -> ::core::option::Option<u64> {
                (bevy_reflect::enums::enum_hash)(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::enums::enum_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 NavAction where  {
            fn from_reflect(__param0: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Enum(__param0) =
                        bevy_reflect::PartialReflect::reflect_ref(__param0) {
                    match bevy_reflect::enums::Enum::variant_name(__param0) {
                        "Next" => ::core::option::Option::Some(NavAction::Next {}),
                        "Previous" =>
                            ::core::option::Option::Some(NavAction::Previous {}),
                        "First" =>
                            ::core::option::Option::Some(NavAction::First {}),
                        "Last" => ::core::option::Option::Some(NavAction::Last {}),
                        name => ::core::option::Option::None,
                    }
                } else { ::core::option::Option::None }
            }
        }
    };Reflect),
109    reflect(Debug, Clone, PartialEq)
110)]
111pub enum NavAction {
112    /// Navigate to the next focusable entity, wrapping around to the beginning if at the end.
113    ///
114    /// This is commonly triggered by pressing the Tab key.
115    #[default]
116    Next,
117    /// Navigate to the previous focusable entity, wrapping around to the end if at the beginning.
118    ///
119    /// This is commonly triggered by pressing Shift+Tab.
120    Previous,
121    /// Navigate to the first focusable entity.
122    ///
123    /// This is commonly triggered by pressing Home.
124    First,
125    /// Navigate to the last focusable entity.
126    ///
127    /// This is commonly triggered by pressing End.
128    Last,
129}
130
131/// An error that can occur during [tab navigation](crate::tab_navigation).
132#[derive(#[automatically_derived]impl ::core::fmt::Debug for TabNavigationError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::NoTabGroups =>
                ::core::fmt::Formatter::write_str(f, "NoTabGroups"),
            Self::NoFocusableEntities =>
                ::core::fmt::Formatter::write_str(f, "NoFocusableEntities"),
            Self::FailedToNavigateToNextFocusableEntity =>
                ::core::fmt::Formatter::write_str(f,
                    "FailedToNavigateToNextFocusableEntity"),
            Self::NoTabGroupForCurrentFocus {
                previous_focus: __self_0, new_focus: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "NoTabGroupForCurrentFocus", "previous_focus", __self_0,
                    "new_focus", &__self_1),
        }
    }
}Debug, #[allow(unused_qualifications)]
#[automatically_derived]
impl ::thiserror::__private21::Error for TabNavigationError { }
#[allow(unused_qualifications)]
#[automatically_derived]
impl ::core::fmt::Display for TabNavigationError {
    fn fmt(&self, __formatter: &mut ::core::fmt::Formatter)
        -> ::core::fmt::Result {
        use ::thiserror::__private21::AsDisplay as _;

        #[allow(unused_variables, deprecated, clippy ::
        used_underscore_binding)]
        match self {
            TabNavigationError::NoTabGroups {} =>
                __formatter.write_str("No tab groups found"),
            TabNavigationError::NoFocusableEntities {} =>
                __formatter.write_str("No focusable entities found"),
            TabNavigationError::FailedToNavigateToNextFocusableEntity {} =>
                __formatter.write_str("Failed to navigate to next focusable entity"),
            TabNavigationError::NoTabGroupForCurrentFocus {
                previous_focus, new_focus } =>
                match (previous_focus.as_display(),) {
                    (__display_previous_focus,) =>
                        __formatter.write_fmt(format_args!("No tab group found for currently focused entity {0}. Users will not be able to navigate back to this entity.",
                                __display_previous_focus)),
                },
        }
    }
}Error, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    TabNavigationError {
}
#[automatically_derived]impl ::core::cmp::PartialEq for TabNavigationError {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::NoTabGroupForCurrentFocus {
                    previous_focus: __self_0, new_focus: __self_1 },
                    Self::NoTabGroupForCurrentFocus {
                    previous_focus: __arg1_0, new_focus: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for TabNavigationError {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Entity>;
    }
}Eq, #[automatically_derived]impl ::core::clone::Clone for TabNavigationError {
    #[inline]
    fn clone(&self) -> Self {
        match self {
            Self::NoTabGroups => TabNavigationError::NoTabGroups,
            Self::NoFocusableEntities =>
                TabNavigationError::NoFocusableEntities,
            Self::FailedToNavigateToNextFocusableEntity =>
                TabNavigationError::FailedToNavigateToNextFocusableEntity,
            Self::NoTabGroupForCurrentFocus {
                previous_focus: __self_0, new_focus: __self_1 } =>
                TabNavigationError::NoTabGroupForCurrentFocus {
                    previous_focus: ::core::clone::Clone::clone(__self_0),
                    new_focus: ::core::clone::Clone::clone(__self_1),
                },
        }
    }
}Clone)]
133pub enum TabNavigationError {
134    /// No tab groups were found.
135    #[error("No tab groups found")]
136    NoTabGroups,
137    /// No focusable entities were found.
138    #[error("No focusable entities found")]
139    NoFocusableEntities,
140    /// Could not navigate to the next focusable entity.
141    ///
142    /// This can occur if your tab groups are malformed.
143    #[error("Failed to navigate to next focusable entity")]
144    FailedToNavigateToNextFocusableEntity,
145    /// No tab group for the current focus entity was found.
146    #[error("No tab group found for currently focused entity {previous_focus}. Users will not be able to navigate back to this entity.")]
147    NoTabGroupForCurrentFocus {
148        /// The entity that was previously focused,
149        /// and is missing its tab group.
150        previous_focus: Entity,
151        /// The new entity that will be focused.
152        ///
153        /// If you want to recover from this error, set [`InputFocus`] to this entity.
154        new_focus: Entity,
155    },
156}
157
158/// An injectable helper object that provides tab navigation functionality.
159#[doc(hidden)]
160#[derive(const _: () =
    {
        type __StructFieldsAlias<'w, 's> =
            (Query<'w, 's, (Entity, &'static TabGroup, &'static Children)>,
            Query<'w, 's,
            (Entity, Option<&'static TabIndex>, Option<&'static Children>,
            Option<&'static InheritedVisibility>), Without<TabGroup>>,
            Query<'w, 's, &'static ChildOf>);
        #[doc(hidden)]
        pub struct FetchState {
            state: <__StructFieldsAlias<'static, 'static> as
            bevy_ecs::system::SystemParam>::State,
        }
        unsafe impl bevy_ecs::system::SystemParam for TabNavigation<'_, '_> {
            type State = FetchState<>;
            type Item<'w, 's> = TabNavigation<'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,
                system_access: &mut bevy_ecs::system::SystemAccess,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::init_access(&state.state,
                    system_meta, system_access, 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 (fieldtabgroup_query, fieldtabindex_query,
                        fieldparent_query) = &mut state.state;
                let fieldtabgroup_query =
                    unsafe {
                                <Query<'w, 's,
                                        (Entity, &'static TabGroup, &'static Children)> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldtabgroup_query,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::tabgroup_query"))?;
                let fieldtabindex_query =
                    unsafe {
                                <Query<'w, 's,
                                        (Entity, Option<&'static TabIndex>,
                                        Option<&'static Children>,
                                        Option<&'static InheritedVisibility>), Without<TabGroup>> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldtabindex_query,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::tabindex_query"))?;
                let fieldparent_query =
                    unsafe {
                                <Query<'w, 's, &'static ChildOf> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldparent_query,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::parent_query"))?;
                ::core::result::Result::Ok(TabNavigation {
                        tabgroup_query: fieldtabgroup_query,
                        tabindex_query: fieldtabindex_query,
                        parent_query: fieldparent_query,
                    })
            }
        }
        unsafe impl<'w, 's> bevy_ecs::system::ReadOnlySystemParam for
            TabNavigation<'w, 's> where
            Query<'w, 's,
            (Entity, &'static TabGroup,
            &'static Children)>: bevy_ecs::system::ReadOnlySystemParam,
            Query<'w, 's,
            (Entity, Option<&'static TabIndex>, Option<&'static Children>,
            Option<&'static InheritedVisibility>),
            Without<TabGroup>>: bevy_ecs::system::ReadOnlySystemParam,
            Query<'w, 's,
            &'static ChildOf>: bevy_ecs::system::ReadOnlySystemParam {}
    };SystemParam)]
161pub struct TabNavigation<'w, 's> {
162    // Query for tab groups.
163    tabgroup_query: Query<'w, 's, (Entity, &'static TabGroup, &'static Children)>,
164    // Query for tab indices.
165    tabindex_query: Query<
166        'w,
167        's,
168        (
169            Entity,
170            Option<&'static TabIndex>,
171            Option<&'static Children>,
172            Option<&'static InheritedVisibility>,
173        ),
174        Without<TabGroup>,
175    >,
176    // Query for parents.
177    parent_query: Query<'w, 's, &'static ChildOf>,
178}
179
180impl TabNavigation<'_, '_> {
181    /// Navigate to the desired focusable entity, relative to the current focused entity.
182    ///
183    /// Change the [`NavAction`] to navigate in a different direction.
184    /// Focusable entities are determined by the presence of the [`TabIndex`] component.
185    ///
186    /// If there is no currently focused entity, then this function will return either the first
187    /// or last focusable entity, depending on the direction of navigation. For example, if
188    /// `action` is `Next` and no focusable entities are found, then this function will return
189    /// the first focusable entity.
190    pub fn navigate(
191        &self,
192        focus: &InputFocus,
193        action: NavAction,
194    ) -> Result<Entity, TabNavigationError> {
195        // If there are no tab groups, then there are no focusable entities.
196        if self.tabgroup_query.is_empty() {
197            return Err(TabNavigationError::NoTabGroups);
198        }
199
200        // Start by identifying which tab group we are in. Mainly what we want to know is if
201        // we're in a modal group.
202        let tabgroup = focus.get().and_then(|focus_ent| {
203            self.parent_query
204                .iter_ancestors(focus_ent)
205                .find_map(|entity| {
206                    self.tabgroup_query
207                        .get(entity)
208                        .ok()
209                        .map(|(_, tg, _)| (entity, tg))
210                })
211        });
212
213        self.navigate_internal(focus.get(), action, tabgroup)
214    }
215
216    /// Initialize focus to a focusable child of a container, either the first or last
217    /// depending on [`NavAction`]. This assumes that the parent entity has a [`TabGroup`]
218    /// component.
219    ///
220    /// Focusable entities are determined by the presence of the [`TabIndex`] component.
221    pub fn initialize(
222        &self,
223        parent: Entity,
224        action: NavAction,
225    ) -> Result<Entity, TabNavigationError> {
226        // If there are no tab groups, then there are no focusable entities.
227        if self.tabgroup_query.is_empty() {
228            return Err(TabNavigationError::NoTabGroups);
229        }
230
231        // Look for the tab group on the parent entity.
232        match self.tabgroup_query.get(parent) {
233            Ok(tabgroup) => self.navigate_internal(None, action, Some((parent, tabgroup.1))),
234            Err(_) => Err(TabNavigationError::NoTabGroups),
235        }
236    }
237
238    pub fn navigate_internal(
239        &self,
240        focus: Option<Entity>,
241        action: NavAction,
242        tabgroup: Option<(Entity, &TabGroup)>,
243    ) -> Result<Entity, TabNavigationError> {
244        let navigation_result = self.navigate_in_group(tabgroup, focus, action);
245
246        match navigation_result {
247            Ok(entity) => {
248                if let Some(previous_focus) = focus
249                    && tabgroup.is_none()
250                {
251                    Err(TabNavigationError::NoTabGroupForCurrentFocus {
252                        previous_focus,
253                        new_focus: entity,
254                    })
255                } else {
256                    Ok(entity)
257                }
258            }
259            Err(e) => Err(e),
260        }
261    }
262
263    fn navigate_in_group(
264        &self,
265        tabgroup: Option<(Entity, &TabGroup)>,
266        focus: Option<Entity>,
267        action: NavAction,
268    ) -> Result<Entity, TabNavigationError> {
269        // List of all focusable entities found.
270        let mut focusable: Vec<(Entity, TabIndex, usize)> =
271            Vec::with_capacity(self.tabindex_query.iter().len());
272
273        match tabgroup {
274            Some((tg_entity, tg)) if tg.modal => {
275                // We're in a modal tab group, then gather all tab indices in that group.
276                if let Ok((_, _, children)) = self.tabgroup_query.get(tg_entity) {
277                    for child in children.iter() {
278                        self.gather_focusable(&mut focusable, *child, 0);
279                    }
280                }
281            }
282            _ => {
283                // Otherwise, gather all tab indices in all non-modal tab groups.
284                let mut tab_groups: Vec<(Entity, TabGroup)> = self
285                    .tabgroup_query
286                    .iter()
287                    .filter(|(_, tg, _)| !tg.modal)
288                    .map(|(e, tg, _)| (e, *tg))
289                    .collect();
290                // Stable sort by group order
291                tab_groups.sort_by_key(|(_, tg)| tg.order);
292
293                // Search group descendants
294                tab_groups
295                    .iter()
296                    .enumerate()
297                    .for_each(|(idx, (tg_entity, _))| {
298                        self.gather_focusable(&mut focusable, *tg_entity, idx);
299                    });
300            }
301        }
302
303        if focusable.is_empty() {
304            return Err(TabNavigationError::NoFocusableEntities);
305        }
306
307        // Sort by TabGroup and then TabIndex
308        focusable.sort_by(|(_, a_tab_idx, a_group), (_, b_tab_idx, b_group)| {
309            if a_group == b_group {
310                a_tab_idx.cmp(b_tab_idx)
311            } else {
312                a_group.cmp(b_group)
313            }
314        });
315
316        let index = focusable.iter().position(|e| Some(e.0) == focus);
317        let count = focusable.len();
318        let next = match (index, action) {
319            (Some(idx), NavAction::Next) => (idx + 1).rem_euclid(count),
320            (Some(idx), NavAction::Previous) => (idx + count - 1).rem_euclid(count),
321            (None, NavAction::Next) | (_, NavAction::First) => 0,
322            (None, NavAction::Previous) | (_, NavAction::Last) => count - 1,
323        };
324        match focusable.get(next) {
325            Some((entity, _, _)) => Ok(*entity),
326            None => Err(TabNavigationError::FailedToNavigateToNextFocusableEntity),
327        }
328    }
329
330    /// Gather all focusable entities in tree order.
331    fn gather_focusable(
332        &self,
333        out: &mut Vec<(Entity, TabIndex, usize)>,
334        parent: Entity,
335        tab_group_idx: usize,
336    ) {
337        if let Ok((entity, tabindex, children, inherited_visibility)) =
338            self.tabindex_query.get(parent)
339        {
340            // Skip hidden entities and their entire subtree. An entity without an
341            // `InheritedVisibility` component (e.g. a non-UI entity) is treated as visible.
342            if inherited_visibility.is_none_or(|v| v.get()) {
343                if let Some(tabindex) = tabindex
344                    && tabindex.0 >= 0
345                {
346                    out.push((entity, *tabindex, tab_group_idx));
347                }
348                if let Some(children) = children {
349                    for child in children.iter() {
350                        // Don't traverse into tab groups, as they are handled separately.
351                        if self.tabgroup_query.get(*child).is_err() {
352                            self.gather_focusable(out, *child, tab_group_idx);
353                        }
354                    }
355                }
356            }
357        } else if let Ok((_, tabgroup, children)) = self.tabgroup_query.get(parent)
358            && !tabgroup.modal
359        {
360            for child in children.iter() {
361                self.gather_focusable(out, *child, tab_group_idx);
362            }
363        }
364    }
365}
366
367/// Observer which focuses the target of an [`AcquireFocus`] request when it has a [`TabIndex`].
368///
369/// This is the tab-navigation-specific half of focus acquisition: only entities that opt into tab
370/// navigation via [`TabIndex`] are treated as focus targets here. When the target is focusable, this
371/// stops the request (so it never reaches the window) and focuses it; otherwise the request keeps
372/// bubbling and is eventually handled by the generalized
373/// [`on_window_acquire_focus_clear`](crate::on_window_acquire_focus_clear) observer at the window.
374///
375/// The [`Without<Window>`] bound is a defensive guard — a window would never carry a [`TabIndex`] in
376/// practice, but excluding it keeps this observer's responsibility (focus a focusable child) cleanly
377/// separate from the window-clearing fallback in [`on_window_acquire_focus_clear`](crate::on_window_acquire_focus_clear).
378///
379/// The `focus.get()` guard avoids spurious mutations so change detection only fires on real changes.
380///
381#[cfg_attr(
382    feature = "bevy_picking",
383    doc = "This observer is also registered by
384[`PointerFocusPlugin`](crate::pointer_focus::PointerFocusPlugin) as a temporary bridge so pointer
385clicks can acquire focus without [`TabNavigationPlugin`]. Because `add_observer` does not
386deduplicate, it may therefore run twice per request when both plugins are present; keep it
387idempotent (stop propagation, only mutate focus on a real change) so the second run is a no-op."
388)]
389pub fn acquire_focus_tab_index(
390    mut acquire_focus: On<AcquireFocus>,
391    focusable: Query<(), (With<TabIndex>, Without<Window>)>,
392    mut focus: ResMut<InputFocus>,
393) {
394    // If the entity has a TabIndex
395    if focusable.contains(acquire_focus.focused_entity) {
396        // Stop and focus it
397        acquire_focus.propagate(false);
398        // Don't mutate unless we need to, for change detection
399        if focus.get() != Some(acquire_focus.focused_entity) {
400            focus.set(acquire_focus.focused_entity, FocusCause::Navigated);
401        }
402    }
403}
404
405/// Plugin for navigating between focusable entities using keyboard input.
406pub struct TabNavigationPlugin;
407
408impl Plugin for TabNavigationPlugin {
409    fn build(&self, app: &mut App) {
410        app.add_systems(Startup, setup_tab_navigation);
411        app.add_observer(acquire_focus_tab_index);
412    }
413}
414
415fn setup_tab_navigation(mut commands: Commands, window: Query<Entity, With<PrimaryWindow>>) {
416    for window in window.iter() {
417        commands.entity(window).observe(handle_tab_navigation);
418    }
419}
420
421/// Observer function which handles tab navigation.
422///
423/// This observer responds to [`KeyCode::Tab`] events and Shift+Tab events,
424/// cycling through focusable entities in the order determined by their tab index.
425///
426/// Any [`TabNavigationError`]s that occur during tab navigation are logged as warnings.
427pub fn handle_tab_navigation(
428    mut event: On<FocusedInput<KeyboardInput>>,
429    nav: TabNavigation,
430    mut focus: ResMut<InputFocus>,
431    mut visible: ResMut<InputFocusVisible>,
432    keys: Res<ButtonInput<KeyCode>>,
433) {
434    // Tab navigation.
435    let key_event = &event.input;
436    if key_event.key_code == KeyCode::Tab
437        && key_event.state == ButtonState::Pressed
438        && !key_event.repeat
439    {
440        let maybe_next = nav.navigate(
441            &focus,
442            if keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight) {
443                NavAction::Previous
444            } else {
445                NavAction::Next
446            },
447        );
448
449        match maybe_next {
450            Ok(next) => {
451                event.propagate(false);
452                focus.set(next, FocusCause::Navigated);
453                visible.0 = true;
454            }
455            Err(e) => {
456                {
    {
        let lvl = ::log::Level::Warn;
        if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {
            ::log::__private_api::log({ ::log::__private_api::GlobalLogger },
                format_args!("Tab navigation error: {0}", e), lvl,
                &("bevy_input_focus::tab_navigation",
                        "bevy_input_focus::tab_navigation",
                        ::log::__private_api::loc()), ());
        }
    }
};warn!("Tab navigation error: {e}");
457                // This failure mode is recoverable, but still indicates a problem.
458                if let TabNavigationError::NoTabGroupForCurrentFocus { new_focus, .. } = e {
459                    event.propagate(false);
460                    focus.set(new_focus, FocusCause::Navigated);
461                    visible.0 = true;
462                }
463            }
464        }
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use bevy_ecs::system::SystemState;
471
472    use super::*;
473
474    #[test]
475    fn test_tab_navigation() {
476        let mut app = App::new();
477        let world = app.world_mut();
478
479        let tab_group_entity = world.spawn(TabGroup::new(0)).id();
480        let tab_entity_1 = world.spawn((TabIndex(0), ChildOf(tab_group_entity))).id();
481        let tab_entity_2 = world.spawn((TabIndex(1), ChildOf(tab_group_entity))).id();
482
483        let mut system_state: SystemState<TabNavigation> = SystemState::new(world);
484        let tab_navigation = system_state.get(world).unwrap();
485        assert_eq!(tab_navigation.tabgroup_query.iter().count(), 1);
486        assert!(tab_navigation.tabindex_query.iter().count() >= 2);
487
488        let next_entity =
489            tab_navigation.navigate(&InputFocus::from_entity(tab_entity_1), NavAction::Next);
490        assert_eq!(next_entity, Ok(tab_entity_2));
491
492        let prev_entity =
493            tab_navigation.navigate(&InputFocus::from_entity(tab_entity_2), NavAction::Previous);
494        assert_eq!(prev_entity, Ok(tab_entity_1));
495
496        let first_entity = tab_navigation.navigate(&InputFocus::default(), NavAction::First);
497        assert_eq!(first_entity, Ok(tab_entity_1));
498
499        let last_entity = tab_navigation.navigate(&InputFocus::default(), NavAction::Last);
500        assert_eq!(last_entity, Ok(tab_entity_2));
501    }
502
503    #[test]
504    fn test_tab_navigation_between_groups_is_sorted_by_group() {
505        let mut app = App::new();
506        let world = app.world_mut();
507
508        let tab_group_1 = world.spawn(TabGroup::new(0)).id();
509        let tab_entity_1 = world.spawn((TabIndex(0), ChildOf(tab_group_1))).id();
510        let tab_entity_2 = world.spawn((TabIndex(1), ChildOf(tab_group_1))).id();
511
512        let tab_group_2 = world.spawn(TabGroup::new(1)).id();
513        let tab_entity_3 = world.spawn((TabIndex(0), ChildOf(tab_group_2))).id();
514        let tab_entity_4 = world.spawn((TabIndex(1), ChildOf(tab_group_2))).id();
515
516        let mut system_state: SystemState<TabNavigation> = SystemState::new(world);
517        let tab_navigation = system_state.get(world).unwrap();
518        assert_eq!(tab_navigation.tabgroup_query.iter().count(), 2);
519        assert!(tab_navigation.tabindex_query.iter().count() >= 4);
520
521        let next_entity =
522            tab_navigation.navigate(&InputFocus::from_entity(tab_entity_1), NavAction::Next);
523        assert_eq!(next_entity, Ok(tab_entity_2));
524
525        let prev_entity =
526            tab_navigation.navigate(&InputFocus::from_entity(tab_entity_2), NavAction::Previous);
527        assert_eq!(prev_entity, Ok(tab_entity_1));
528
529        let first_entity = tab_navigation.navigate(&InputFocus::default(), NavAction::First);
530        assert_eq!(first_entity, Ok(tab_entity_1));
531
532        let last_entity = tab_navigation.navigate(&InputFocus::default(), NavAction::Last);
533        assert_eq!(last_entity, Ok(tab_entity_4));
534
535        let next_from_end_of_group_entity =
536            tab_navigation.navigate(&InputFocus::from_entity(tab_entity_2), NavAction::Next);
537        assert_eq!(next_from_end_of_group_entity, Ok(tab_entity_3));
538
539        let prev_entity_from_start_of_group =
540            tab_navigation.navigate(&InputFocus::from_entity(tab_entity_3), NavAction::Previous);
541        assert_eq!(prev_entity_from_start_of_group, Ok(tab_entity_2));
542    }
543
544    /// Sets up an app with a primary window and both the input-focus and tab-navigation plugins,
545    /// so both `AcquireFocus` observers (window-clearing in `bevy_input_focus`, and
546    /// `acquire_focus_tab_index` here) are registered, with initial focus resolved.
547    fn acquire_focus_app() -> (App, Entity) {
548        use crate::InputFocusPlugin;
549        use bevy_input::InputPlugin;
550
551        let mut app = App::new();
552        app.add_plugins((InputPlugin, InputFocusPlugin, TabNavigationPlugin));
553        let window = app
554            .world_mut()
555            .spawn((Window::default(), PrimaryWindow))
556            .id();
557        // Resolve initial focus (focus goes to the primary window).
558        app.update();
559        (app, window)
560    }
561
562    #[test]
563    fn acquire_focus_focuses_entity_with_tab_index() {
564        let (mut app, window) = acquire_focus_app();
565
566        let focusable = app.world_mut().spawn(TabIndex(0)).id();
567
568        app.world_mut().trigger(AcquireFocus {
569            focused_entity: focusable,
570            window,
571        });
572        app.update();
573
574        assert_eq!(app.world().resource::<InputFocus>().get(), Some(focusable));
575    }
576
577    #[test]
578    fn acquire_focus_does_not_focus_entity_without_tab_index() {
579        let (mut app, window) = acquire_focus_app();
580
581        // A non-focusable entity must never become focused just because it was the request target:
582        // only `TabIndex` entities are valid focus targets for `acquire_focus_tab_index`. The
583        // request instead bubbles up to the window, where the generalized
584        // `on_window_acquire_focus_clear` observer clears focus.
585        let non_focusable = app.world_mut().spawn(ChildOf(window)).id();
586        app.world_mut().trigger(AcquireFocus {
587            focused_entity: non_focusable,
588            window,
589        });
590        app.update();
591
592        assert_ne!(
593            app.world().resource::<InputFocus>().get(),
594            Some(non_focusable)
595        );
596        assert_eq!(app.world().resource::<InputFocus>().get(), None);
597    }
598}