Skip to content
Snippets Groups Projects
tab.rs 3.76 KiB
Newer Older
MrGVSV's avatar
MrGVSV committed
use kayak_ui::{
    core::{
        render_command::RenderCommand,
MrGVSV's avatar
MrGVSV committed
        rsx,
MrGVSV's avatar
MrGVSV committed
        styles::{Edge, LayoutType, Style, StyleProp, Units},
MrGVSV's avatar
MrGVSV committed
        use_state, widget, Bound, EventType, OnEvent, WidgetProps,
MrGVSV's avatar
MrGVSV committed
    },
    widgets::{Background, Text},
};

use crate::TabTheme;

#[derive(Clone, PartialEq)]
enum TabHoverState {
    None,
    Inactive,
    Active,
}

MrGVSV's avatar
MrGVSV committed
#[derive(WidgetProps, Default, Debug, PartialEq, Clone)]
pub struct TabProps {
    pub content: String,
    pub selected: bool,
    #[prop_field(Styles)]
    pub styles: Option<Style>,
    #[prop_field(OnEvent)]
    pub on_event: Option<OnEvent>,
}

/// The actual tab, displayed in a [TabBar](crate::tab_bar::TabBar)
MrGVSV's avatar
MrGVSV committed
#[widget]
MrGVSV's avatar
MrGVSV committed
pub fn Tab(props: TabProps) {
MrGVSV's avatar
MrGVSV committed
    let TabProps {
        content, selected, ..
    } = props.clone();
MrGVSV's avatar
MrGVSV committed
    let theme = context.create_consumer::<TabTheme>().unwrap_or_default();
    let (focus_state, set_focus_state, ..) = use_state!(false);
MrGVSV's avatar
MrGVSV committed
    let (hover_state, set_hover_state, ..) = use_state!(TabHoverState::None);
    match hover_state {
        TabHoverState::Inactive if selected => set_hover_state(TabHoverState::Active),
        TabHoverState::Active if !selected => set_hover_state(TabHoverState::Inactive),
        _ => {}
    };

MrGVSV's avatar
MrGVSV committed
    let event_handler = OnEvent::new(move |_, event| match event.event_type {
MrGVSV's avatar
MrGVSV committed
        EventType::Hover(..) => {
MrGVSV's avatar
MrGVSV committed
            if selected {
                set_hover_state(TabHoverState::Active);
            } else {
                set_hover_state(TabHoverState::Inactive);
MrGVSV's avatar
MrGVSV committed
            }
        }
MrGVSV's avatar
MrGVSV committed
        EventType::MouseOut(..) => {
MrGVSV's avatar
MrGVSV committed
            set_hover_state(TabHoverState::None);
        }
        EventType::Focus => {
            set_focus_state(true);
        }
        EventType::Blur => {
            set_focus_state(false);
        }
        _ => {}
MrGVSV's avatar
MrGVSV committed
    });

    let tab_color = match hover_state {
        TabHoverState::None if selected => theme.get().active_tab.normal,
        TabHoverState::None => theme.get().inactive_tab.normal,
        TabHoverState::Inactive => theme.get().inactive_tab.hovered,
        TabHoverState::Active => theme.get().active_tab.hovered,
    };

    let pad_x = Units::Pixels(2.0);
    let bg_styles = Style {
        background_color: StyleProp::Value(tab_color),
        layout_type: StyleProp::Value(LayoutType::Row),
        padding_left: StyleProp::Value(pad_x),
        padding_right: StyleProp::Value(pad_x),
        ..Default::default()
    };

    let border_width = Units::Pixels(2.0);
    let border_styles = Style {
        background_color: if focus_state {
            StyleProp::Value(theme.get().focus)
        } else {
            StyleProp::Value(tab_color)
        },
MrGVSV's avatar
MrGVSV committed
        padding: StyleProp::Value(Edge::all(border_width)),
MrGVSV's avatar
MrGVSV committed
        layout_type: StyleProp::Value(LayoutType::Row),
        ..Default::default()
    };

    let text_styles = Style {
        background_color: if focus_state {
            StyleProp::Value(theme.get().focus)
        } else {
            StyleProp::Value(tab_color)
        },
        color: StyleProp::Value(theme.get().text.normal),
        top: StyleProp::Value(Units::Stretch(0.1)),
        bottom: StyleProp::Value(Units::Stretch(1.0)),
        width: StyleProp::Value(Units::Stretch(1.0)),
        ..Default::default()
    };

MrGVSV's avatar
MrGVSV committed
    props.styles = Some(Style {
MrGVSV's avatar
MrGVSV committed
        render_command: StyleProp::Value(RenderCommand::Layout),
        height: StyleProp::Value(Units::Pixels(theme.get().tab_height)),
        max_width: StyleProp::Value(Units::Pixels(100.0)),
MrGVSV's avatar
MrGVSV committed
        ..props.styles.clone().unwrap_or_default()
MrGVSV's avatar
MrGVSV committed
    });

    rsx! {
        <Background focusable={Some(true)} on_event={Some(event_handler)} styles={Some(border_styles)}>
            <Background styles={Some(bg_styles)}>
                <Text content={content} size={12.0} styles={Some(text_styles)} />
            </Background>
        </Background>
    }
MrGVSV's avatar
MrGVSV committed
}