text.rs

  1use crate::{
  2    AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, Line, Pixels, SharedString,
  3    Size, TextRun, ViewContext,
  4};
  5use parking_lot::Mutex;
  6use smallvec::SmallVec;
  7use std::{marker::PhantomData, sync::Arc};
  8use util::ResultExt;
  9
 10impl<V: 'static> Component<V> for SharedString {
 11    fn render(self) -> AnyElement<V> {
 12        Text {
 13            text: self,
 14            runs: None,
 15            state_type: PhantomData,
 16        }
 17        .render()
 18    }
 19}
 20
 21impl<V: 'static> Component<V> for &'static str {
 22    fn render(self) -> AnyElement<V> {
 23        Text {
 24            text: self.into(),
 25            runs: None,
 26            state_type: PhantomData,
 27        }
 28        .render()
 29    }
 30}
 31
 32// TODO: Figure out how to pass `String` to `child` without this.
 33// This impl doesn't exist in the `gpui2` crate.
 34impl<V: 'static> Component<V> for String {
 35    fn render(self) -> AnyElement<V> {
 36        Text {
 37            text: self.into(),
 38            runs: None,
 39            state_type: PhantomData,
 40        }
 41        .render()
 42    }
 43}
 44
 45pub struct Text<V> {
 46    text: SharedString,
 47    runs: Option<Vec<TextRun>>,
 48    state_type: PhantomData<V>,
 49}
 50
 51impl<V: 'static> Text<V> {
 52    /// styled renders text that has different runs of different styles.
 53    /// callers are responsible for setting the correct style for each run.
 54    ////
 55    /// For uniform text you can usually just pass a string as a child, and
 56    /// cx.text_style() will be used automatically.
 57    pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
 58        Text {
 59            text,
 60            runs: Some(runs),
 61            state_type: Default::default(),
 62        }
 63    }
 64}
 65
 66impl<V: 'static> Component<V> for Text<V> {
 67    fn render(self) -> AnyElement<V> {
 68        AnyElement::new(self)
 69    }
 70}
 71
 72impl<V: 'static> Element<V> for Text<V> {
 73    type ElementState = Arc<Mutex<Option<TextElementState>>>;
 74
 75    fn element_id(&self) -> Option<crate::ElementId> {
 76        None
 77    }
 78
 79    fn initialize(
 80        &mut self,
 81        _view_state: &mut V,
 82        element_state: Option<Self::ElementState>,
 83        _cx: &mut ViewContext<V>,
 84    ) -> Self::ElementState {
 85        element_state.unwrap_or_default()
 86    }
 87
 88    fn layout(
 89        &mut self,
 90        _view: &mut V,
 91        element_state: &mut Self::ElementState,
 92        cx: &mut ViewContext<V>,
 93    ) -> LayoutId {
 94        let text_system = cx.text_system().clone();
 95        let text_style = cx.text_style();
 96        let font_size = text_style.font_size.to_pixels(cx.rem_size());
 97        let line_height = text_style
 98            .line_height
 99            .to_pixels(font_size.into(), cx.rem_size());
100        let text = self.text.clone();
101
102        let rem_size = cx.rem_size();
103
104        let runs = if let Some(runs) = self.runs.take() {
105            runs
106        } else {
107            vec![text_style.to_run(text.len())]
108        };
109
110        let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
111            let element_state = element_state.clone();
112            move |known_dimensions, _| {
113                let Some(lines) = text_system
114                    .layout_text(
115                        &text,
116                        font_size,
117                        &runs[..],
118                        known_dimensions.width, // Wrap if we know the width.
119                    )
120                    .log_err()
121                else {
122                    element_state.lock().replace(TextElementState {
123                        lines: Default::default(),
124                        line_height,
125                    });
126                    return Size::default();
127                };
128
129                let line_count = lines
130                    .iter()
131                    .map(|line| line.wrap_count() + 1)
132                    .sum::<usize>();
133                let size = Size {
134                    width: lines
135                        .iter()
136                        .map(|line| line.layout.width)
137                        .max()
138                        .unwrap()
139                        .ceil(),
140                    height: line_height * line_count,
141                };
142
143                element_state
144                    .lock()
145                    .replace(TextElementState { lines, line_height });
146
147                size
148            }
149        });
150
151        layout_id
152    }
153
154    fn paint(
155        &mut self,
156        bounds: Bounds<Pixels>,
157        _: &mut V,
158        element_state: &mut Self::ElementState,
159        cx: &mut ViewContext<V>,
160    ) {
161        let element_state = element_state.lock();
162        let element_state = element_state
163            .as_ref()
164            .ok_or_else(|| anyhow::anyhow!("measurement has not been performed on {}", &self.text))
165            .unwrap();
166
167        let line_height = element_state.line_height;
168        let mut line_origin = bounds.origin;
169        for line in &element_state.lines {
170            line.paint(line_origin, line_height, cx).log_err();
171            line_origin.y += line.size(line_height).height;
172        }
173    }
174}
175
176pub struct TextElementState {
177    lines: SmallVec<[Line; 1]>,
178    line_height: Pixels,
179}