1use crate::prelude::*;
2use crate::{theme, Icon, IconColor, IconElement, Label, LabelColor};
3
4#[derive(Element, Clone)]
5pub struct Tab {
6 title: String,
7 icon: Option<Icon>,
8 current: bool,
9 dirty: bool,
10 fs_status: FileSystemStatus,
11 git_status: GitStatus,
12 diagnostic_status: DiagnosticStatus,
13 close_side: IconSide,
14}
15
16impl Tab {
17 pub fn new() -> Self {
18 Self {
19 title: "untitled".to_string(),
20 icon: None,
21 current: false,
22 dirty: false,
23 fs_status: FileSystemStatus::None,
24 git_status: GitStatus::None,
25 diagnostic_status: DiagnosticStatus::None,
26 close_side: IconSide::Right,
27 }
28 }
29
30 pub fn current(mut self, current: bool) -> Self {
31 self.current = current;
32 self
33 }
34
35 pub fn title(mut self, title: String) -> Self {
36 self.title = title;
37 self
38 }
39
40 pub fn icon<I>(mut self, icon: I) -> Self
41 where
42 I: Into<Option<Icon>>,
43 {
44 self.icon = icon.into();
45 self
46 }
47
48 pub fn dirty(mut self, dirty: bool) -> Self {
49 self.dirty = dirty;
50 self
51 }
52
53 pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
54 self.fs_status = fs_status;
55 self
56 }
57
58 pub fn git_status(mut self, git_status: GitStatus) -> Self {
59 self.git_status = git_status;
60 self
61 }
62
63 pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
64 self.diagnostic_status = diagnostic_status;
65 self
66 }
67
68 pub fn close_side(mut self, close_side: IconSide) -> Self {
69 self.close_side = close_side;
70 self
71 }
72
73 fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
74 let theme = theme(cx);
75 let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
76 let is_deleted = self.fs_status == FileSystemStatus::Deleted;
77
78 let label = match (self.git_status, is_deleted) {
79 (_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone())
80 .color(LabelColor::Hidden)
81 .set_strikethrough(true),
82 (GitStatus::None, false) => Label::new(self.title.clone()),
83 (GitStatus::Created, false) => {
84 Label::new(self.title.clone()).color(LabelColor::Created)
85 }
86 (GitStatus::Modified, false) => {
87 Label::new(self.title.clone()).color(LabelColor::Modified)
88 }
89 (GitStatus::Renamed, false) => Label::new(self.title.clone()).color(LabelColor::Accent),
90 (GitStatus::Conflict, false) => Label::new(self.title.clone()),
91 };
92
93 let close_icon = IconElement::new(Icon::Close).color(IconColor::Muted);
94
95 div()
96 .px_2()
97 .py_0p5()
98 .flex()
99 .items_center()
100 .justify_center()
101 .fill(if self.current {
102 theme.highest.base.default.background
103 } else {
104 theme.middle.base.default.background
105 })
106 .child(
107 div()
108 .px_1()
109 .flex()
110 .items_center()
111 .gap_1()
112 .children(has_fs_conflict.then(|| {
113 IconElement::new(Icon::ExclamationTriangle)
114 .size(crate::IconSize::Small)
115 .color(IconColor::Warning)
116 }))
117 .children(self.icon.map(IconElement::new))
118 .children(if self.close_side == IconSide::Left {
119 Some(close_icon.clone())
120 } else {
121 None
122 })
123 .child(label)
124 .children(if self.close_side == IconSide::Right {
125 Some(close_icon)
126 } else {
127 None
128 }),
129 )
130 }
131}