text.rs

  1use crate::{
  2    element::{Element, IntoElement, Layout},
  3    layout_context::LayoutContext,
  4    paint_context::PaintContext,
  5};
  6use anyhow::Result;
  7use gpui::{geometry::Size, text_layout::LineLayout, LayoutId, RenderContext};
  8use parking_lot::Mutex;
  9use std::sync::Arc;
 10
 11impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
 12    type Element = Text;
 13
 14    fn into_element(self) -> Self::Element {
 15        Text { text: self.into() }
 16    }
 17}
 18
 19pub struct Text {
 20    text: ArcCow<'static, str>,
 21}
 22
 23impl<V: 'static> Element<V> for Text {
 24    type PaintState = Arc<Mutex<Option<TextLayout>>>;
 25
 26    fn layout(
 27        &mut self,
 28        _view: &mut V,
 29        cx: &mut LayoutContext<V>,
 30    ) -> Result<(LayoutId, Self::PaintState)> {
 31        let fonts = cx.platform().fonts();
 32        let text_style = cx.text_style();
 33        let line_height = cx.font_cache().line_height(text_style.font_size);
 34        let text = self.text.clone();
 35        let paint_state = Arc::new(Mutex::new(None));
 36
 37        let layout_id = cx.add_measured_layout_node(Default::default(), {
 38            let paint_state = paint_state.clone();
 39            move |_params| {
 40                let line_layout = fonts.layout_line(
 41                    text.as_ref(),
 42                    text_style.font_size,
 43                    &[(text.len(), text_style.to_run())],
 44                );
 45
 46                let size = Size {
 47                    width: line_layout.width,
 48                    height: line_height,
 49                };
 50
 51                paint_state.lock().replace(TextLayout {
 52                    line_layout: Arc::new(line_layout),
 53                    line_height,
 54                });
 55
 56                size
 57            }
 58        });
 59
 60        Ok((layout_id?, paint_state))
 61    }
 62
 63    fn paint<'a>(
 64        &mut self,
 65        _view: &mut V,
 66        layout: &Layout,
 67        paint_state: &mut Self::PaintState,
 68        cx: &mut PaintContext<V>,
 69    ) {
 70        let line_layout;
 71        let line_height;
 72        {
 73            let paint_state = paint_state.lock();
 74            let paint_state = paint_state
 75                .as_ref()
 76                .expect("measurement has not been performed");
 77            line_layout = paint_state.line_layout.clone();
 78            line_height = paint_state.line_height;
 79        }
 80
 81        let text_style = cx.text_style();
 82        let line =
 83            gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
 84
 85        let origin = layout.bounds.origin();
 86        // TODO: We haven't added visible bounds to the new element system yet, so this is a placeholder.
 87        let visible_bounds = layout.bounds;
 88        line.paint(cx.scene, origin, visible_bounds, line_height, cx.legacy_cx);
 89    }
 90}
 91
 92pub struct TextLayout {
 93    line_layout: Arc<LineLayout>,
 94    line_height: f32,
 95}
 96
 97pub enum ArcCow<'a, T: ?Sized> {
 98    Borrowed(&'a T),
 99    Owned(Arc<T>),
100}
101
102impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
103    fn clone(&self) -> Self {
104        match self {
105            Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
106            Self::Owned(owned) => Self::Owned(owned.clone()),
107        }
108    }
109}
110
111impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
112    fn from(s: &'a T) -> Self {
113        Self::Borrowed(s)
114    }
115}
116
117impl<T> From<Arc<T>> for ArcCow<'_, T> {
118    fn from(s: Arc<T>) -> Self {
119        Self::Owned(s)
120    }
121}
122
123impl From<String> for ArcCow<'_, str> {
124    fn from(value: String) -> Self {
125        Self::Owned(value.into())
126    }
127}
128
129impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
130    type Target = T;
131
132    fn deref(&self) -> &Self::Target {
133        match self {
134            ArcCow::Borrowed(s) => s,
135            ArcCow::Owned(s) => s.as_ref(),
136        }
137    }
138}
139
140impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
141    fn as_ref(&self) -> &T {
142        match self {
143            ArcCow::Borrowed(borrowed) => borrowed,
144            ArcCow::Owned(owned) => owned.as_ref(),
145        }
146    }
147}