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}