line.rs

  1use crate::{
  2    black, point, px, size, BorrowWindow, Bounds, Hsla, Pixels, Point, Result, Size,
  3    UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
  4};
  5use smallvec::SmallVec;
  6use std::sync::Arc;
  7
  8#[derive(Debug, Clone)]
  9pub struct DecorationRun {
 10    pub len: u32,
 11    pub color: Hsla,
 12    pub underline: Option<UnderlineStyle>,
 13}
 14
 15#[derive(Clone, Default, Debug)]
 16pub struct Line {
 17    pub(crate) layout: Arc<WrappedLineLayout>,
 18    pub(crate) decorations: SmallVec<[DecorationRun; 32]>,
 19}
 20
 21impl Line {
 22    pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
 23        size(
 24            self.layout.width,
 25            line_height * (self.layout.wrap_boundaries.len() + 1),
 26        )
 27    }
 28
 29    pub fn wrap_count(&self) -> usize {
 30        self.layout.wrap_boundaries.len()
 31    }
 32
 33    pub fn paint(
 34        &self,
 35        origin: Point<Pixels>,
 36        line_height: Pixels,
 37        cx: &mut WindowContext,
 38    ) -> Result<()> {
 39        let padding_top =
 40            (line_height - self.layout.layout.ascent - self.layout.layout.descent) / 2.;
 41        let baseline_offset = point(px(0.), padding_top + self.layout.layout.ascent);
 42
 43        let mut style_runs = self.decorations.iter();
 44        let mut wraps = self.layout.wrap_boundaries.iter().peekable();
 45        let mut run_end = 0;
 46        let mut color = black();
 47        let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
 48        let text_system = cx.text_system().clone();
 49
 50        let mut glyph_origin = origin;
 51        let mut prev_glyph_position = Point::default();
 52        for (run_ix, run) in self.layout.layout.runs.iter().enumerate() {
 53            let max_glyph_size = text_system
 54                .bounding_box(run.font_id, self.layout.layout.font_size)?
 55                .size;
 56
 57            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
 58                glyph_origin.x += glyph.position.x - prev_glyph_position.x;
 59
 60                if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) {
 61                    wraps.next();
 62                    if let Some((underline_origin, underline_style)) = current_underline.take() {
 63                        cx.paint_underline(
 64                            underline_origin,
 65                            glyph_origin.x - underline_origin.x,
 66                            &underline_style,
 67                        )?;
 68                    }
 69
 70                    glyph_origin.x = origin.x;
 71                    glyph_origin.y += line_height;
 72                }
 73                prev_glyph_position = glyph.position;
 74                let glyph_origin = glyph_origin + baseline_offset;
 75
 76                let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
 77                if glyph.index >= run_end {
 78                    if let Some(style_run) = style_runs.next() {
 79                        if let Some((_, underline_style)) = &mut current_underline {
 80                            if style_run.underline.as_ref() != Some(underline_style) {
 81                                finished_underline = current_underline.take();
 82                            }
 83                        }
 84                        if let Some(run_underline) = style_run.underline.as_ref() {
 85                            current_underline.get_or_insert((
 86                                point(
 87                                    glyph_origin.x,
 88                                    origin.y
 89                                        + baseline_offset.y
 90                                        + (self.layout.layout.descent * 0.618),
 91                                ),
 92                                UnderlineStyle {
 93                                    color: Some(run_underline.color.unwrap_or(style_run.color)),
 94                                    thickness: run_underline.thickness,
 95                                    wavy: run_underline.wavy,
 96                                },
 97                            ));
 98                        }
 99
100                        run_end += style_run.len as usize;
101                        color = style_run.color;
102                    } else {
103                        run_end = self.layout.text.len();
104                        finished_underline = current_underline.take();
105                    }
106                }
107
108                if let Some((underline_origin, underline_style)) = finished_underline {
109                    cx.paint_underline(
110                        underline_origin,
111                        glyph_origin.x - underline_origin.x,
112                        &underline_style,
113                    )?;
114                }
115
116                let max_glyph_bounds = Bounds {
117                    origin: glyph_origin,
118                    size: max_glyph_size,
119                };
120
121                let content_mask = cx.content_mask();
122                if max_glyph_bounds.intersects(&content_mask.bounds) {
123                    if glyph.is_emoji {
124                        cx.paint_emoji(
125                            glyph_origin,
126                            run.font_id,
127                            glyph.id,
128                            self.layout.layout.font_size,
129                        )?;
130                    } else {
131                        cx.paint_glyph(
132                            glyph_origin,
133                            run.font_id,
134                            glyph.id,
135                            self.layout.layout.font_size,
136                            color,
137                        )?;
138                    }
139                }
140            }
141        }
142
143        if let Some((underline_start, underline_style)) = current_underline.take() {
144            let line_end_x = origin.x + self.layout.layout.width;
145            cx.paint_underline(
146                underline_start,
147                line_end_x - underline_start.x,
148                &underline_style,
149            )?;
150        }
151
152        Ok(())
153    }
154}