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