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 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}