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