text.rs

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