1use gpui::{AnyElement, ScrollHandle};
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 extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
87 self.children.extend(elements)
88 }
89}
90
91impl RenderOnce for TabBar {
92 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
93 const HEIGHT_IN_REMS: f32 = 29. / 16.;
94
95 div()
96 .id(self.id)
97 .group("tab_bar")
98 .flex()
99 .flex_none()
100 .w_full()
101 .h(rems(HEIGHT_IN_REMS))
102 .bg(cx.theme().colors().tab_bar_background)
103 .when(!self.start_children.is_empty(), |this| {
104 this.child(
105 h_flex()
106 .flex_none()
107 .gap_1()
108 .px_1()
109 .border_b()
110 .border_r()
111 .border_color(cx.theme().colors().border)
112 .children(self.start_children),
113 )
114 })
115 .child(
116 div()
117 .relative()
118 .flex_1()
119 .h_full()
120 .overflow_x_hidden()
121 .child(
122 div()
123 .absolute()
124 .top_0()
125 .left_0()
126 .size_full()
127 .border_b()
128 .border_color(cx.theme().colors().border),
129 )
130 .child(
131 h_flex()
132 .id("tabs")
133 .flex_grow()
134 .overflow_x_scroll()
135 .when_some(self.scroll_handle, |cx, scroll_handle| {
136 cx.track_scroll(&scroll_handle)
137 })
138 .children(self.children),
139 ),
140 )
141 .when(!self.end_children.is_empty(), |this| {
142 this.child(
143 h_flex()
144 .flex_none()
145 .gap_1()
146 .px_1()
147 .border_b()
148 .border_l()
149 .border_color(cx.theme().colors().border)
150 .children(self.end_children),
151 )
152 })
153 }
154}