text.rs

  1use crate::{
  2    AnyElement, BorrowWindow, Bounds, Element, IntoAnyElement, LayoutId, Line, Pixels,
  3    SharedString, Size, ViewContext,
  4};
  5use parking_lot::Mutex;
  6use smallvec::SmallVec;
  7use std::{marker::PhantomData, sync::Arc};
  8use util::ResultExt;
  9
 10impl<S: 'static + Send + Sync> IntoAnyElement<S> for SharedString {
 11    fn into_any(self) -> AnyElement<S> {
 12        Text {
 13            text: self,
 14            state_type: PhantomData,
 15        }
 16        .into_any()
 17    }
 18}
 19
 20impl<V: 'static + Send + Sync> IntoAnyElement<V> for &'static str {
 21    fn into_any(self) -> AnyElement<V> {
 22        Text {
 23            text: self.into(),
 24            state_type: PhantomData,
 25        }
 26        .into_any()
 27    }
 28}
 29
 30// TODO: Figure out how to pass `String` to `child` without this.
 31// This impl doesn't exist in the `gpui2` crate.
 32impl<S: 'static + Send + Sync> IntoAnyElement<S> for String {
 33    fn into_any(self) -> AnyElement<S> {
 34        Text {
 35            text: self.into(),
 36            state_type: PhantomData,
 37        }
 38        .into_any()
 39    }
 40}
 41
 42pub struct Text<S> {
 43    text: SharedString,
 44    state_type: PhantomData<S>,
 45}
 46
 47impl<S: 'static + Send + Sync> IntoAnyElement<S> for Text<S> {
 48    fn into_any(self) -> AnyElement<S> {
 49        AnyElement::new(self)
 50    }
 51}
 52
 53impl<S: 'static + Send + Sync> Element for Text<S> {
 54    type ViewState = S;
 55    type ElementState = Arc<Mutex<Option<TextElementState>>>;
 56
 57    fn element_id(&self) -> Option<crate::ElementId> {
 58        None
 59    }
 60
 61    fn layout(
 62        &mut self,
 63        _view: &mut S,
 64        _element_state: Option<Self::ElementState>,
 65        cx: &mut ViewContext<S>,
 66    ) -> (LayoutId, Self::ElementState) {
 67        let text_system = cx.text_system().clone();
 68        let text_style = cx.text_style();
 69        let font_size = text_style.font_size * cx.rem_size();
 70        let line_height = text_style
 71            .line_height
 72            .to_pixels(font_size.into(), cx.rem_size());
 73        let text = self.text.clone();
 74        let element_state = Arc::new(Mutex::new(None));
 75
 76        let rem_size = cx.rem_size();
 77        let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
 78            let element_state = element_state.clone();
 79            move |known_dimensions, _| {
 80                let Some(lines) = text_system
 81                    .layout_text(
 82                        &text,
 83                        font_size,
 84                        &[text_style.to_run(text.len())],
 85                        known_dimensions.width, // Wrap if we know the width.
 86                    )
 87                    .log_err()
 88                else {
 89                    return Size::default();
 90                };
 91
 92                let size = Size {
 93                    width: lines.iter().map(|line| line.layout.width).max().unwrap(),
 94                    height: line_height * lines.len(),
 95                };
 96
 97                element_state
 98                    .lock()
 99                    .replace(TextElementState { lines, line_height });
100
101                size
102            }
103        });
104
105        (layout_id, element_state)
106    }
107
108    fn paint<'a>(
109        &mut self,
110        bounds: Bounds<Pixels>,
111        _: &mut Self::ViewState,
112        element_state: &mut Self::ElementState,
113        cx: &mut ViewContext<S>,
114    ) {
115        let element_state = element_state.lock();
116        let element_state = element_state
117            .as_ref()
118            .expect("measurement has not been performed");
119        let line_height = element_state.line_height;
120        let mut line_origin = bounds.origin;
121        for line in &element_state.lines {
122            line.paint(line_origin, line_height, cx).log_err();
123            line_origin.y += line.size(line_height).height;
124        }
125    }
126}
127
128pub struct TextElementState {
129    lines: SmallVec<[Line; 1]>,
130    line_height: Pixels,
131}