tab_bar.rs

  1use crate::{prelude::*, Icon, IconButton, Tab};
  2use gpui::prelude::*;
  3use gpui::Div;
  4use gpui::Stateful;
  5
  6#[derive(RenderOnce)]
  7pub struct TabBar {
  8    id: ElementId,
  9    /// Backwards, Forwards
 10    can_navigate: (bool, bool),
 11    tabs: Vec<Tab>,
 12}
 13
 14impl<V: 'static> Component<V> for TabBar {
 15    type Rendered = Stateful<V, Div<V>>;
 16
 17    fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
 18        let (can_navigate_back, can_navigate_forward) = self.can_navigate;
 19
 20        div()
 21            .group("tab_bar")
 22            .id(self.id.clone())
 23            .w_full()
 24            .flex()
 25            .bg(cx.theme().colors().tab_bar_background)
 26            // Left Side
 27            .child(
 28                div()
 29                    .relative()
 30                    .px_1()
 31                    .flex()
 32                    .flex_none()
 33                    .gap_2()
 34                    // Nav Buttons
 35                    .child(
 36                        div()
 37                            .right_0()
 38                            .flex()
 39                            .items_center()
 40                            .gap_px()
 41                            .child(
 42                                IconButton::new("arrow_left", Icon::ArrowLeft)
 43                                    .state(InteractionState::Enabled.if_enabled(can_navigate_back)),
 44                            )
 45                            .child(
 46                                IconButton::new("arrow_right", Icon::ArrowRight).state(
 47                                    InteractionState::Enabled.if_enabled(can_navigate_forward),
 48                                ),
 49                            ),
 50                    ),
 51            )
 52            .child(
 53                div().w_0().flex_1().h_full().child(
 54                    div()
 55                        .id("tabs")
 56                        .flex()
 57                        .overflow_x_scroll()
 58                        .children(self.tabs.clone()),
 59                ),
 60            )
 61            // Right Side
 62            .child(
 63                div()
 64                    // We only use absolute here since we don't
 65                    // have opacity or `hidden()` yet
 66                    .absolute()
 67                    .neg_top_7()
 68                    .px_1()
 69                    .flex()
 70                    .flex_none()
 71                    .gap_2()
 72                    .group_hover("tab_bar", |this| this.top_0())
 73                    // Nav Buttons
 74                    .child(
 75                        div()
 76                            .flex()
 77                            .items_center()
 78                            .gap_px()
 79                            .child(IconButton::new("plus", Icon::Plus))
 80                            .child(IconButton::new("split", Icon::Split)),
 81                    ),
 82            )
 83    }
 84}
 85
 86impl TabBar {
 87    pub fn new(id: impl Into<ElementId>, tabs: Vec<Tab>) -> Self {
 88        Self {
 89            id: id.into(),
 90            can_navigate: (false, false),
 91            tabs,
 92        }
 93    }
 94
 95    pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
 96        self.can_navigate = can_navigate;
 97        self
 98    }
 99}
100
101use gpui::ElementId;
102#[cfg(feature = "stories")]
103pub use stories::*;
104
105#[cfg(feature = "stories")]
106mod stories {
107    use super::*;
108    use crate::Story;
109    use gpui::{Div, Render};
110
111    pub struct TabBarStory;
112
113    impl Render<Self> for TabBarStory {
114        type Element = Div<Self>;
115
116        fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
117            Story::container(cx)
118                .child(Story::title_for::<_, TabBar>(cx))
119                .child(Story::label(cx, "Default"))
120                .child(TabBar::new(
121                    "tab-bar",
122                    vec![
123                        Tab::new(1)
124                            .title("Cargo.toml".to_string())
125                            .current(false)
126                            .git_status(GitStatus::Modified),
127                        Tab::new(2)
128                            .title("Channels Panel".to_string())
129                            .current(false),
130                        Tab::new(3)
131                            .title("channels_panel.rs".to_string())
132                            .current(true)
133                            .git_status(GitStatus::Modified),
134                        Tab::new(4)
135                            .title("workspace.rs".to_string())
136                            .current(false)
137                            .git_status(GitStatus::Modified),
138                        Tab::new(5)
139                            .title("icon_button.rs".to_string())
140                            .current(false),
141                        Tab::new(6)
142                            .title("storybook.rs".to_string())
143                            .current(false)
144                            .git_status(GitStatus::Created),
145                        Tab::new(7).title("theme.rs".to_string()).current(false),
146                        Tab::new(8)
147                            .title("theme_registry.rs".to_string())
148                            .current(false),
149                        Tab::new(9)
150                            .title("styleable_helpers.rs".to_string())
151                            .current(false),
152                    ],
153                ))
154        }
155    }
156}