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