1use crate::{
2 AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels,
3 SharedString, Size, TextRun, ViewContext, WindowContext, WrappedLine,
4};
5use anyhow::anyhow;
6use parking_lot::{Mutex, MutexGuard};
7use smallvec::SmallVec;
8use std::{cell::Cell, rc::Rc, sync::Arc};
9use util::ResultExt;
10
11impl<V: 'static> Element<V> for &'static str {
12 type State = TextState;
13
14 fn element_id(&self) -> Option<ElementId> {
15 None
16 }
17
18 fn layout(
19 &mut self,
20 _: &mut V,
21 _: Option<Self::State>,
22 cx: &mut ViewContext<V>,
23 ) -> (LayoutId, Self::State) {
24 let mut state = TextState::default();
25 let layout_id = state.layout(SharedString::from(*self), None, cx);
26 (layout_id, state)
27 }
28
29 fn paint(
30 self,
31 bounds: Bounds<Pixels>,
32 _: &mut V,
33 state: &mut TextState,
34 cx: &mut ViewContext<V>,
35 ) {
36 state.paint(bounds, self, cx)
37 }
38}
39
40impl<V: 'static> Element<V> for SharedString {
41 type State = TextState;
42
43 fn element_id(&self) -> Option<ElementId> {
44 Some(self.clone().into())
45 }
46
47 fn layout(
48 &mut self,
49 _: &mut V,
50 _: Option<Self::State>,
51 cx: &mut ViewContext<V>,
52 ) -> (LayoutId, Self::State) {
53 let mut state = TextState::default();
54 let layout_id = state.layout(self.clone(), None, cx);
55 (layout_id, state)
56 }
57
58 fn paint(
59 self,
60 bounds: Bounds<Pixels>,
61 _: &mut V,
62 state: &mut TextState,
63 cx: &mut ViewContext<V>,
64 ) {
65 let text_str: &str = self.as_ref();
66 state.paint(bounds, text_str, cx)
67 }
68}
69
70pub struct Text {
71 text: SharedString,
72 runs: Option<Vec<TextRun>>,
73}
74
75impl Text {
76 /// Renders text with runs of different styles.
77 ///
78 /// Callers are responsible for setting the correct style for each run.
79 /// For text with a uniform style, you can usually avoid calling this constructor
80 /// and just pass text directly.
81 pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
82 Text {
83 text,
84 runs: Some(runs),
85 }
86 }
87}
88
89impl<V: 'static> Component<V> for Text {
90 fn render(self) -> AnyElement<V> {
91 AnyElement::new(self)
92 }
93}
94
95impl<V: 'static> Element<V> for Text {
96 type State = TextState;
97
98 fn element_id(&self) -> Option<crate::ElementId> {
99 None
100 }
101
102 fn layout(
103 &mut self,
104 _view: &mut V,
105 element_state: Option<Self::State>,
106 cx: &mut ViewContext<V>,
107 ) -> (LayoutId, Self::State) {
108 let element_state = element_state.unwrap_or_default();
109 let text_system = cx.text_system().clone();
110 let text_style = cx.text_style();
111 let font_size = text_style.font_size.to_pixels(cx.rem_size());
112 let line_height = text_style
113 .line_height
114 .to_pixels(font_size.into(), cx.rem_size());
115 let text = self.text.clone();
116
117 let rem_size = cx.rem_size();
118
119 let runs = if let Some(runs) = self.runs.take() {
120 runs
121 } else {
122 vec![text_style.to_run(text.len())]
123 };
124
125 let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
126 let element_state = element_state.clone();
127 move |known_dimensions, _| {
128 let Some(lines) = text_system
129 .shape_text(
130 &text,
131 font_size,
132 &runs[..],
133 known_dimensions.width, // Wrap if we know the width.
134 )
135 .log_err()
136 else {
137 element_state.lock().replace(TextStateInner {
138 lines: Default::default(),
139 line_height,
140 });
141 return Size::default();
142 };
143
144 let mut size: Size<Pixels> = Size::default();
145 for line in &lines {
146 let line_size = line.size(line_height);
147 size.height += line_size.height;
148 size.width = size.width.max(line_size.width);
149 }
150
151 element_state
152 .lock()
153 .replace(TextStateInner { lines, line_height });
154
155 size
156 }
157 });
158
159 (layout_id, element_state)
160 }
161
162 fn paint(
163 self,
164 bounds: Bounds<Pixels>,
165 _: &mut V,
166 element_state: &mut Self::State,
167 cx: &mut ViewContext<V>,
168 ) {
169 let element_state = element_state.lock();
170 let element_state = element_state
171 .as_ref()
172 .ok_or_else(|| anyhow!("measurement has not been performed on {}", &self.text))
173 .unwrap();
174
175 let line_height = element_state.line_height;
176 let mut line_origin = bounds.origin;
177 for line in &element_state.lines {
178 line.paint(line_origin, line_height, cx).log_err();
179 line_origin.y += line.size(line_height).height;
180 }
181 }
182}
183
184#[derive(Default, Clone)]
185pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
186
187impl TextState {
188 fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
189 self.0.lock()
190 }
191
192 fn layout(
193 &mut self,
194 text: SharedString,
195 runs: Option<Vec<TextRun>>,
196 cx: &mut WindowContext,
197 ) -> LayoutId {
198 let text_system = cx.text_system().clone();
199 let text_style = cx.text_style();
200 let font_size = text_style.font_size.to_pixels(cx.rem_size());
201 let line_height = text_style
202 .line_height
203 .to_pixels(font_size.into(), cx.rem_size());
204 let text = SharedString::from(text);
205
206 let rem_size = cx.rem_size();
207
208 let runs = if let Some(runs) = runs {
209 runs
210 } else {
211 vec![text_style.to_run(text.len())]
212 };
213
214 let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
215 let element_state = self.clone();
216 move |known_dimensions, _| {
217 let Some(lines) = text_system
218 .shape_text(
219 &text,
220 font_size,
221 &runs[..],
222 known_dimensions.width, // Wrap if we know the width.
223 )
224 .log_err()
225 else {
226 element_state.lock().replace(TextStateInner {
227 lines: Default::default(),
228 line_height,
229 });
230 return Size::default();
231 };
232
233 let mut size: Size<Pixels> = Size::default();
234 for line in &lines {
235 let line_size = line.size(line_height);
236 size.height += line_size.height;
237 size.width = size.width.max(line_size.width);
238 }
239
240 element_state
241 .lock()
242 .replace(TextStateInner { lines, line_height });
243
244 size
245 }
246 });
247
248 layout_id
249 }
250
251 fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
252 let element_state = self.lock();
253 let element_state = element_state
254 .as_ref()
255 .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
256 .unwrap();
257
258 let line_height = element_state.line_height;
259 let mut line_origin = bounds.origin;
260 for line in &element_state.lines {
261 line.paint(line_origin, line_height, cx).log_err();
262 line_origin.y += line.size(line_height).height;
263 }
264 }
265}
266
267struct TextStateInner {
268 lines: SmallVec<[WrappedLine; 1]>,
269 line_height: Pixels,
270}
271
272struct InteractiveText {
273 id: ElementId,
274 text: Text,
275}
276
277struct InteractiveTextState {
278 text_state: TextState,
279 clicked_range_ixs: Rc<Cell<SmallVec<[usize; 1]>>>,
280}
281
282impl<V: 'static> Element<V> for InteractiveText {
283 type State = InteractiveTextState;
284
285 fn element_id(&self) -> Option<ElementId> {
286 Some(self.id.clone())
287 }
288
289 fn layout(
290 &mut self,
291 view_state: &mut V,
292 element_state: Option<Self::State>,
293 cx: &mut ViewContext<V>,
294 ) -> (LayoutId, Self::State) {
295 if let Some(InteractiveTextState {
296 text_state,
297 clicked_range_ixs,
298 }) = element_state
299 {
300 let (layout_id, text_state) = self.text.layout(view_state, Some(text_state), cx);
301 let element_state = InteractiveTextState {
302 text_state,
303 clicked_range_ixs,
304 };
305 (layout_id, element_state)
306 } else {
307 let (layout_id, text_state) = self.text.layout(view_state, None, cx);
308 let element_state = InteractiveTextState {
309 text_state,
310 clicked_range_ixs: Rc::default(),
311 };
312 (layout_id, element_state)
313 }
314 }
315
316 fn paint(
317 self,
318 bounds: Bounds<Pixels>,
319 view_state: &mut V,
320 element_state: &mut Self::State,
321 cx: &mut ViewContext<V>,
322 ) {
323 self.text
324 .paint(bounds, view_state, &mut element_state.text_state, cx)
325 }
326}
327
328impl<V: 'static> Component<V> for SharedString {
329 fn render(self) -> AnyElement<V> {
330 Text {
331 text: self,
332 runs: None,
333 }
334 .render()
335 }
336}
337
338impl<V: 'static> Component<V> for &'static str {
339 fn render(self) -> AnyElement<V> {
340 Text {
341 text: self.into(),
342 runs: None,
343 }
344 .render()
345 }
346}
347
348// TODO: Figure out how to pass `String` to `child` without this.
349// This impl doesn't exist in the `gpui2` crate.
350impl<V: 'static> Component<V> for String {
351 fn render(self) -> AnyElement<V> {
352 Text {
353 text: self.into(),
354 runs: None,
355 }
356 .render()
357 }
358}