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