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 let glyph_origin = glyph_origin + baseline_offset;
78
79 let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
80 if glyph.index >= run_end {
81 if let Some(style_run) = style_runs.next() {
82 if let Some((_, underline_style)) = &mut current_underline {
83 if style_run.underline.as_ref() != Some(underline_style) {
84 finished_underline = current_underline.take();
85 }
86 }
87 if let Some(run_underline) = style_run.underline.as_ref() {
88 current_underline.get_or_insert((
89 point(
90 glyph_origin.x,
91 origin.y
92 + baseline_offset.y
93 + (self.layout.layout.descent * 0.618),
94 ),
95 UnderlineStyle {
96 color: Some(run_underline.color.unwrap_or(style_run.color)),
97 thickness: run_underline.thickness,
98 wavy: run_underline.wavy,
99 },
100 ));
101 }
102
103 run_end += style_run.len as usize;
104 color = style_run.color;
105 } else {
106 run_end = self.layout.text.len();
107 finished_underline = current_underline.take();
108 }
109 }
110
111 if let Some((underline_origin, underline_style)) = finished_underline {
112 cx.paint_underline(
113 underline_origin,
114 glyph_origin.x - underline_origin.x,
115 &underline_style,
116 )?;
117 }
118
119 let max_glyph_bounds = Bounds {
120 origin: glyph_origin,
121 size: max_glyph_size,
122 };
123
124 let content_mask = cx.content_mask();
125 if max_glyph_bounds.intersects(&content_mask.bounds) {
126 if glyph.is_emoji {
127 cx.paint_emoji(
128 glyph_origin,
129 run.font_id,
130 glyph.id,
131 self.layout.layout.font_size,
132 )?;
133 } else {
134 cx.paint_glyph(
135 glyph_origin,
136 run.font_id,
137 glyph.id,
138 self.layout.layout.font_size,
139 color,
140 )?;
141 }
142 }
143 }
144 }
145
146 if let Some((underline_start, underline_style)) = current_underline.take() {
147 let line_end_x = origin.x + self.layout.layout.width;
148 cx.paint_underline(
149 underline_start,
150 line_end_x - underline_start.x,
151 &underline_style,
152 )?;
153 }
154
155 Ok(())
156 }
157}