1use std::ops::Range;
8
9use gpui::{
10 BorderStyle, Bounds, Hsla, Pixels, Point, TextRun, Window, WrappedLineLayout, point, px, quad,
11 size,
12};
13
14use super::caret;
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct Overdraw {
19 pub range: Range<usize>,
20 pub plate: Hsla,
22 pub ink: Hsla,
24}
25
26pub fn overdraws(runs: &[TextRun], page: Hsla, layers: &[(Range<usize>, Hsla)]) -> Vec<Overdraw> {
31 let mut out: Vec<Overdraw> = Vec::new();
32 let mut at = 0;
33 for run in runs {
34 let end = at + run.len;
35 let mut cuts: Vec<usize> = layers
36 .iter()
37 .flat_map(|(range, _)| [range.start, range.end])
38 .filter(|cut| (at + 1..end).contains(cut))
39 .chain([at, end])
40 .collect();
41 cuts.sort_unstable();
42 cuts.dedup();
43 for piece in cuts.windows(2) {
44 let (from, to) = (piece[0], piece[1]);
45 let mut over = layers
46 .iter()
47 .filter(|(range, _)| range.start <= from && to <= range.end)
48 .map(|(_, color)| *color)
49 .peekable();
50 if over.peek().is_none() {
51 continue;
52 }
53 let plate = theme::stack(page, over);
54 let ink = theme::legible(run.color, plate, theme::kept(run.color, page));
55 if ink == run.color {
56 continue;
57 }
58 match out.last_mut() {
59 Some(last) if last.range.end == from && last.plate == plate && last.ink == ink => {
60 last.range.end = to;
61 }
62 _ => out.push(Overdraw {
63 range: from..to,
64 plate,
65 ink,
66 }),
67 }
68 }
69 at = end;
70 }
71 out
72}
73
74pub fn paint<'a>(
79 lines: impl IntoIterator<Item = &'a WrappedLineLayout>,
80 origin: Point<Pixels>,
81 line_height: Pixels,
82 radius: Pixels,
83 pieces: &[Overdraw],
84 window: &mut Window,
85) {
86 if pieces.is_empty() {
87 return;
88 }
89 let (mut start, mut top) = (0, origin);
90 for line in lines {
91 let end = start + line.len();
92 for piece in pieces {
93 let (from, to) = (piece.range.start.max(start), piece.range.end.min(end));
94 if from < to {
95 paint_line(
96 line,
97 top,
98 line_height,
99 radius,
100 from - start..to - start,
101 piece,
102 window,
103 );
104 }
105 }
106 start = end + 1;
107 top.y += line.size(line_height).height;
108 }
109}
110
111fn paint_line(
113 line: &WrappedLineLayout,
114 origin: Point<Pixels>,
115 line_height: Pixels,
116 radius: Pixels,
117 range: Range<usize>,
118 piece: &Overdraw,
119 window: &mut Window,
120) {
121 let layout = &line.unwrapped_layout;
122 let starts: Vec<usize> = std::iter::once(0)
123 .chain(
124 line.wrap_boundaries()
125 .iter()
126 .map(|wrap| layout.runs[wrap.run_ix].glyphs[wrap.glyph_ix].index),
127 )
128 .collect();
129 let ends = starts.iter().skip(1).copied().chain([line.len()]);
130 for (row, (start, end)) in starts.iter().copied().zip(ends).enumerate() {
131 let (from, to) = (range.start.max(start), range.end.min(end));
132 if from >= to {
133 continue;
134 }
135 let (left, right) = (layout.x_for_index(from), layout.x_for_index(to));
136 let at = origin + point(left - layout.x_for_index(start), line_height * row as f32);
137 window.paint_quad(quad(
138 Bounds::new(at, size(right - left, line_height)),
139 radius,
140 piece.plate,
141 px(0.),
142 gpui::transparent_black(),
143 BorderStyle::default(),
144 ));
145 caret::paint_covered(layout, &(from..to), at, line_height, piece.ink, window);
146 }
147}