Skip to main content

ui/tabs/
bar.rs

1//! The shared scroll surface for plain and reorderable tab strips.
2
3use gpui::{
4    AnyElement, App, Axis, Div, ElementId, Interactivity, IntoElement, Refineable, RenderOnce,
5    ScrollHandle, Stateful, StyleRefinement, Window, div, prelude::*, px,
6};
7
8use crate::scroll;
9
10#[derive(IntoElement)]
11pub struct TabBar {
12    id: ElementId,
13    row: Stateful<Div>,
14    viewport: Option<AnyElement>,
15    handle: Option<ScrollHandle>,
16    style: StyleRefinement,
17}
18
19impl TabBar {
20    pub(super) fn new(id: ElementId) -> Self {
21        Self {
22            id,
23            row: div()
24                .id("tab-row")
25                .min_w_0()
26                .flex()
27                .flex_row()
28                .items_center()
29                .gap(px(super::GAP))
30                .overflow_x_scroll(),
31            viewport: None,
32            handle: None,
33            style: StyleRefinement::default(),
34        }
35    }
36
37    /// Optional access for programmatic scrolling. Otherwise the bar keeps its own handle.
38    pub fn track_scroll(mut self, handle: &ScrollHandle) -> Self {
39        self.handle = Some(handle.clone());
40        self
41    }
42
43    pub(super) fn viewport(mut self, viewport: AnyElement, handle: &ScrollHandle) -> Self {
44        self.viewport = Some(viewport);
45        self.track_scroll(handle)
46    }
47}
48
49impl ParentElement for TabBar {
50    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
51        self.row.extend(elements);
52    }
53}
54
55impl Styled for TabBar {
56    fn style(&mut self) -> &mut StyleRefinement {
57        &mut self.style
58    }
59}
60
61impl InteractiveElement for TabBar {
62    fn interactivity(&mut self) -> &mut Interactivity {
63        self.row.interactivity()
64    }
65}
66
67impl StatefulInteractiveElement for TabBar {}
68
69impl RenderOnce for TabBar {
70    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
71        let handle = self.handle.unwrap_or_else(|| {
72            window
73                .use_keyed_state(self.id.clone(), cx, |_, _| ScrollHandle::new())
74                .read(cx)
75                .clone()
76        });
77        let viewport = self
78            .viewport
79            .unwrap_or_else(|| self.row.flex_1().track_scroll(&handle).into_any_element());
80        let mut frame = div()
81            .id(self.id)
82            .relative()
83            .min_w_0()
84            .min_h_0()
85            .flex()
86            .flex_1();
87        frame.style().refine(&self.style);
88        frame.child(viewport).child(scroll::Overlay::new(
89            "tab-scrollbar",
90            &handle,
91            Axis::Horizontal,
92        ))
93    }
94}