text.rs

  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}