label.rs

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