label.rs

  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}