Skip to main content

freya_components/
floating_tab.rs

1use freya_core::prelude::*;
2
3use crate::{
4    activable_route_context::use_activable_route,
5    get_theme,
6    theming::component_themes::{
7        FloatingTabTheme,
8        FloatingTabThemePartial,
9    },
10};
11
12/// Current status of the Tab.
13#[derive(Debug, Default, PartialEq, Clone, Copy)]
14pub enum TabStatus {
15    /// Default state.
16    #[default]
17    Idle,
18    /// Mouse is hovering the Tab.
19    Hovering,
20}
21
22#[derive(Clone, PartialEq)]
23pub struct FloatingTab {
24    pub(crate) theme: Option<FloatingTabThemePartial>,
25    children: Vec<Element>,
26    /// Optionally handle the `on_press` event in [FloatingTab].
27    on_press: Option<EventHandler<Event<PressEventData>>>,
28    key: DiffKey,
29}
30
31impl KeyExt for FloatingTab {
32    fn write_key(&mut self) -> &mut DiffKey {
33        &mut self.key
34    }
35}
36
37impl Default for FloatingTab {
38    fn default() -> Self {
39        Self::new()
40    }
41}
42
43impl ChildrenExt for FloatingTab {
44    fn get_children(&mut self) -> &mut Vec<Element> {
45        &mut self.children
46    }
47}
48
49/// Floating Tab component.
50///
51/// # Example
52///
53/// ```rust
54/// # use freya::prelude::*;
55/// fn app() -> impl IntoElement {
56///     rect()
57///         .spacing(8.)
58///         .child(FloatingTab::new().child("Page 1"))
59///         .child(FloatingTab::new().child("Page 2"))
60/// }
61///
62/// # use freya_testing::prelude::*;
63/// # launch_doc(|| {
64/// #   rect().center().expanded().child(app())
65/// # }, "./images/gallery_floating_tab.png").with_hook(|t| { t.move_cursor((125., 115.)); t.sync_and_update(); }).with_scale_factor(1.).render();
66/// ```
67///
68/// # Preview
69/// ![FloatingTab Preview][floating_tab]
70#[cfg_attr(feature = "docs",
71    doc = embed_doc_image::embed_image!("floating_tab", "images/gallery_floating_tab.png")
72)]
73impl FloatingTab {
74    pub fn new() -> Self {
75        Self {
76            children: vec![],
77            theme: None,
78            on_press: None,
79            key: DiffKey::None,
80        }
81    }
82
83    /// Get the theme override for this component.
84    pub fn get_theme(&self) -> Option<&FloatingTabThemePartial> {
85        self.theme.as_ref()
86    }
87
88    /// Set a theme override for this component.
89    pub fn theme(mut self, theme: FloatingTabThemePartial) -> Self {
90        self.theme = Some(theme);
91        self
92    }
93}
94
95impl Component for FloatingTab {
96    fn render(&self) -> impl IntoElement {
97        let focus = use_focus();
98        let focus_status = use_focus_status(focus);
99        let mut status = use_state(TabStatus::default);
100        let is_active = use_activable_route();
101
102        let FloatingTabTheme {
103            background,
104            hover_background,
105            padding,
106            width,
107            height,
108            color,
109            corner_radius,
110        } = get_theme!(&self.theme, floating_tab);
111
112        let on_pointer_enter = move |_| {
113            Cursor::set(CursorIcon::Pointer);
114            status.set(TabStatus::Hovering);
115        };
116
117        let on_pointer_leave = move |_| {
118            Cursor::set(CursorIcon::default());
119            status.set(TabStatus::default());
120        };
121
122        let background = if focus_status() == FocusStatus::Keyboard
123            || is_active
124            || *status.read() == TabStatus::Hovering
125        {
126            hover_background
127        } else {
128            background
129        };
130
131        rect()
132            .a11y_id(focus.a11y_id())
133            .a11y_focusable(Focusable::Enabled)
134            .a11y_role(AccessibilityRole::Tab)
135            .on_pointer_enter(on_pointer_enter)
136            .on_pointer_leave(on_pointer_leave)
137            .map(self.on_press.clone(), |el, on_press| el.on_press(on_press))
138            .width(width)
139            .height(height)
140            .center()
141            .overflow(Overflow::Clip)
142            .padding(padding)
143            .background(background)
144            .color(color)
145            .corner_radius(corner_radius)
146            .children(self.children.clone())
147    }
148
149    fn render_key(&self) -> DiffKey {
150        self.key.clone().or(self.default_key())
151    }
152}