1use gpui::{AnyElement, ScrollHandle, Stateful};
2use smallvec::SmallVec;
3
4use crate::prelude::*;
5
6#[derive(IntoElement)]
7pub struct TabBar {
8 id: ElementId,
9 start_children: SmallVec<[AnyElement; 2]>,
10 children: SmallVec<[AnyElement; 2]>,
11 end_children: SmallVec<[AnyElement; 2]>,
12 scroll_handle: Option<ScrollHandle>,
13}
14
15impl TabBar {
16 pub fn new(id: impl Into<ElementId>) -> Self {
17 Self {
18 id: id.into(),
19 start_children: SmallVec::new(),
20 children: SmallVec::new(),
21 end_children: SmallVec::new(),
22 scroll_handle: None,
23 }
24 }
25
26 pub fn track_scroll(mut self, scroll_handle: ScrollHandle) -> Self {
27 self.scroll_handle = Some(scroll_handle);
28 self
29 }
30
31 pub fn start_children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
32 &mut self.start_children
33 }
34
35 pub fn start_child(mut self, start_child: impl IntoElement) -> Self
36 where
37 Self: Sized,
38 {
39 self.start_children_mut()
40 .push(start_child.into_element().into_any());
41 self
42 }
43
44 pub fn start_children(
45 mut self,
46 start_children: impl IntoIterator<Item = impl IntoElement>,
47 ) -> Self
48 where
49 Self: Sized,
50 {
51 self.start_children_mut().extend(
52 start_children
53 .into_iter()
54 .map(|child| child.into_any_element()),
55 );
56 self
57 }
58
59 pub fn end_children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
60 &mut self.end_children
61 }
62
63 pub fn end_child(mut self, end_child: impl IntoElement) -> Self
64 where
65 Self: Sized,
66 {
67 self.end_children_mut()
68 .push(end_child.into_element().into_any());
69 self
70 }
71
72 pub fn end_children(mut self, end_children: impl IntoIterator<Item = impl IntoElement>) -> Self
73 where
74 Self: Sized,
75 {
76 self.end_children_mut().extend(
77 end_children
78 .into_iter()
79 .map(|child| child.into_any_element()),
80 );
81 self
82 }
83}
84
85impl ParentElement for TabBar {
86 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
87 &mut self.children
88 }
89}
90
91impl RenderOnce for TabBar {
92 type Rendered = Stateful<Div>;
93
94 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
95 const HEIGHT_IN_REMS: f32 = 30. / 16.;
96
97 div()
98 .id(self.id)
99 .group("tab_bar")
100 .flex()
101 .flex_none()
102 .w_full()
103 .h(rems(HEIGHT_IN_REMS))
104 .bg(cx.theme().colors().tab_bar_background)
105 .when(!self.start_children.is_empty(), |this| {
106 this.child(
107 h_stack()
108 .flex_none()
109 .gap_1()
110 .px_1()
111 .border_b()
112 .border_r()
113 .border_color(cx.theme().colors().border)
114 .children(self.start_children),
115 )
116 })
117 .child(
118 div()
119 .relative()
120 .flex_1()
121 .h_full()
122 .overflow_hidden_x()
123 .child(
124 div()
125 .absolute()
126 .top_0()
127 .left_0()
128 .z_index(1)
129 .size_full()
130 .border_b()
131 .border_color(cx.theme().colors().border),
132 )
133 .child(
134 h_stack()
135 .id("tabs")
136 .z_index(2)
137 .flex_grow()
138 .overflow_x_scroll()
139 .when_some(self.scroll_handle, |cx, scroll_handle| {
140 cx.track_scroll(&scroll_handle)
141 })
142 .children(self.children),
143 ),
144 )
145 .when(!self.end_children.is_empty(), |this| {
146 this.child(
147 h_stack()
148 .flex_none()
149 .gap_1()
150 .px_1()
151 .border_b()
152 .border_l()
153 .border_color(cx.theme().colors().border)
154 .children(self.end_children),
155 )
156 })
157 }
158}