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 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> RenderOnce<V> for &'static str {
41 type Element = Self;
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 element_id(&self) -> Option<ElementId> {
52 Some(self.clone().into())
53 }
54
55 fn layout(
56 &mut self,
57 _: &mut V,
58 _: Option<Self::State>,
59 cx: &mut ViewContext<V>,
60 ) -> (LayoutId, Self::State) {
61 let mut state = TextState::default();
62 let layout_id = state.layout(self.clone(), None, cx);
63 (layout_id, state)
64 }
65
66 fn paint(
67 self,
68 bounds: Bounds<Pixels>,
69 _: &mut V,
70 state: &mut TextState,
71 cx: &mut ViewContext<V>,
72 ) {
73 let text_str: &str = self.as_ref();
74 state.paint(bounds, text_str, cx)
75 }
76}
77
78impl<V: 'static> RenderOnce<V> for SharedString {
79 type Element = Self;
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 element_id(&self) -> Option<crate::ElementId> {
109 None
110 }
111
112 fn layout(
113 &mut self,
114 _view: &mut V,
115 element_state: Option<Self::State>,
116 cx: &mut ViewContext<V>,
117 ) -> (LayoutId, Self::State) {
118 let element_state = element_state.unwrap_or_default();
119 let text_system = cx.text_system().clone();
120 let text_style = cx.text_style();
121 let font_size = text_style.font_size.to_pixels(cx.rem_size());
122 let line_height = text_style
123 .line_height
124 .to_pixels(font_size.into(), cx.rem_size());
125 let text = self.text.clone();
126
127 let rem_size = cx.rem_size();
128
129 let runs = if let Some(runs) = self.runs.take() {
130 runs
131 } else {
132 vec![text_style.to_run(text.len())]
133 };
134
135 let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
136 let element_state = element_state.clone();
137 move |known_dimensions, _| {
138 let Some(lines) = text_system
139 .shape_text(
140 &text,
141 font_size,
142 &runs[..],
143 known_dimensions.width, // Wrap if we know the width.
144 )
145 .log_err()
146 else {
147 element_state.lock().replace(TextStateInner {
148 lines: Default::default(),
149 line_height,
150 });
151 return Size::default();
152 };
153
154 let mut size: Size<Pixels> = Size::default();
155 for line in &lines {
156 let line_size = line.size(line_height);
157 size.height += line_size.height;
158 size.width = size.width.max(line_size.width);
159 }
160
161 element_state
162 .lock()
163 .replace(TextStateInner { lines, line_height });
164
165 size
166 }
167 });
168
169 (layout_id, element_state)
170 }
171
172 fn paint(
173 self,
174 bounds: Bounds<Pixels>,
175 _: &mut V,
176 element_state: &mut Self::State,
177 cx: &mut ViewContext<V>,
178 ) {
179 let element_state = element_state.lock();
180 let element_state = element_state
181 .as_ref()
182 .ok_or_else(|| anyhow!("measurement has not been performed on {}", &self.text))
183 .unwrap();
184
185 let line_height = element_state.line_height;
186 let mut line_origin = bounds.origin;
187 for line in &element_state.lines {
188 line.paint(line_origin, line_height, cx).log_err();
189 line_origin.y += line.size(line_height).height;
190 }
191 }
192}
193
194impl<V: 'static> RenderOnce<V> for StyledText {
195 type Element = Self;
196
197 fn render_once(self) -> Self::Element {
198 self
199 }
200}
201
202#[derive(Default, Clone)]
203pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
204
205struct TextStateInner {
206 lines: SmallVec<[WrappedLine; 1]>,
207 line_height: Pixels,
208}
209
210impl TextState {
211 fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
212 self.0.lock()
213 }
214
215 fn layout(
216 &mut self,
217 text: SharedString,
218 runs: Option<Vec<TextRun>>,
219 cx: &mut WindowContext,
220 ) -> LayoutId {
221 let text_system = cx.text_system().clone();
222 let text_style = cx.text_style();
223 let font_size = text_style.font_size.to_pixels(cx.rem_size());
224 let line_height = text_style
225 .line_height
226 .to_pixels(font_size.into(), cx.rem_size());
227 let text = SharedString::from(text);
228
229 let rem_size = cx.rem_size();
230
231 let runs = if let Some(runs) = runs {
232 runs
233 } else {
234 vec![text_style.to_run(text.len())]
235 };
236
237 let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
238 let element_state = self.clone();
239 move |known_dimensions, _| {
240 let Some(lines) = text_system
241 .shape_text(
242 &text,
243 font_size,
244 &runs[..],
245 known_dimensions.width, // Wrap if we know the width.
246 )
247 .log_err()
248 else {
249 element_state.lock().replace(TextStateInner {
250 lines: Default::default(),
251 line_height,
252 });
253 return Size::default();
254 };
255
256 let mut size: Size<Pixels> = Size::default();
257 for line in &lines {
258 let line_size = line.size(line_height);
259 size.height += line_size.height;
260 size.width = size.width.max(line_size.width);
261 }
262
263 element_state
264 .lock()
265 .replace(TextStateInner { lines, line_height });
266
267 size
268 }
269 });
270
271 layout_id
272 }
273
274 fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
275 let element_state = self.lock();
276 let element_state = element_state
277 .as_ref()
278 .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
279 .unwrap();
280
281 let line_height = element_state.line_height;
282 let mut line_origin = bounds.origin;
283 for line in &element_state.lines {
284 line.paint(line_origin, line_height, cx).log_err();
285 line_origin.y += line.size(line_height).height;
286 }
287 }
288}
289
290struct InteractiveText {
291 id: ElementId,
292 text: StyledText,
293}
294
295struct InteractiveTextState {
296 text_state: TextState,
297 clicked_range_ixs: Rc<Cell<SmallVec<[usize; 1]>>>,
298}
299
300impl<V: 'static> Element<V> for InteractiveText {
301 type State = InteractiveTextState;
302
303 fn element_id(&self) -> Option<ElementId> {
304 Some(self.id.clone())
305 }
306
307 fn layout(
308 &mut self,
309 view_state: &mut V,
310 element_state: Option<Self::State>,
311 cx: &mut ViewContext<V>,
312 ) -> (LayoutId, Self::State) {
313 if let Some(InteractiveTextState {
314 text_state,
315 clicked_range_ixs,
316 }) = element_state
317 {
318 let (layout_id, text_state) = self.text.layout(view_state, Some(text_state), cx);
319 let element_state = InteractiveTextState {
320 text_state,
321 clicked_range_ixs,
322 };
323 (layout_id, element_state)
324 } else {
325 let (layout_id, text_state) = self.text.layout(view_state, None, cx);
326 let element_state = InteractiveTextState {
327 text_state,
328 clicked_range_ixs: Rc::default(),
329 };
330 (layout_id, element_state)
331 }
332 }
333
334 fn paint(
335 self,
336 bounds: Bounds<Pixels>,
337 view_state: &mut V,
338 element_state: &mut Self::State,
339 cx: &mut ViewContext<V>,
340 ) {
341 self.text
342 .paint(bounds, view_state, &mut element_state.text_state, cx)
343 }
344}
345
346impl<V: 'static> RenderOnce<V> for InteractiveText {
347 type Element = Self;
348
349 fn render_once(self) -> Self::Element {
350 self
351 }
352}