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