1use crate::{
2 color::ColorU,
3 fonts::{FamilyId, Properties},
4 geometry::vector::{vec2f, Vector2F},
5 text_layout::Line,
6 AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
7};
8use std::{ops::Range, sync::Arc};
9
10pub struct Label {
11 text: String,
12 family_id: FamilyId,
13 font_properties: Properties,
14 font_size: f32,
15 highlights: Option<Highlights>,
16}
17
18pub struct LayoutState {
19 line: Arc<Line>,
20 colors: Vec<(Range<usize>, ColorU)>,
21}
22
23pub struct Highlights {
24 color: ColorU,
25 indices: Vec<usize>,
26 font_properties: Properties,
27}
28
29impl Label {
30 pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
31 Self {
32 text,
33 family_id,
34 font_properties: Properties::new(),
35 font_size,
36 highlights: None,
37 }
38 }
39
40 pub fn with_highlights(
41 mut self,
42 color: ColorU,
43 font_properties: Properties,
44 indices: Vec<usize>,
45 ) -> Self {
46 self.highlights = Some(Highlights {
47 color,
48 font_properties,
49 indices,
50 });
51 self
52 }
53}
54
55impl Element for Label {
56 type LayoutState = LayoutState;
57 type PaintState = ();
58
59 fn layout(
60 &mut self,
61 constraint: SizeConstraint,
62 ctx: &mut LayoutContext,
63 ) -> (Vector2F, Self::LayoutState) {
64 let font_id = ctx
65 .font_cache
66 .select_font(self.family_id, &self.font_properties)
67 .unwrap();
68 let text_len = self.text.chars().count();
69 let mut styles;
70 let mut colors;
71 if let Some(highlights) = self.highlights.as_ref() {
72 styles = Vec::new();
73 colors = Vec::new();
74 let highlight_font_id = ctx
75 .font_cache
76 .select_font(self.family_id, &highlights.font_properties)
77 .unwrap_or(font_id);
78 let mut pending_highlight: Option<Range<usize>> = None;
79 for ix in &highlights.indices {
80 if let Some(pending_highlight) = pending_highlight.as_mut() {
81 if *ix == pending_highlight.end {
82 pending_highlight.end += 1;
83 } else {
84 styles.push((pending_highlight.clone(), highlight_font_id));
85 colors.push((pending_highlight.clone(), highlights.color));
86 styles.push((pending_highlight.end..*ix, font_id));
87 colors.push((pending_highlight.end..*ix, ColorU::black()));
88 *pending_highlight = *ix..*ix + 1;
89 }
90 } else {
91 styles.push((0..*ix, font_id));
92 colors.push((0..*ix, ColorU::black()));
93 pending_highlight = Some(*ix..*ix + 1);
94 }
95 }
96 if let Some(pending_highlight) = pending_highlight.as_mut() {
97 styles.push((pending_highlight.clone(), highlight_font_id));
98 colors.push((pending_highlight.clone(), highlights.color));
99 if text_len > pending_highlight.end {
100 styles.push((pending_highlight.end..text_len, font_id));
101 colors.push((pending_highlight.end..text_len, ColorU::black()));
102 }
103 } else {
104 styles.push((0..text_len, font_id));
105 colors.push((0..text_len, ColorU::black()));
106 }
107 } else {
108 styles = vec![(0..text_len, font_id)];
109 colors = vec![(0..text_len, ColorU::black())];
110 }
111
112 let line = ctx.text_layout_cache.layout_str(
113 self.text.as_str(),
114 self.font_size,
115 styles.as_slice(),
116 ctx.font_cache,
117 );
118
119 let size = vec2f(
120 line.width.max(constraint.min.x()).min(constraint.max.x()),
121 ctx.font_cache.line_height(font_id, self.font_size).ceil(),
122 );
123
124 (size, LayoutState { line, colors })
125 }
126
127 fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
128 }
129
130 fn paint(
131 &mut self,
132 bounds: pathfinder_geometry::rect::RectF,
133 layout: &mut Self::LayoutState,
134 ctx: &mut PaintContext,
135 ) -> Self::PaintState {
136 layout.line.paint(bounds, layout.colors.as_slice(), ctx)
137 }
138
139 fn dispatch_event(
140 &mut self,
141 _: &Event,
142 _: pathfinder_geometry::rect::RectF,
143 _: &mut Self::LayoutState,
144 _: &mut Self::PaintState,
145 _: &mut EventContext,
146 ) -> bool {
147 false
148 }
149}