Skip to main content

bevy_ui_widgets/
tabs.rs

1use accesskit::Role;
2use bevy_a11y::AccessibilityNode;
3use bevy_app::{App, Plugin, PostUpdate};
4use bevy_ecs::{
5    change_detection::DetectChanges,
6    component::Component,
7    entity::Entity,
8    hierarchy::{ChildOf, Children},
9    lifecycle::RemovedComponents,
10    observer::On,
11    query::{Added, Changed, Has, Or, With},
12    reflect::ReflectComponent,
13    schedule::IntoScheduleConfigs,
14    system::{Commands, Query, Res, ResMut},
15    template::FromTemplate,
16};
17use bevy_input::{
18    keyboard::{KeyCode, KeyboardInput},
19    ButtonState,
20};
21use bevy_input_focus::{
22    tab_navigation::TabIndex, FocusCause, FocusedInput, InputFocus, InputFocusSystems,
23    InputFocusVisible,
24};
25use bevy_picking::{events::PointerClick, pointer::PointerButton};
26use bevy_reflect::{prelude::ReflectDefault, Reflect};
27use bevy_ui::{InteractionDisabled, Selectable, Selected};
28
29use crate::ControlOrientation;
30
31/// Determines whether moving keyboard focus also requests tab selection.
32#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TabActivation {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                TabActivation::Manual => "Manual",
                TabActivation::Automatic => "Automatic",
            })
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for TabActivation {
    #[inline]
    fn default() -> Self { Self::Manual }
}Default, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for TabActivation { }
#[automatically_derived]
impl ::core::clone::Clone for TabActivation {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for TabActivation { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for TabActivation { }
#[automatically_derived]
impl ::core::cmp::PartialEq for TabActivation {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for TabActivation { }Eq, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for TabActivation 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
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {}
        }
        impl bevy_reflect::Typed for TabActivation 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("Manual")),
                                                bevy_reflect::enums::VariantInfo::Unit(bevy_reflect::enums::UnitVariantInfo::new("Automatic"))]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for TabActivation where  {
            fn type_path() -> &'static str {
                "bevy_ui_widgets::tabs::TabActivation"
            }
            fn short_type_path() -> &'static str { "TabActivation" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("TabActivation")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs")
            }
        }
        impl bevy_reflect::Reflect for TabActivation 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(<TabActivation
                                        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 TabActivation 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 {
                    TabActivation::Manual { .. } => 0usize,
                    TabActivation::Automatic { .. } => 0usize,
                    _ => 0,
                }
            }
            #[inline]
            fn variant_name(&self) -> &str {
                match self {
                    TabActivation::Manual { .. } => "Manual",
                    TabActivation::Automatic { .. } => "Automatic",
                    _ =>
                        ::core::panicking::panic("internal error: entered unreachable code"),
                }
            }
            #[inline]
            fn variant_index(&self) -> usize {
                match self {
                    TabActivation::Manual { .. } => 0usize,
                    TabActivation::Automatic { .. } => 1usize,
                    _ =>
                        ::core::panicking::panic("internal error: entered unreachable code"),
                }
            }
            #[inline]
            fn variant_type(&self) -> bevy_reflect::enums::VariantType {
                match self {
                    TabActivation::Manual { .. } =>
                        bevy_reflect::enums::VariantType::Unit,
                    TabActivation::Automatic { .. } =>
                        bevy_reflect::enums::VariantType::Unit,
                    _ =>
                        ::core::panicking::panic("internal error: entered unreachable code"),
                }
            }
            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 TabActivation 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)
                            {
                            "Manual" => { *self = TabActivation::Manual {} }
                            "Automatic" => { *self = TabActivation::Automatic {} }
                            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)
            }
            #[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 TabActivation 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) {
                        "Manual" =>
                            ::core::option::Option::Some(TabActivation::Manual {}),
                        "Automatic" =>
                            ::core::option::Option::Some(TabActivation::Automatic {}),
                        name => ::core::option::Option::None,
                    }
                } else { ::core::option::Option::None }
            }
        }
    };Reflect)]
