1use crate::{
2 AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels,
3 SharedString, Size, TextRun, ViewContext, WrappedLine,
4};
5use parking_lot::{Mutex, MutexGuard};
6use smallvec::SmallVec;
7use std::{cell::Cell, rc::Rc, sync::Arc};
8use util::ResultExt;
9
10pub struct Text {
11 text: SharedString,
12 runs: Option<Vec<TextRun>>,
13}
14
15impl Text {
16 /// Renders text with runs of different styles.
17 ///
18 /// Callers are responsible for setting the correct style for each run.
19 /// For text with a uniform style, you can usually avoid calling this constructor
20 /// and just pass text directly.
21 pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
22 Text {
23 text,
24 runs: Some(runs),
25 }
26 }
27}
28
29impl<V: 'static> Component<V> for Text {
30 fn render(self) -> AnyElement<V> {
31 AnyElement::new(self)
32 }
33}
34
35impl<V: 'static> Element<V> for Text {
36 type ElementState = TextState;
37
38 fn element_id(&self) -> Option<crate::ElementId> {
39 None
40 }
41
42 fn layout(
43 &mut self,
44 _view: &mut V,
45 element_state: Option<Self::ElementState>,
46 cx: &mut ViewContext<V>,
47 ) -> (LayoutId, Self::ElementState) {
48 let element_state = element_state.unwrap_or_default();
49 let text_system = cx.text_system().clone();
50 let text_style = cx.text_style();
51 let font_size = text_style.font_size.to_pixels(cx.rem_size());
52 let line_height = text_style
53 .line_height
54 .to_pixels(font_size.into(), cx.rem_size());
55 let text = self.text.clone();
56
57 let rem_size = cx.rem_size();
58
59 let runs = if let Some(runs) = self.runs.take() {
60 runs
61 } else {
62 vec![text_style.to_run(text.len())]
63 };
64
65 let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
66 let element_state = element_state.clone();
67 move |known_dimensions, available_space| {
68 let wrap_width = known_dimensions.width.or(match available_space.width {
69 crate::AvailableSpace::Definite(x) => Some(x),
70 _ => None,
71 });
72
73 if let Some(text_state) = element_state.0.lock().as_ref() {
74 if text_state.size.is_some()
75 && (wrap_width.is_none() || wrap_width == text_state.wrap_width)
76 {
77 return text_state.size.unwrap();
78 }
79 }
80
81 let Some(lines) = text_system
82 .shape_text(
83 &text,
84 font_size,
85 &runs[..],
86 wrap_width, // Wrap if we know the width.
87 )
88 .log_err()
89 else {
90 element_state.lock().replace(TextStateInner {
91 lines: Default::default(),
92 line_height,
93 wrap_width,
94 size: Some(Size::default()),
95 });
96 return Size::default();
97 };
98
99 let mut size: Size<Pixels> = Size::default();
100 for line in &lines {
101 let line_size = line.size(line_height);
102 size.height += line_size.height;
103 size.width = size.width.max(line_size.width);
104 }
105
106 element_state.lock().replace(TextStateInner {
107 lines,
108 line_height,
109 wrap_width,
110 size: Some(size),
111 });
112
113 size
114 }
115 });
116
117 (layout_id, element_state)
118 }
119
120 fn paint(
121 &mut self,
122 bounds: Bounds<Pixels>,
123 _: &mut V,
124 element_state: &mut Self::ElementState,
125 cx: &mut ViewContext<V>,
126 ) {
127 let element_state = element_state.lock();
128 let element_state = element_state
129 .as_ref()
130 .ok_or_else(|| anyhow::anyhow!("measurement has not been performed on {}", &self.text))
131 .unwrap();
132
133 let line_height = element_state.line_height;
134 let mut line_origin = bounds.origin;
135 for line in &element_state.lines {
136 line.paint(line_origin, line_height, cx).log_err();
137 line_origin.y += line.size(line_height).height;
138 }
139 }
140}
141
142#[derive(Default, Clone)]
143pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
144
145impl TextState {
146 fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
147 self.0.lock()
148 }
149}
150
151struct TextStateInner {
152 lines: SmallVec<[WrappedLine; 1]>,
153 line_height: Pixels,
154 wrap_width: Option<Pixels>,
155 size: Option<Size<Pixels>>,
156}
157
158struct InteractiveText {
159 id: ElementId,
160 text: Text,
161}
162
163struct InteractiveTextState {
164 text_state: TextState,
165 clicked_range_ixs: Rc<Cell<SmallVec<[usize; 1]>>>,
166}
167
168impl<V: 'static> Element<V> for InteractiveText {
169 type ElementState = InteractiveTextState;
170
171 fn element_id(&self) -> Option<ElementId> {
172 Some(self.id.clone())
173 }
174
175 fn layout(
176 &mut self,
177 view_state: &mut V,
178 element_state: Option<Self::ElementState>,
179 cx: &mut ViewContext<V>,
180 ) -> (LayoutId, Self::ElementState) {
181 if let Some(InteractiveTextState {
182 text_state,
183 clicked_range_ixs,
184 }) = element_state
185 {
186 let (layout_id, text_state) = self.text.layout(view_state, Some(text_state), cx);
187 let element_state = InteractiveTextState {
188 text_state,
189 clicked_range_ixs,
190 };
191 (layout_id, element_state)
192 } else {
193 let (layout_id, text_state) = self.text.layout(view_state, None, cx);
194 let element_state = InteractiveTextState {
195 text_state,
196 clicked_range_ixs: Rc::default(),
197 };
198 (layout_id, element_state)
199 }
200 }
201
202 fn paint(
203 &mut self,
204 bounds: Bounds<Pixels>,
205 view_state: &mut V,
206 element_state: &mut Self::ElementState,
207 cx: &mut ViewContext<V>,
208 ) {
209 self.text
210 .paint(bounds, view_state, &mut element_state.text_state, cx)
211 }
212}
213
214impl<V: 'static> Component<V> for SharedString {
215 fn render(self) -> AnyElement<V> {
216 Text {
217 text: self,
218 runs: None,
219 }
220 .render()
221 }
222}
223
224impl<V: 'static> Component<V> for &'static str {
225 fn render(self) -> AnyElement<V> {
226 Text {
227 text: self.into(),
228 runs: None,
229 }
230 .render()
231 }
232}
233
234// TODO: Figure out how to pass `String` to `child` without this.
235// This impl doesn't exist in the `gpui2` crate.
236impl<V: 'static> Component<V> for String {
237 fn render(self) -> AnyElement<V> {
238 Text {
239 text: self.into(),
240 runs: None,
241 }
242 .render()
243 }
244}