label.rs

  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 =
113            ctx.text_layout_cache
114                .layout_str(self.text.as_str(), self.font_size, styles.as_slice());
115
116        let size = vec2f(
117            line.width.max(constraint.min.x()).min(constraint.max.x()),
118            ctx.font_cache.line_height(font_id, self.font_size).ceil(),
119        );
120
121        (size, LayoutState { line, colors })
122    }
123
124    fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
125    }
126
127    fn paint(
128        &mut self,
129        bounds: pathfinder_geometry::rect::RectF,
130        layout: &mut Self::LayoutState,
131        ctx: &mut PaintContext,
132    ) -> Self::PaintState {
133        layout.line.paint(bounds, layout.colors.as_slice(), ctx)
134    }
135
136    fn dispatch_event(
137        &mut self,
138        _: &Event,
139        _: pathfinder_geometry::rect::RectF,
140        _: &mut Self::LayoutState,
141        _: &mut Self::PaintState,
142        _: &mut EventContext,
143    ) -> bool {
144        false
145    }
146}