33#[reflect(Default, Clone, PartialEq)]
34pub enum TabActivation {
35    /// Enter or Space requests selection of the focused tab.
36    #[default]
37    Manual,
38    /// Moving focus with a navigation key requests selection of the focused tab.
39    Automatic,
40}
41
42/// Headless tab-strip behavior and policy.
43///
44/// Selection is stored separately in [`SelectedTab`]. User interaction emits
45/// [`crate::ValueChange<Option<Entity>>`] from this entity and does not update that state unless
46/// [`tablist_self_update`] is attached as an observer. Activating the already-selected tab does
47/// not re-emit.
48///
49/// Only primary-button clicks change selection. [`InteractionDisabled`] on this entity disables
50/// the whole strip; disabled strips and disabled tabs let pointer and keyboard events propagate
51/// instead of consuming them.
52#[derive(#[doc =
"**Required Components**: [`AccessibilityNode`], [`SelectedTab`]. \n\n A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order."]
impl bevy_ecs::component::Component for TabList 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) {
        required_components.register_required::<AccessibilityNode>(||
                {
                    let x: AccessibilityNode =
                        (||
                                        AccessibilityNode(accesskit::Node::new(Role::TabList)))().into();
                    x
                });
        required_components.register_required::<SelectedTab>(<SelectedTab as
                ::core::default::Default>::default);
    }
    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::fmt::Debug for TabList {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "TabList",
            "orientation", &self.orientation, "activation", &&self.activation)
    }
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for TabList { }
#[automatically_derived]
impl ::core::clone::Clone for TabList {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<ControlOrientation>;
        let _: ::core::clone::AssertParamIsClone<TabActivation>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for TabList { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for TabList { }
#[automatically_derived]
impl ::core::cmp::PartialEq for TabList {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.orientation == other.orientation &&
            self.activation == other.activation
    }
}PartialEq, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for TabList 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::<ReflectComponent, Self,
                    _>(());
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <ControlOrientation as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <TabActivation as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for TabList 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::<ControlOrientation>("orientation"),
                                                bevy_reflect::NamedField::new::<TabActivation>("activation")]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for TabList where  {
            fn type_path() -> &'static str {
                "bevy_ui_widgets::tabs::TabList"
            }
            fn short_type_path() -> &'static str { "TabList" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("TabList")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs")
            }
        }
        impl bevy_reflect::Reflect for TabList 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(<TabList
                                        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 TabList where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "orientation" =>
                        ::core::option::Option::Some(&self.orientation),
                    "activation" =>
                        ::core::option::Option::Some(&self.activation),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "orientation" =>
                        ::core::option::Option::Some(&mut self.orientation),
                    "activation" =>
                        ::core::option::Option::Some(&mut self.activation),
                    _ => ::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.orientation),
                    1usize => ::core::option::Option::Some(&self.activation),
                    _ => ::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.orientation),
                    1usize =>
                        ::core::option::Option::Some(&mut self.activation),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("orientation"),
                    1usize => ::core::option::Option::Some("activation"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "orientation" => ::core::option::Option::Some(0usize),
                    "activation" => ::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("orientation",
                    bevy_reflect::PartialReflect::to_dynamic(&self.orientation)?);
                dynamic.insert_boxed("activation",
                    bevy_reflect::PartialReflect::to_dynamic(&self.activation)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for TabList 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)
            }
            #[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 TabList 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) =
                            (||
                                        <ControlOrientation as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "orientation")?))() {
                        __this.orientation = __field;
                    }
                    if let ::core::option::Option::Some(__field) =
                            (||
                                        <TabActivation as
                                                bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                                    "activation")?))() {
                        __this.activation = __field;
                    }
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect)]
53#[require(AccessibilityNode(accesskit::Node::new(Role::TabList)), SelectedTab)]
54#[reflect(Component, Default, Clone, PartialEq)]
55pub struct TabList {
56    /// The axis used by arrow-key navigation.
57    pub orientation: ControlOrientation,
58    /// Whether keyboard navigation requests selection immediately.
59    pub activation: TabActivation,
60}
61
62impl Default for TabList {
63    fn default() -> Self {
64        Self {
65            orientation: ControlOrientation::Horizontal,
66            activation: TabActivation::default(),
67        }
68    }
69}
70
71/// The selected [`Tab`] within a [`TabList`].
72///
73/// The referenced entity must be an enabled direct child of the list. Missing, stale, disabled,
74/// and unrelated entities are treated as no selection.
75#[derive(impl bevy_ecs::component::Component for SelectedTab 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, impl bevy_ecs::template::FromTemplate for SelectedTab {
    type Template = SelectedTabTemplate;
}
impl ::core::marker::Unpin for SelectedTab where
    for<'a> [()]: bevy_ecs::template::SpecializeFromTemplate {}
#[allow(missing_docs)]
pub struct SelectedTabTemplate(pub <Option<Entity> as
    bevy_ecs::template::BuiltInTemplate>::Template);
