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 IntoIterator<Item = AnyElement>) {
87 self.children.extend(elements)
88 }
89}
90
91impl RenderOnce for TabBar {
92 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
93 div()
94 .id(self.id)
95 .group("tab_bar")
96 .flex()
97 .flex_none()
98 .w_full()
99 .h(
100 // TODO: This should scale with [UiDensity], however tabs,
101 // and other tab bar tools need to scale dynamically first.
102 rems_from_px(29.),
103 )
104 .bg(cx.theme().colors().tab_bar_background)
105 .when(!self.start_children.is_empty(), |this| {
106 this.child(
107 h_flex()
108 .flex_none()
109 .gap(Spacing::Small.rems(cx))
110 .px(Spacing::Medium.rems(cx))
111 .border_b_1()
112 .border_r_1()
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_x_hidden()
123 .child(
124 div()
125 .absolute()
126 .top_0()
127 .left_0()
128 .size_full()
129 .border_b_1()
130 .border_color(cx.theme().colors().border),
131 )
132 .child(
133 h_flex()
134 .id("tabs")
135 .flex_grow()
136 .overflow_x_scroll()
137 .when_some(self.scroll_handle, |cx, scroll_handle| {
138 cx.track_scroll(&scroll_handle)
139 })
140 .children(self.children),
141 ),
142 )
143 .when(!self.end_children.is_empty(), |this| {
144 this.child(
145 h_flex()
146 .flex_none()
147 .gap(Spacing::Small.rems(cx))
148 .px(Spacing::Medium.rems(cx))
149 .border_b_1()
150 .border_l_1()
151 .border_color(cx.theme().colors().border)
152 .children(self.end_children),
153 )
154 })
155 }
156}