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