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