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