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