text.rs

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