1use crate::{
2 AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, Line, Pixels, SharedString,
3 Size, TextRun, ViewContext,
4};
5use parking_lot::Mutex;
6use smallvec::SmallVec;
7use std::{marker::PhantomData, sync::Arc};
8use util::ResultExt;
9
10impl<V: 'static> Component<V> for SharedString {
11 fn render(self) -> AnyElement<V> {
12 Text {
13 text: self,
14 runs: None,
15 state_type: PhantomData,
16 }
17 .render()
18 }
19}
20
21impl<V: 'static> Component<V> for &'static str {
22 fn render(self) -> AnyElement<V> {
23 Text {
24 text: self.into(),
25 runs: None,
26 state_type: PhantomData,
27 }
28 .render()
29 }
30}
31
32// TODO: Figure out how to pass `String` to `child` without this.
33// This impl doesn't exist in the `gpui2` crate.
34impl<V: 'static> Component<V> for String {
35 fn render(self) -> AnyElement<V> {
36 Text {
37 text: self.into(),
38 runs: None,
39 state_type: PhantomData,
40 }
41 .render()
42 }
43}
44
45pub struct Text<V> {
46 text: SharedString,
47 runs: Option<Vec<TextRun>>,
48 state_type: PhantomData<V>,
49}
50
51impl<V: 'static> Text<V> {
52 /// styled renders text that has different runs of different styles.
53 /// callers are responsible for setting the correct style for each run.
54 ////
55 /// For uniform text you can usually just pass a string as a child, and
56 /// cx.text_style() will be used automatically.
57 pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
58 Text {
59 text,
60 runs: Some(runs),
61 state_type: Default::default(),
62 }
63 }
64}
65
66impl<V: 'static> Component<V> for Text<V> {
67 fn render(self) -> AnyElement<V> {
68 AnyElement::new(self)
69 }
70}
71
72impl<V: 'static> Element<V> for Text<V> {
73 type ElementState = Arc<Mutex<Option<TextElementState>>>;
74
75 fn element_id(&self) -> Option<crate::ElementId> {
76 None
77 }
78
79 fn initialize(
80 &mut self,
81 _view_state: &mut V,
82 element_state: Option<Self::ElementState>,
83 _cx: &mut ViewContext<V>,
84 ) -> Self::ElementState {
85 element_state.unwrap_or_default()
86 }
87
88 fn layout(
89 &mut self,
90 _view: &mut V,
91 element_state: &mut Self::ElementState,
92 cx: &mut ViewContext<V>,
93 ) -> LayoutId {
94 let text_system = cx.text_system().clone();
95 let text_style = cx.text_style();
96 let font_size = text_style.font_size.to_pixels(cx.rem_size());
97 let line_height = text_style
98 .line_height
99 .to_pixels(font_size.into(), cx.rem_size());
100 let text = self.text.clone();
101
102 let rem_size = cx.rem_size();
103
104 let runs = if let Some(runs) = self.runs.take() {
105 runs
106 } else {
107 vec![text_style.to_run(text.len())]
108 };
109
110 let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
111 let element_state = element_state.clone();
112 move |known_dimensions, _| {
113 let Some(lines) = text_system
114 .layout_text(
115 &text,
116 font_size,
117 &runs[..],
118 known_dimensions.width, // Wrap if we know the width.
119 )
120 .log_err()
121 else {
122 element_state.lock().replace(TextElementState {
123 lines: Default::default(),
124 line_height,
125 });
126 return Size::default();
127 };
128
129 let line_count = lines
130 .iter()
131 .map(|line| line.wrap_count() + 1)
132 .sum::<usize>();
133 let size = Size {
134 width: lines
135 .iter()
136 .map(|line| line.layout.width)
137 .max()
138 .unwrap()
139 .ceil(),
140 height: line_height * line_count,
141 };
142
143 element_state
144 .lock()
145 .replace(TextElementState { lines, line_height });
146
147 size
148 }
149 });
150
151 layout_id
152 }
153
154 fn paint(
155 &mut self,
156 bounds: Bounds<Pixels>,
157 _: &mut V,
158 element_state: &mut Self::ElementState,
159 cx: &mut ViewContext<V>,
160 ) {
161 let element_state = element_state.lock();
162 let element_state = element_state
163 .as_ref()
164 .ok_or_else(|| anyhow::anyhow!("measurement has not been performed on {}", &self.text))
165 .unwrap();
166
167 let line_height = element_state.line_height;
168 let mut line_origin = bounds.origin;
169 for line in &element_state.lines {
170 line.paint(line_origin, line_height, cx).log_err();
171 line_origin.y += line.size(line_height).height;
172 }
173 }
174}
175
176pub struct TextElementState {
177 lines: SmallVec<[Line; 1]>,
178 line_height: Pixels,
179}