Skip to main content

ferroui_controls/primitives/
tab_strip.rs

1use super::{
2    SelectingItemsControl, SelectingItemsControlImpl, SelectingItemsControlImplExt, TabStripItem, TemplatedControlImpl,
3};
4use crate::generators::RecycleKey;
5use crate::templates::{FuncTemplate, ITemplateOf};
6use crate::{Control, ControlImpl, ItemsControl, ItemsControlImpl, Panel, SelectionMode, WrapPanel};
7use ferroui_base::input::{
8    FocusChangedEventArgs, InputElement, InputElementImpl, NavigationMethod, PointerEventArgs, PointerUpdateKind,
9};
10use ferroui_base::interactivity::{IRoutedEventArgs, InteractiveImpl};
11use ferroui_base::layout::{LayoutableImpl, Orientation};
12use ferroui_base::{
13    ferro_class, ferro_impl_classes, instantiate, BoxedValue, FerroObjectImpl, Ref,
14    StyledElementImpl, Visual, VisualImpl,
15};
16use std::rc::Rc;
17
18/// A strip of tabs: a [`SelectingItemsControl`] that always has a selected
19/// item.
20#[repr(C)]
21pub struct TabStrip {
22    base: SelectingItemsControl,
23}
24
25ferro_class!(TabStrip: SelectingItemsControl);
26ferroui_base::ferro_class_info!(TabStrip { new: TabStrip::new });
27ferro_impl_classes!(
28    TabStrip: StyledElementImpl,
29    VisualImpl,
30    LayoutableImpl,
31    InteractiveImpl,
32    InputElementImpl,
33    ControlImpl,
34    TemplatedControlImpl
35);
36
37ferroui_base::ferro_impl_classes!(TabStrip: FerroObjectImpl);
38
39impl ItemsControlImpl for TabStrip {
40    fn create_container_for_item_override(
41        _this: &Self,
42        _item: &Option<BoxedValue>,
43        _index: i32,
44        _recycle_key: Option<RecycleKey>,
45    ) -> Ref<Control> {
46        TabStripItem::new().upcast()
47    }
48
49    fn needs_container_override(this: &Self, item: &Option<BoxedValue>, _index: i32) -> (bool, Option<RecycleKey>) {
50        this.needs_container::<TabStripItem>(item)
51    }
52}
53
54impl SelectingItemsControlImpl for TabStrip {
55    fn should_trigger_selection(this: &Self, selectable: &Visual, e: &PointerEventArgs) -> bool {
56        matches!(
57            e.properties().pointer_update_kind,
58            PointerUpdateKind::LeftButtonPressed | PointerUpdateKind::LeftButtonReleased
59        ) && Self::parent_should_trigger_selection(this, selectable, e)
60    }
61
62    fn update_selection_from_event(this: &Self, container: &Ref<Control>, event_args: &dyn IRoutedEventArgs) -> bool {
63        if let Some(focus) = event_args.downcast_ref::<FocusChangedEventArgs>() {
64            if focus.navigation_method != NavigationMethod::Directional {
65                return false;
66            }
67        }
68
69        Self::parent_update_selection_from_event(this, container, event_args)
70    }
71}
72
73impl TabStrip {
74    fn static_constructor() {
75        // The default value for the `ItemsPanel` property.
76        let default_panel: Rc<dyn ITemplateOf<Option<Ref<Panel>>>> = FuncTemplate::new(|| {
77            let panel = WrapPanel::new();
78            panel.set_orientation(Orientation::Horizontal);
79            Some(panel.upcast::<Panel>())
80        });
81
82        SelectingItemsControl::selection_mode_property()
83            .override_default_value::<TabStrip>(SelectionMode::ALWAYS_SELECTED);
84        InputElement::focusable_property().override_default_value::<TabStrip>(false);
85        ItemsControl::items_panel_property().override_default_value::<TabStrip>(default_panel);
86    }
87
88    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
89    pub fn construct() -> Self {
90        Self { base: SelectingItemsControl::construct() }
91    }
92
93    pub fn new() -> Ref<Self> {
94        instantiate(Self::construct())
95    }
96}