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