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