text.rs

  1use crate::{
  2    color::Color,
  3    font_cache::FamilyId,
  4    fonts::TextStyle,
  5    geometry::{
  6        rect::RectF,
  7        vector::{vec2f, Vector2F},
  8    },
  9    json::{ToJson, Value},
 10    text_layout::{Line, LineWrapper, ShapedBoundary},
 11    DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
 12};
 13use serde_json::json;
 14
 15pub struct Text {
 16    text: String,
 17    family_id: FamilyId,
 18    font_size: f32,
 19    style: TextStyle,
 20}
 21
 22pub struct LayoutState {
 23    line: Line,
 24    wrap_boundaries: Vec<ShapedBoundary>,
 25    line_height: f32,
 26}
 27
 28impl Text {
 29    pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
 30        Self {
 31            text,
 32            family_id,
 33            font_size,
 34            style: Default::default(),
 35        }
 36    }
 37
 38    pub fn with_style(mut self, style: &TextStyle) -> Self {
 39        self.style = style.clone();
 40        self
 41    }
 42
 43    pub fn with_default_color(mut self, color: Color) -> Self {
 44        self.style.color = color;
 45        self
 46    }
 47}
 48
 49impl Element for Text {
 50    type LayoutState = LayoutState;
 51    type PaintState = ();
 52
 53    fn layout(
 54        &mut self,
 55        constraint: SizeConstraint,
 56        cx: &mut LayoutContext,
 57    ) -> (Vector2F, Self::LayoutState) {
 58        let font_id = cx
 59            .font_cache
 60            .select_font(self.family_id, &self.style.font_properties)
 61            .unwrap();
 62        let line_height = cx.font_cache.line_height(font_id, self.font_size);
 63        let line = cx.text_layout_cache.layout_str(
 64            self.text.as_str(),
 65            self.font_size,
 66            &[(self.text.len(), font_id, self.style.color)],
 67        );
 68        let mut wrapper = LineWrapper::acquire(font_id, self.font_size, cx.font_system.clone());
 69        let wrap_boundaries = wrapper
 70            .wrap_shaped_line(&self.text, &line, constraint.max.x())
 71            .collect::<Vec<_>>();
 72        let size = vec2f(
 73            line.width()
 74                .ceil()
 75                .max(constraint.min.x())
 76                .min(constraint.max.x()),
 77            (line_height * (wrap_boundaries.len() + 1) as f32).ceil(),
 78        );
 79        let layout = LayoutState {
 80            line,
 81            wrap_boundaries,
 82            line_height,
 83        };
 84
 85        (size, layout)
 86    }
 87
 88    fn paint(
 89        &mut self,
 90        bounds: RectF,
 91        layout: &mut Self::LayoutState,
 92        cx: &mut PaintContext,
 93    ) -> Self::PaintState {
 94        layout.line.paint_wrapped(
 95            bounds.origin(),
 96            layout.line_height,
 97            layout.wrap_boundaries.iter().copied(),
 98            cx,
 99        );
100    }
101
102    fn dispatch_event(
103        &mut self,
104        _: &Event,
105        _: RectF,
106        _: &mut Self::LayoutState,
107        _: &mut Self::PaintState,
108        _: &mut EventContext,
109    ) -> bool {
110        false
111    }
112
113    fn debug(
114        &self,
115        bounds: RectF,
116        _: &Self::LayoutState,
117        _: &Self::PaintState,
118        cx: &DebugContext,
119    ) -> Value {
120        json!({
121            "type": "Label",
122            "bounds": bounds.to_json(),
123            "text": &self.text,
124            "font_family": cx.font_cache.family_name(self.family_id).unwrap(),
125            "font_size": self.font_size,
126            "style": self.style.to_json(),
127        })
128    }
129}