1use gpui2::{Hsla, WindowContext};
2
3use crate::prelude::*;
4use crate::{h_stack, theme, v_stack, Icon, IconElement};
5
6#[derive(Default, PartialEq, Copy, Clone)]
7pub struct PlayerCursor {
8 color: Hsla,
9 index: usize,
10}
11
12#[derive(Default, PartialEq, Clone)]
13pub struct HighlightedText {
14 pub text: String,
15 pub color: Hsla,
16}
17
18#[derive(Default, PartialEq, Clone)]
19pub struct HighlightedLine {
20 pub highlighted_texts: Vec<HighlightedText>,
21}
22
23#[derive(Default, PartialEq, Clone)]
24pub struct BufferRow {
25 pub line_number: usize,
26 pub code_action: bool,
27 pub current: bool,
28 pub line: Option<HighlightedLine>,
29 pub cursors: Option<Vec<PlayerCursor>>,
30 pub status: GitStatus,
31 pub show_line_number: bool,
32}
33
34#[derive(Clone)]
35pub struct BufferRows {
36 pub show_line_numbers: bool,
37 pub rows: Vec<BufferRow>,
38}
39
40impl Default for BufferRows {
41 fn default() -> Self {
42 Self {
43 show_line_numbers: true,
44 rows: vec![BufferRow {
45 line_number: 1,
46 code_action: false,
47 current: true,
48 line: None,
49 cursors: None,
50 status: GitStatus::None,
51 show_line_number: true,
52 }],
53 }
54 }
55}
56
57impl BufferRow {
58 pub fn new(line_number: usize) -> Self {
59 Self {
60 line_number,
61 code_action: false,
62 current: false,
63 line: None,
64 cursors: None,
65 status: GitStatus::None,
66 show_line_number: true,
67 }
68 }
69
70 pub fn set_line(mut self, line: Option<HighlightedLine>) -> Self {
71 self.line = line;
72 self
73 }
74
75 pub fn set_cursors(mut self, cursors: Option<Vec<PlayerCursor>>) -> Self {
76 self.cursors = cursors;
77 self
78 }
79
80 pub fn add_cursor(mut self, cursor: PlayerCursor) -> Self {
81 if let Some(cursors) = &mut self.cursors {
82 cursors.push(cursor);
83 } else {
84 self.cursors = Some(vec![cursor]);
85 }
86 self
87 }
88
89 pub fn set_status(mut self, status: GitStatus) -> Self {
90 self.status = status;
91 self
92 }
93
94 pub fn set_show_line_number(mut self, show_line_number: bool) -> Self {
95 self.show_line_number = show_line_number;
96 self
97 }
98
99 pub fn set_code_action(mut self, code_action: bool) -> Self {
100 self.code_action = code_action;
101 self
102 }
103
104 pub fn set_current(mut self, current: bool) -> Self {
105 self.current = current;
106 self
107 }
108}
109
110#[derive(Element, Clone)]
111pub struct Buffer {
112 scroll_state: ScrollState,
113 rows: Option<BufferRows>,
114 readonly: bool,
115 language: Option<String>,
116 title: Option<String>,
117 path: Option<String>,
118}
119
120impl Buffer {
121 pub fn new() -> Self {
122 Self {
123 scroll_state: ScrollState::default(),
124 rows: Some(BufferRows::default()),
125 readonly: false,
126 language: None,
127 title: Some("untitled".to_string()),
128 path: None,
129 }
130 }
131
132 pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) {
133 self.scroll_state = scroll_state;
134 }
135
136 pub fn set_title<T: Into<Option<String>>>(mut self, title: T) -> Self {
137 self.title = title.into();
138 self
139 }
140
141 pub fn set_path<P: Into<Option<String>>>(mut self, path: P) -> Self {
142 self.path = path.into();
143 self
144 }
145
146 pub fn set_readonly(mut self, readonly: bool) -> Self {
147 self.readonly = readonly;
148 self
149 }
150
151 pub fn set_rows<R: Into<Option<BufferRows>>>(mut self, rows: R) -> Self {
152 self.rows = rows.into();
153 self
154 }
155
156 pub fn set_language<L: Into<Option<String>>>(mut self, language: L) -> Self {
157 self.language = language.into();
158 self
159 }
160
161 fn render_row<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl IntoElement<V> {
162 let theme = theme(cx);
163 let system_color = SystemColor::new();
164
165 let line_background = if row.current {
166 theme.middle.base.default.background
167 } else {
168 system_color.transparent
169 };
170
171 let line_number_color = if row.current {
172 HighlightColor::Default.hsla(&theme)
173 } else {
174 HighlightColor::Comment.hsla(&theme)
175 };
176
177 h_stack()
178 .fill(line_background)
179 .w_full()
180 .gap_2()
181 .px_1()
182 .child(
183 h_stack()
184 .w_4()
185 .h_full()
186 .px_0p5()
187 .when(row.code_action, |c| {
188 div().child(IconElement::new(Icon::Bolt))
189 }),
190 )
191 .when(row.show_line_number, |this| {
192 this.child(
193 h_stack().justify_end().px_0p5().w_3().child(
194 div()
195 .text_color(line_number_color)
196 .child(row.line_number.to_string()),
197 ),
198 )
199 })
200 .child(div().mx_0p5().w_1().h_full().fill(row.status.hsla(cx)))
201 .children(row.line.map(|line| {
202 div()
203 .flex()
204 .children(line.highlighted_texts.iter().map(|highlighted_text| {
205 div()
206 .text_color(highlighted_text.color)
207 .child(highlighted_text.text.clone())
208 }))
209 }))
210 }
211
212 fn render_rows<V: 'static>(&self, cx: &WindowContext) -> Vec<impl IntoElement<V>> {
213 match &self.rows {
214 Some(rows) => rows
215 .rows
216 .iter()
217 .map(|row| Self::render_row(row.clone(), cx))
218 .collect(),
219 None => vec![],
220 }
221 }
222
223 fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
224 let theme = theme(cx);
225 let rows = self.render_rows(cx);
226 v_stack()
227 .flex_1()
228 .w_full()
229 .h_full()
230 .fill(theme.highest.base.default.background)
231 .children(rows)
232 }
233}