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}