impl bevy_ecs::template::Template for SelectedTabTemplate {
    type Output = SelectedTab;
    fn build_template(&self,
        context: &mut bevy_ecs::template::TemplateContext)
        -> bevy_ecs::error::Result<Self::Output> {
        bevy_ecs::error::Result::Ok(SelectedTab(self.0.build_template(context)?))
    }
    fn clone_template(&self) -> Self {
        Self(bevy_ecs::template::Template::clone_template(&self.0))
    }
}
impl ::core::default::Default for SelectedTabTemplate {
    fn default() -> Self { Self(::core::default::Default::default()) }
}FromTemplate, #[automatically_derived]
impl ::core::fmt::Debug for SelectedTab {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "SelectedTab",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for SelectedTab {
    #[inline]
    fn default() -> Self { Self(::core::default::Default::default()) }
}Default, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for SelectedTab { }
#[automatically_derived]
impl ::core::cmp::PartialEq for SelectedTab {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for SelectedTab {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Option<Entity>>;
    }
}Eq, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for SelectedTab 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::<ReflectComponent, Self,
                    _>(());
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <Option<Entity> as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl bevy_reflect::Typed for SelectedTab 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::<Option<Entity>>(0usize)]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for SelectedTab where  {
            fn type_path() -> &'static str {
                "bevy_ui_widgets::tabs::SelectedTab"
            }
            fn short_type_path() -> &'static str { "SelectedTab" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("SelectedTab")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs")
            }
        }
        impl bevy_reflect::Reflect for SelectedTab 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(<SelectedTab
                                        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 SelectedTab 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 SelectedTab 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)
            }
            #[inline]
            #[allow(unreachable_code, reason =
            "Ignored fields without a `clone` attribute will early-return with an error")]
            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(Self {
                            0: <Option<Entity> as
                                        bevy_reflect::PartialReflect>::reflect_clone_and_take(&self.0)?,
                        }))
            }
        }
        impl bevy_reflect::FromReflect for SelectedTab 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) =
                            (||
                                        <Option<Entity> 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)]
