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, LineWrapper, 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 line: Line,
24 wrap_boundaries: Vec<ShapedBoundary>,
25 line_height: f32,
26}
27
28impl Text {
29 pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
30 Self {
31 text,
32 family_id,
33 font_size,
34 style: Default::default(),
35 }
36 }
37
38 pub fn with_style(mut self, style: &TextStyle) -> Self {
39 self.style = style.clone();
40 self
41 }
42
43 pub fn with_default_color(mut self, color: Color) -> Self {
44 self.style.color = color;
45 self
46 }
47}
48
49impl Element for Text {
50 type LayoutState = LayoutState;
51 type PaintState = ();
52
53 fn layout(
54 &mut self,
55 constraint: SizeConstraint,
56 cx: &mut LayoutContext,
57 ) -> (Vector2F, Self::LayoutState) {
58 let font_id = cx
59 .font_cache
60 .select_font(self.family_id, &self.style.font_properties)
61 .unwrap();
62 let line_height = cx.font_cache.line_height(font_id, self.font_size);
63 let line = cx.text_layout_cache.layout_str(
64 self.text.as_str(),
65 self.font_size,
66 &[(self.text.len(), font_id, self.style.color)],
67 );
68 let mut wrapper = LineWrapper::acquire(font_id, self.font_size, cx.font_system.clone());
69 let wrap_boundaries = wrapper
70 .wrap_shaped_line(&self.text, &line, constraint.max.x())
71 .collect::<Vec<_>>();
72 let size = vec2f(
73 line.width()
74 .ceil()
75 .max(constraint.min.x())
76 .min(constraint.max.x()),
77 (line_height * (wrap_boundaries.len() + 1) as f32).ceil(),
78 );
79 let layout = LayoutState {
80 line,
81 wrap_boundaries,
82 line_height,
83 };
84
85 (size, layout)
86 }
87
88 fn paint(
89 &mut self,
90 bounds: RectF,
91 layout: &mut Self::LayoutState,
92 cx: &mut PaintContext,
93 ) -> Self::PaintState {
94 layout.line.paint_wrapped(
95 bounds.origin(),
96 layout.line_height,
97 layout.wrap_boundaries.iter().copied(),
98 cx,
99 );
100 }
101
102 fn dispatch_event(
103 &mut self,
104 _: &Event,
105 _: RectF,
106 _: &mut Self::LayoutState,
107 _: &mut Self::PaintState,
108 _: &mut EventContext,
109 ) -> bool {
110 false
111 }
112
113 fn debug(
114 &self,
115 bounds: RectF,
116 _: &Self::LayoutState,
117 _: &Self::PaintState,
118 cx: &DebugContext,
119 ) -> Value {
120 json!({
121 "type": "Label",
122 "bounds": bounds.to_json(),
123 "text": &self.text,
124 "font_family": cx.font_cache.family_name(self.family_id).unwrap(),
125 "font_size": self.font_size,
126 "style": self.style.to_json(),
127 })
128 }
129}