text.rs

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