76#[reflect(Component, Default, PartialEq)]
77pub struct SelectedTab(#[template(built_in)] pub Option<Entity>);
78
79/// A headless tab header.
80///
81/// Tabs are focusable using a roving [`TabIndex`]. Their derived [`Selected`] state mirrors the
82/// containing list's valid [`SelectedTab`] value.
83#[derive(#[doc =
"**Required Components**: [`AccessibilityNode`], [`Selectable`], [`TabIndex`]. \n\n A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order."]
impl bevy_ecs::component::Component for Tab 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) {
        required_components.register_required::<AccessibilityNode>(||
                {
                    let x: AccessibilityNode =
                        (||
                                        AccessibilityNode(accesskit::Node::new(Role::Tab)))().into();
                    x
                });
        required_components.register_required::<Selectable>(<Selectable as
                ::core::default::Default>::default);
        required_components.register_required::<TabIndex>(||
                { let x: TabIndex = (|| TabIndex(-1))().into(); x });
    }
    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::fmt::Debug for Tab {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "Tab")
    }
}Debug, #[automatically_derived]
impl ::core::default::Default for Tab {
    #[inline]
    fn default() -> Self { Self }
}Default, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Tab { }
#[automatically_derived]
impl ::core::clone::Clone for Tab {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Tab { }Copy, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for Tab 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::<ReflectComponent, Self,
                    _>(());
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {}
        }
        impl bevy_reflect::Typed for Tab 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>(&[]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for Tab where  {
            fn type_path() -> &'static str { "bevy_ui_widgets::tabs::Tab" }
            fn short_type_path() -> &'static str { "Tab" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("Tab")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui_widgets::tabs")
            }
        }
        impl bevy_reflect::Reflect for Tab 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(<Tab
                                        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 Tab where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name { _ => ::core::option::Option::None, }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name { _ => ::core::option::Option::None, }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index { _ => ::core::option::Option::None, }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index { _ => ::core::option::Option::None, }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index { _ => ::core::option::Option::None, }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name { _ => ::core::option::Option::None, }
            }
            fn field_len(&self) -> usize { 0usize }
            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));
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for Tab 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)
            }
            #[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 Tab 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();
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect)]
84#[require(
85    AccessibilityNode(accesskit::Node::new(Role::Tab)),
86    Selectable,
87    TabIndex(-1)
88)]
89#[reflect(Component, Default, Clone)]
90pub struct Tab;
91
92/// Plugin that registers tab-list observers and derived state.
93pub struct TabPlugin;
94
95impl Plugin for TabPlugin {
96    fn build(&self, app: &mut App) {
97        app.add_observer(tablist_on_click)
98            .add_observer(tab_on_key_input)
99            .add_systems(
100                PostUpdate,
101                update_tablist_derived_state
102                    .after(crate::MenuFocusSystem)
103                    .before(InputFocusSystems::FocusChangeEvents),
104            );
105    }
106}
107
108/// Observer that applies tab selection requests to [`SelectedTab`].
109pub fn tablist_self_update(
110    change: On<crate::ValueChange<Option<Entity>>>,
111    tablists: Query<(), With<TabList>>,
112    mut commands: Commands,
113) {
114    if tablists.contains(change.source) {
115        commands
116            .entity(change.source)
117            .insert(SelectedTab(change.value));
118    }
119}
120
121fn tablist_on_click(
122    mut click: On<PointerClick>,
123    tablists: Query<(&SelectedTab, Has<InteractionDisabled>), With<TabList>>,
124    tabs: Query<Has<InteractionDisabled>, With<Tab>>,
125    parents: Query<&ChildOf>,
126    mut commands: Commands,
127) {
128    if click.button != PointerButton::Primary {
129        return;
130    }
131    let Ok((selection, list_disabled)) = tablists.get(click.entity) else {
132        return;
133    };
134
135    let target = click.original_event_target();
136    let tab = if tabs.contains(target) {
137        Some(target)
138    } else {
139        parents
140            .iter_ancestors(target)
141            .take_while(|ancestor| *ancestor != click.entity)
142            .find(|ancestor| tabs.contains(*ancestor))
143    };
144    let Some(tab) = tab else {
145        return;
146    };
147    let Ok(parent) = parents.get(tab) else {
148        return;
149    };
150    if parent.parent() != click.entity {
151        return;
152    }
153    if list_disabled || tabs.get(tab).is_ok_and(|disabled| disabled) {
154        return;
155    }
156
157    click.propagate(false);
158    if selection.0 != Some(tab) {
159        commands.trigger(crate::ValueChange::<Option<Entity>> {
160            source: click.entity,
161            value: Some(tab),
162            is_final: true,
163        });
164    }
165}
166
167fn tab_on_key_input(
168    mut input: On<FocusedInput<KeyboardInput>>,
169    tablists: Query<(&TabList, &SelectedTab, &Children, Has<InteractionDisabled>)>,
170    tabs: Query<Has<InteractionDisabled>, With<Tab>>,
171    parents: Query<&ChildOf>,
172    mut focus: ResMut<InputFocus>,
173    mut focus_visible: ResMut<InputFocusVisible>,
174    mut commands: Commands,
175) {
176    if !tabs.contains(input.focused_entity) {
177        return;
178    }
179    let Ok(parent) = parents.get(input.focused_entity) else {
180        return;
181    };
182    let Ok((tablist, selection, children, list_disabled)) = tablists.get(parent.parent()) else {
183        return;
184    };
185    if list_disabled {
186        return;
187    }
188    let event = &input.input;
189    if event.state != ButtonState::Pressed || event.repeat {
190        return;
191    }
192
193    enum Navigation {
194        Previous,
195        Next,
196        First,
197        Last,
198        Activate,
199    }
200
201    let navigation = match event.key_code {
202        KeyCode::ArrowLeft if tablist.orientation == ControlOrientation::Horizontal => {
203            Navigation::Previous
204        }
205        KeyCode::ArrowRight if tablist.orientation == ControlOrientation::Horizontal => {
206            Navigation::Next
207        }
208        KeyCode::ArrowUp if tablist.orientation == ControlOrientation::Vertical => {
209            Navigation::Previous
210        }
211        KeyCode::ArrowDown if tablist.orientation == ControlOrientation::Vertical => {
212            Navigation::Next
213        }
214        KeyCode::Home => Navigation::First,
215        KeyCode::End => Navigation::Last,
216        KeyCode::Enter | KeyCode::Space => Navigation::Activate,
217        _ => return,
218    };
219
220    if #[allow(non_exhaustive_omitted_patterns)] match navigation {
    Navigation::Activate => true,
    _ => false,
}matches!(navigation, Navigation::Activate) {
221        if tabs
222            .get(input.focused_entity)
223            .is_ok_and(|disabled| disabled)
224        {
225            return;
226        }
227        input.propagate(false);
228        if selection.0 != Some(input.focused_entity) {
229            commands.trigger(crate::ValueChange::<Option<Entity>> {
230                source: parent.parent(),
231                value: Some(input.focused_entity),
232                is_final: true,
233            });
234        }
235        return;
236    }
237    input.propagate(false);
238
239    let enabled = children
240        .iter()
241        .copied()
242        .filter(|child| tabs.get(*child).is_ok_and(|disabled| !disabled))
243        .collect::<Vec<_>>();
244    if enabled.is_empty() {
245        return;
246    }
247
248    let current = enabled
249        .iter()
250        .position(|tab| *tab == input.focused_entity)
251        .or_else(|| {
252            selection
253                .0
254                .and_then(|selected| enabled.iter().position(|tab| *tab == selected))
255        });
256    let next_index = match navigation {
257        Navigation::Previous => current
258            .filter(|index| *index > 0)
259            .map_or(enabled.len() - 1, |index| index - 1),
260        Navigation::Next => current.map_or(0, |index| (index + 1) % enabled.len()),
261        Navigation::First => 0,
262        Navigation::Last => enabled.len() - 1,
263        Navigation::Activate => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
264    };
265    let next = enabled[next_index];
266    if focus.get() != Some(next) {
267        focus.set(next, FocusCause::Navigated);
268    }
269    focus_visible.0 = true;
270    if tablist.activation == TabActivation::Automatic && selection.0 != Some(next) {
271        commands.trigger(crate::ValueChange::<Option<Entity>> {
272            source: parent.parent(),
273            value: Some(next),
274            is_final: true,
275        });
276    }
277}
278
279/// Derives per-tab state from each [`TabList`]'s [`SelectedTab`] and the current keyboard focus:
280///
281/// - [`Selected`] markers mirror a validated `SelectedTab` (the referenced entity must be an
282///   enabled direct child; anything else counts as no selection).
283/// - [`TabIndex`] follows the roving-tabindex pattern: one tab per list is focusable (the
284///   focused tab, else the selected tab, else the first enabled tab), so Tab/Shift+Tab skip
285///   the strip while arrow keys move within it.
286///
287/// Runs in `PostUpdate`, early-returning unless selection, children, focus, or disabled state
288/// changed.
289fn update_tablist_derived_state(
290    tablists: Query<(&SelectedTab, &Children), With<TabList>>,
291    tabs: Query<(Has<InteractionDisabled>, Has<Selected>, &TabIndex), With<Tab>>,
292    focus: Option<Res<InputFocus>>,
293    changed_tablists: Query<(), (With<TabList>, Or<(Changed<SelectedTab>, Changed<Children>)>)>,
294    changed_tabs: Query<(), (With<Tab>, Or<(Added<Tab>, Added<InteractionDisabled>)>)>,
295    mut removed_disabled: RemovedComponents<InteractionDisabled>,
296    mut commands: Commands,
297) {
298    let focus_changed = focus.as_ref().is_some_and(DetectChanges::is_changed);
299    let disabled_removed = !removed_disabled.is_empty();
300    removed_disabled.clear();
301    if !focus_changed && !disabled_removed && changed_tablists.is_empty() && changed_tabs.is_empty()
302    {
303        return;
304    }
305
306    for (selection, children) in tablists.iter() {
307        let selected = selection.0.filter(|entity| {
308            children.contains(entity) && tabs.get(*entity).is_ok_and(|(disabled, _, _)| !disabled)
309        });
310        let focused = focus
311            .as_ref()
312            .and_then(|focus| focus.get())
313            .filter(|entity| {
314                children.contains(entity)
315                    && tabs.get(*entity).is_ok_and(|(disabled, _, _)| !disabled)
316            });
317        let roving = focused.or(selected).or_else(|| {
318            children
319                .iter()
320                .find(|child| tabs.get(**child).is_ok_and(|(disabled, _, _)| !disabled))
321                .copied()
322        });
323
324        for child in children.iter() {
325            let Ok((_, is_selected, tab_index)) = tabs.get(*child) else {
326                continue;
327            };
328            let should_select = selected == Some(*child);
329            if should_select && !is_selected {
330                commands.entity(*child).insert(Selected);
331            } else if !should_select && is_selected {
332                commands.entity(*child).remove::<Selected>();
333            }
334
335            let desired_index = if roving == Some(*child) { 0 } else { -1 };
336            if tab_index.0 != desired_index {
337                commands.entity(*child).insert(TabIndex(desired_index));
338            }
339        }
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346    use bevy_ecs::{hierarchy::ChildOf, observer::On, resource::Resource, system::ResMut};
347    use bevy_input::{keyboard::Key, InputPlugin};
348    use bevy_input_focus::{FocusCause, InputDispatchPlugin, InputFocusPlugin};
349    use bevy_math::Vec2;
350    use bevy_picking::{
351        backend::HitData,
352        events::Pointer,
353        pointer::{Location, PointerButton, PointerId},
354    };
355    use bevy_window::{PrimaryWindow, Window, WindowRef};
356
357    #[derive(Resource, Default)]
358    struct SelectionRequests(Vec<(Entity, Option<Entity>)>);
359
360    fn tab_app() -> (App, Entity) {
361        let mut app = App::new();
362        app.add_plugins((
363            InputPlugin,
364            InputFocusPlugin,
365            InputDispatchPlugin,
366            TabPlugin,
367        ))
368        .init_resource::<SelectionRequests>()
369        .add_observer(
370            |change: On<crate::ValueChange<Option<Entity>>>,
371             mut requests: ResMut<SelectionRequests>| {
372                requests.0.push((change.source, change.value));
373            },
374        );
375        let window = app
376            .world_mut()
377            .spawn((Window::default(), PrimaryWindow))
378            .id();
379        app.update();
380        (app, window)
381    }
382
383    fn press_key(app: &mut App, key_code: KeyCode, window: Entity) {
384        let logical_key = match key_code {
385            KeyCode::ArrowLeft => Key::ArrowLeft,
386            KeyCode::ArrowRight => Key::ArrowRight,
387            KeyCode::ArrowUp => Key::ArrowUp,
388            KeyCode::ArrowDown => Key::ArrowDown,
389            KeyCode::Home => Key::Home,
390            KeyCode::End => Key::End,
391            KeyCode::Enter => Key::Enter,
392            KeyCode::Space => Key::Space,
393            _ => Key::Unidentified(bevy_input::keyboard::NativeKey::Unidentified),
394        };
395        app.world_mut().write_message(KeyboardInput {
396            key_code,
397            logical_key,
398            state: ButtonState::Pressed,
399            text: None,
400            repeat: false,
401            window,
402        });
403        app.update();
404    }
405
406    fn click(app: &mut App, target: Entity, window: Entity) {
407        click_with_button(app, target, window, PointerButton::Primary);
408    }
409
410    fn click_with_button(app: &mut App, target: Entity, window: Entity, button: PointerButton) {
411        let location = Location {
412            target: bevy_camera::NormalizedRenderTarget::Window(
413                WindowRef::Entity(window).normalize(Some(window)).unwrap(),
414            ),
415            position: Vec2::ZERO,
416        };
417        app.world_mut().trigger(PointerClick {
418            entity: target,
419            pointer: Pointer::new(PointerId::Mouse, location),
420            button,
421            hit: HitData::new(window, 0.0, None, None),
422            duration: core::time::Duration::from_millis(10),
423            count: 1,
424        });
425        app.update();
426    }
427
428    #[test]
429    fn clicking_enabled_tab_requests_selection_without_mutating_controlled_state() {
430        let (mut app, window) = tab_app();
431        let list = app
432            .world_mut()
433            .spawn((TabList::default(), ChildOf(window)))
434            .id();
435        let first = app.world_mut().spawn((Tab, ChildOf(list))).id();
436        let second = app.world_mut().spawn((Tab, ChildOf(list))).id();
437        app.world_mut()
438            .entity_mut(list)
439            .insert(SelectedTab(Some(first)));
440        app.update();
441
442        click(&mut app, second, window);
443
444        assert_eq!(
445            app.world().resource::<SelectionRequests>().0,
446            [(list, Some(second))]
447        );
448        assert_eq!(
449            app.world().entity(list).get::<SelectedTab>(),
450            Some(&SelectedTab(Some(first)))
451        );
452    }
453
454    #[test]
455    fn self_update_observer_applies_selection_request() {
456        let (mut app, window) = tab_app();
457        let list = app
458            .world_mut()
459            .spawn((TabList::default(), ChildOf(window)))
460            .observe(tablist_self_update)
461            .id();
462        let tab = app.world_mut().spawn((Tab, ChildOf(list))).id();
463        app.update();
464
465        click(&mut app, tab, window);
466
467        assert_eq!(
468            app.world().entity(list).get::<SelectedTab>(),
469            Some(&SelectedTab(Some(tab)))
470        );
471    }
472
473    #[test]
474    fn valid_selection_derives_selected_state_and_roving_entry() {
475        let (mut app, window) = tab_app();
476        let list = app
477            .world_mut()
478            .spawn((TabList::default(), ChildOf(window)))
479            .id();
480        let first = app.world_mut().spawn((Tab, ChildOf(list))).id();
481        let second = app.world_mut().spawn((Tab, ChildOf(list))).id();
482        app.world_mut()
483            .entity_mut(list)
484            .insert(SelectedTab(Some(second)));
485
486        app.update();
487
488        assert!(!app.world().entity(first).contains::<Selected>());
489        assert!(app.world().entity(second).contains::<Selected>());
490        assert_eq!(
491            app.world().entity(first).get::<TabIndex>(),
492            Some(&TabIndex(-1))
493        );
494        assert_eq!(
495            app.world().entity(second).get::<TabIndex>(),
496            Some(&TabIndex(0))
497        );
498    }
499
500    #[test]
501    fn invalid_selection_clears_selected_state_and_uses_first_enabled_roving_entry() {
502        let (mut app, window) = tab_app();
503        let stale = app.world_mut().spawn_empty().id();
504        let list = app
505            .world_mut()
506            .spawn((
507                TabList::default(),
508                SelectedTab(Some(stale)),
509                ChildOf(window),
510            ))
511            .id();
512        let disabled = app
513            .world_mut()
514            .spawn((Tab, Selected, InteractionDisabled, ChildOf(list)))
515            .id();
516        let enabled = app.world_mut().spawn((Tab, ChildOf(list))).id();
517
518        app.update();
519
520        assert!(!app.world().entity(disabled).contains::<Selected>());
521        assert!(!app.world().entity(enabled).contains::<Selected>());
522        assert_eq!(
523            app.world().entity(disabled).get::<TabIndex>(),
524            Some(&TabIndex(-1))
525        );
526        assert_eq!(
527            app.world().entity(enabled).get::<TabIndex>(),
528            Some(&TabIndex(0))
529        );
530    }
531
532    #[test]
533    fn horizontal_manual_arrow_navigation_wraps_and_skips_disabled_tabs() {
534        use bevy_input::keyboard::KeyCode;
535
536        let (mut app, window) = tab_app();
537        let list = app
538            .world_mut()
539            .spawn((TabList::default(), ChildOf(window)))
540            .id();
541        let first = app.world_mut().spawn((Tab, ChildOf(list))).id();
542        app.world_mut()
543            .spawn((Tab, InteractionDisabled, ChildOf(list)));
544        let last = app.world_mut().spawn((Tab, ChildOf(list))).id();
545        app.world_mut()
546            .entity_mut(list)
547            .insert(SelectedTab(Some(first)));
548        app.world_mut()
549            .resource_mut::<InputFocus>()
550            .set(first, FocusCause::Navigated);
551        app.update();
552
553        press_key(&mut app, KeyCode::ArrowRight, window);
554        assert_eq!(app.world().resource::<InputFocus>().get(), Some(last));
555
556        press_key(&mut app, KeyCode::ArrowRight, window);
557        assert_eq!(app.world().resource::<InputFocus>().get(), Some(first));
558
559        press_key(&mut app, KeyCode::ArrowLeft, window);
560        assert_eq!(app.world().resource::<InputFocus>().get(), Some(last));
561        assert!(
562            app.world().resource::<SelectionRequests>().0.is_empty(),
563            "manual navigation must not request selection"
564        );
565    }
566
567    #[test]
568    fn automatic_navigation_requests_selection_when_focus_moves() {
569        let (mut app, window) = tab_app();
570        let list = app
571            .world_mut()
572            .spawn((
573                TabList {
574                    activation: TabActivation::Automatic,
575                    ..Default::default()
576                },
577                ChildOf(window),
578            ))
579            .id();
580        let first = app.world_mut().spawn((Tab, ChildOf(list))).id();
581        let second = app.world_mut().spawn((Tab, ChildOf(list))).id();
582        app.world_mut()
583            .entity_mut(list)
584            .insert(SelectedTab(Some(first)));
585        app.world_mut()
586            .resource_mut::<InputFocus>()
587            .set(first, FocusCause::Navigated);
588        app.update();
589
590        press_key(&mut app, KeyCode::ArrowRight, window);
591
592        assert_eq!(app.world().resource::<InputFocus>().get(), Some(second));
593        assert_eq!(
594            app.world().resource::<SelectionRequests>().0,
595            [(list, Some(second))]
596        );
597        assert_eq!(
598            app.world().entity(list).get::<SelectedTab>(),
599            Some(&SelectedTab(Some(first))),
600            "automatic activation remains a controlled selection request"
601        );
602    }
603
604    #[test]
605    fn manual_tabs_request_selection_on_enter_and_space() {
606        let (mut app, window) = tab_app();
607        let list = app
608            .world_mut()
609            .spawn((TabList::default(), ChildOf(window)))
610            .id();
611        let tab = app.world_mut().spawn((Tab, ChildOf(list))).id();
612        app.world_mut()
613            .resource_mut::<InputFocus>()
614            .set(tab, FocusCause::Navigated);
615        app.update();
616
617        press_key(&mut app, KeyCode::Enter, window);
618        press_key(&mut app, KeyCode::Space, window);
619
620        assert_eq!(
621            app.world().resource::<SelectionRequests>().0,
622            [(list, Some(tab)), (list, Some(tab))]
623        );
624    }
625
626    #[test]
627    fn vertical_tabs_use_up_and_down_arrows_only() {
628        let (mut app, window) = tab_app();
629        let list = app
630            .world_mut()
631            .spawn((
632                TabList {
633                    orientation: ControlOrientation::Vertical,
634                    ..Default::default()
635                },
636                ChildOf(window),
637            ))
638            .id();
639        let first = app.world_mut().spawn((Tab, ChildOf(list))).id();
640        let second = app.world_mut().spawn((Tab, ChildOf(list))).id();
641        app.world_mut()
642            .resource_mut::<InputFocus>()
643            .set(first, FocusCause::Navigated);
644        app.update();
645
646        press_key(&mut app, KeyCode::ArrowRight, window);
647        assert_eq!(app.world().resource::<InputFocus>().get(), Some(first));
648
649        press_key(&mut app, KeyCode::ArrowDown, window);
650        assert_eq!(app.world().resource::<InputFocus>().get(), Some(second));
651
652        press_key(&mut app, KeyCode::ArrowUp, window);
653        assert_eq!(app.world().resource::<InputFocus>().get(), Some(first));
654    }
655
656    #[test]
657    fn home_and_end_focus_first_and_last_enabled_tabs() {
658        let (mut app, window) = tab_app();
659        let list = app
660            .world_mut()
661            .spawn((TabList::default(), ChildOf(window)))
662            .id();
663        app.world_mut()
664            .spawn((Tab, InteractionDisabled, ChildOf(list)));
665        let first_enabled = app.world_mut().spawn((Tab, ChildOf(list))).id();
666        let last_enabled = app.world_mut().spawn((Tab, ChildOf(list))).id();
667        app.world_mut()
668            .spawn((Tab, InteractionDisabled, ChildOf(list)));
669        app.world_mut()
670            .resource_mut::<InputFocus>()
671            .set(first_enabled, FocusCause::Navigated);
672        app.update();
673
674        press_key(&mut app, KeyCode::End, window);
675        assert_eq!(
676            app.world().resource::<InputFocus>().get(),
677            Some(last_enabled)
678        );
679
680        press_key(&mut app, KeyCode::Home, window);
681        assert_eq!(
682            app.world().resource::<InputFocus>().get(),
683            Some(first_enabled)
684        );
685    }
686
687    #[test]
688    fn focused_tab_is_the_roving_entry_during_manual_navigation() {
689        let (mut app, window) = tab_app();
690        let list = app
691            .world_mut()
692            .spawn((TabList::default(), ChildOf(window)))
693            .id();
694        let selected = app.world_mut().spawn((Tab, ChildOf(list))).id();
695        let focused = app.world_mut().spawn((Tab, ChildOf(list))).id();
696        app.world_mut()
697            .entity_mut(list)
698            .insert(SelectedTab(Some(selected)));
699        app.world_mut()
700            .resource_mut::<InputFocus>()
701            .set(focused, FocusCause::Navigated);
702
703        app.update();
704
705        assert_eq!(
706            app.world().entity(selected).get::<TabIndex>(),
707            Some(&TabIndex(-1))
708        );
709        assert_eq!(
710            app.world().entity(focused).get::<TabIndex>(),
711            Some(&TabIndex(0))
712        );
713        assert!(app.world().entity(selected).contains::<Selected>());
714        assert!(!app.world().entity(focused).contains::<Selected>());
715    }
716
717    #[test]
718    fn tablist_and_tab_install_accessibility_semantics() {
719        let (mut app, window) = tab_app();
720        let list = app
721            .world_mut()
722            .spawn((TabList::default(), ChildOf(window)))
723            .id();
724        let tab = app.world_mut().spawn((Tab, ChildOf(list))).id();
725        app.update();
726
727        let list_node = app.world().entity(list).get::<AccessibilityNode>().unwrap();
728        let tab_node = app.world().entity(tab).get::<AccessibilityNode>().unwrap();
729        assert_eq!(list_node.role(), Role::TabList);
730        assert_eq!(tab_node.role(), Role::Tab);
731        assert!(app.world().entity(tab).contains::<Selectable>());
732    }
733
734    #[test]
735    fn disabled_tab_does_not_request_selection() {
736        let (mut app, window) = tab_app();
737        let list = app
738            .world_mut()
739            .spawn((TabList::default(), ChildOf(window)))
740            .id();
741        let disabled = app
742            .world_mut()
743            .spawn((Tab, InteractionDisabled, ChildOf(list)))
744            .id();
745        app.update();
746
747        click(&mut app, disabled, window);
748
749        assert!(app.world().resource::<SelectionRequests>().0.is_empty());
750    }
751
752    #[test]
753    fn secondary_click_does_not_change_selection() {
754        let (mut app, window) = tab_app();
755        let list = app
756            .world_mut()
757            .spawn((TabList::default(), ChildOf(window)))
758            .id();
759        let tab = app.world_mut().spawn((Tab, ChildOf(list))).id();
760        app.update();
761
762        click_with_button(&mut app, tab, window, PointerButton::Secondary);
763        click_with_button(&mut app, tab, window, PointerButton::Middle);
764
765        assert!(app.world().resource::<SelectionRequests>().0.is_empty());
766    }
767
768    #[test]
769    fn activating_the_selected_tab_does_not_reemit() {
770        let (mut app, window) = tab_app();
771        let list = app
772            .world_mut()
773            .spawn((TabList::default(), ChildOf(window)))
774            .observe(tablist_self_update)
775            .id();
776        let tab = app.world_mut().spawn((Tab, ChildOf(list))).id();
777        app.update();
778
779        click(&mut app, tab, window);
780        click(&mut app, tab, window);
781
782        assert_eq!(
783            app.world().resource::<SelectionRequests>().0,
784            [(list, Some(tab))]
785        );
786        assert_eq!(
787            app.world().entity(list).get::<SelectedTab>(),
788            Some(&SelectedTab(Some(tab)))
789        );
790    }
791
792    #[test]
793    fn disabled_tablist_ignores_clicks_and_keys() {
794        let (mut app, window) = tab_app();
795        let list = app
796            .world_mut()
797            .spawn((TabList::default(), InteractionDisabled, ChildOf(window)))
798            .id();
799        let first = app.world_mut().spawn((Tab, ChildOf(list))).id();
800        let second = app.world_mut().spawn((Tab, ChildOf(list))).id();
801        app.world_mut()
802            .resource_mut::<InputFocus>()
803            .set(first, FocusCause::Navigated);
804        app.update();
805
806        click(&mut app, second, window);
807        press_key(&mut app, KeyCode::ArrowRight, window);
808        press_key(&mut app, KeyCode::Enter, window);
809
810        assert!(app.world().resource::<SelectionRequests>().0.is_empty());
811        assert_eq!(app.world().resource::<InputFocus>().get(), Some(first));
812    }
813}