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<V: 'static> IntoAnyElement<V> for SharedString {
 11    fn into_any(self) -> AnyElement<V> {
 12        Text {
 13            text: self,
 14            state_type: PhantomData,
 15        }
 16        .into_any()
 17    }
 18}
 19
 20impl<V: 'static> 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<V: 'static> IntoAnyElement<V> for String {
 33    fn into_any(self) -> AnyElement<V> {
 34        Text {
 35            text: self.into(),
 36            state_type: PhantomData,
 37        }
 38        .into_any()
 39    }
 40}
 41
 42pub struct Text<V> {
 43    text: SharedString,
 44    state_type: PhantomData<V>,
 45}
 46
 47unsafe impl<V> Send for Text<V> {}
 48unsafe impl<V> Sync for Text<V> {}
 49
 50impl<V: 'static> IntoAnyElement<V> for Text<V> {
 51    fn into_any(self) -> AnyElement<V> {
 52        AnyElement::new(self)
 53    }
 54}
 55
 56impl<V: 'static> Element for Text<V> {
 57    type ViewState = V;
 58    type ElementState = Arc<Mutex<Option<TextElementState>>>;
 59
 60    fn id(&self) -> Option<crate::ElementId> {
 61        None
 62    }
 63
 64    fn initialize(
 65        &mut self,
 66        _view_state: &mut V,
 67        element_state: Option<Self::ElementState>,
 68        _cx: &mut ViewContext<V>,
 69    ) -> Self::ElementState {
 70        element_state.unwrap_or_default()
 71    }
 72
 73    fn layout(
 74        &mut self,
 75        _view: &mut V,
 76        element_state: &mut Self::ElementState,
 77        cx: &mut ViewContext<V>,
 78    ) -> LayoutId {
 79        let text_system = cx.text_system().clone();
 80        let text_style = cx.text_style();
 81        let font_size = text_style.font_size * cx.rem_size();
 82        let line_height = text_style
 83            .line_height
 84            .to_pixels(font_size.into(), cx.rem_size());
 85        let text = self.text.clone();
 86
 87        let rem_size = cx.rem_size();
 88        let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
 89            let element_state = element_state.clone();
 90            move |known_dimensions, _| {
 91                let Some(lines) = text_system
 92                    .layout_text(
 93                        &text,
 94                        font_size,
 95                        &[text_style.to_run(text.len())],
 96                        known_dimensions.width, // Wrap if we know the width.
 97                    )
 98                    .log_err()
 99                else {
100                    return Size::default();
101                };
102
103                let line_count = lines
104                    .iter()
105                    .map(|line| line.wrap_count() + 1)
106                    .sum::<usize>();
107                let size = Size {
108                    width: lines.iter().map(|line| line.layout.width).max().unwrap(),
109                    height: line_height * line_count,
110                };
111
112                element_state
113                    .lock()
114                    .replace(TextElementState { lines, line_height });
115
116                size
117            }
118        });
119
120        layout_id
121    }
122
123    fn paint(
124        &mut self,
125        bounds: Bounds<Pixels>,
126        _: &mut V,
127        element_state: &mut Self::ElementState,
128        cx: &mut ViewContext<V>,
129    ) {
130        let element_state = element_state.lock();
131        let element_state = element_state
132            .as_ref()
133            .expect("measurement has not been performed");
134        let line_height = element_state.line_height;
135        let mut line_origin = bounds.origin;
136        for line in &element_state.lines {
137            line.paint(line_origin, line_height, cx).log_err();
138            line_origin.y += line.size(line_height).height;
139        }
140    }
141}
142
143pub struct TextElementState {
144    lines: SmallVec<[Line; 1]>,
145    line_height: Pixels,
146}