1use crate::{
2 color::Color,
3 fonts::TextStyle,
4 geometry::{
5 rect::RectF,
6 vector::{vec2f, Vector2F},
7 },
8 json::{ToJson, Value},
9 text_layout::{Line, ShapedBoundary},
10 DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
11};
12use serde_json::json;
13
14pub struct Text {
15 text: String,
16 style: TextStyle,
17}
18
19pub struct LayoutState {
20 lines: Vec<(Line, Vec<ShapedBoundary>)>,
21 line_height: f32,
22}
23
24impl Text {
25 pub fn new(text: String, style: TextStyle) -> Self {
26 Self { text, style }
27 }
28
29 pub fn with_default_color(mut self, color: Color) -> Self {
30 self.style.color = color;
31 self
32 }
33}
34
35impl Element for Text {
36 type LayoutState = LayoutState;
37 type PaintState = ();
38
39 fn layout(
40 &mut self,
41 constraint: SizeConstraint,
42 cx: &mut LayoutContext,
43 ) -> (Vector2F, Self::LayoutState) {
44 let font_id = self.style.font_id;
45 let line_height = cx.font_cache.line_height(font_id, self.style.font_size);
46
47 let mut wrapper = cx.font_cache.line_wrapper(font_id, self.style.font_size);
48 let mut lines = Vec::new();
49 let mut line_count = 0;
50 let mut max_line_width = 0_f32;
51 for line in self.text.lines() {
52 let shaped_line = cx.text_layout_cache.layout_str(
53 line,
54 self.style.font_size,
55 &[(line.len(), font_id, self.style.color)],
56 );
57 let wrap_boundaries = wrapper
58 .wrap_shaped_line(line, &shaped_line, constraint.max.x())
59 .collect::<Vec<_>>();
60
61 max_line_width = max_line_width.max(shaped_line.width());
62 line_count += wrap_boundaries.len() + 1;
63 lines.push((shaped_line, wrap_boundaries));
64 }
65
66 let size = vec2f(
67 max_line_width
68 .ceil()
69 .max(constraint.min.x())
70 .min(constraint.max.x()),
71 (line_height * line_count as f32).ceil(),
72 );
73 (size, LayoutState { lines, line_height })
74 }
75
76 fn paint(
77 &mut self,
78 bounds: RectF,
79 layout: &mut Self::LayoutState,
80 cx: &mut PaintContext,
81 ) -> Self::PaintState {
82 let mut origin = bounds.origin();
83 for (line, wrap_boundaries) in &layout.lines {
84 line.paint_wrapped(
85 origin,
86 layout.line_height,
87 wrap_boundaries.iter().copied(),
88 cx,
89 );
90 origin.set_y(origin.y() + (wrap_boundaries.len() + 1) as f32 * layout.line_height);
91 }
92 }
93
94 fn dispatch_event(
95 &mut self,
96 _: &Event,
97 _: RectF,
98 _: &mut Self::LayoutState,
99 _: &mut Self::PaintState,
100 _: &mut EventContext,
101 ) -> bool {
102 false
103 }
104
105 fn debug(
106 &self,
107 bounds: RectF,
108 _: &Self::LayoutState,
109 _: &Self::PaintState,
110 _: &DebugContext,
111 ) -> Value {
112 json!({
113 "type": "Text",
114 "bounds": bounds.to_json(),
115 "text": &self.text,
116 "style": self.style.to_json(),
117 })
118 }
119}