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