text rendering: support strikethroughs (#7363)

<img width="1269" alt="image"
src="https://github.com/zed-industries/zed/assets/18583882/d4c93033-b2ac-4ae0-8e12-457f256ee869">

Release Notes:

- Added support for styling text with strikethrough.

Related: 
- https://github.com/zed-industries/zed/issues/5364
- https://github.com/zed-industries/zed/pull/7345
This commit is contained in:
Kieran Gill 2024-02-07 09:51:27 -05:00 committed by GitHub
parent 55129d4d6c
commit ad3940c66f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 145 additions and 4 deletions

View file

@ -1,6 +1,6 @@
use crate::{
black, fill, point, px, size, Bounds, ElementContext, Hsla, LineLayout, Pixels, Point, Result,
SharedString, UnderlineStyle, WrapBoundary, WrappedLineLayout,
SharedString, StrikethroughStyle, UnderlineStyle, WrapBoundary, WrappedLineLayout,
};
use derive_more::{Deref, DerefMut};
use smallvec::SmallVec;
@ -20,6 +20,9 @@ pub struct DecorationRun {
/// The underline style for this run
pub underline: Option<UnderlineStyle>,
/// The strikethrough style for this run
pub strikethrough: Option<StrikethroughStyle>,
}
/// A line of text that has been shaped and decorated.
@ -113,6 +116,7 @@ fn paint_line(
let mut run_end = 0;
let mut color = black();
let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let mut current_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
let mut current_background: Option<(Point<Pixels>, Hsla)> = None;
let text_system = cx.text_system().clone();
let mut glyph_origin = origin;
@ -145,6 +149,17 @@ fn paint_line(
underline_origin.x = origin.x;
underline_origin.y += line_height;
}
if let Some((strikethrough_origin, strikethrough_style)) =
current_strikethrough.as_mut()
{
cx.paint_strikethrough(
*strikethrough_origin,
glyph_origin.x - strikethrough_origin.x,
strikethrough_style,
);
strikethrough_origin.x = origin.x;
strikethrough_origin.y += line_height;
}
glyph_origin.x = origin.x;
glyph_origin.y += line_height;
@ -153,6 +168,7 @@ fn paint_line(
let mut finished_background: Option<(Point<Pixels>, Hsla)> = None;
let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let mut finished_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
if glyph.index >= run_end {
if let Some(style_run) = decoration_runs.next() {
if let Some((_, background_color)) = &mut current_background {
@ -183,6 +199,24 @@ fn paint_line(
},
));
}
if let Some((_, strikethrough_style)) = &mut current_strikethrough {
if style_run.strikethrough.as_ref() != Some(strikethrough_style) {
finished_strikethrough = current_strikethrough.take();
}
}
if let Some(run_strikethrough) = style_run.strikethrough.as_ref() {
current_strikethrough.get_or_insert((
point(
glyph_origin.x,
glyph_origin.y
+ (((layout.ascent * 0.5) + baseline_offset.y) * 0.5),
),
StrikethroughStyle {
color: Some(run_strikethrough.color.unwrap_or(style_run.color)),
thickness: run_strikethrough.thickness,
},
));
}
run_end += style_run.len as usize;
color = style_run.color;
@ -190,6 +224,7 @@ fn paint_line(
run_end = layout.len;
finished_background = current_background.take();
finished_underline = current_underline.take();
finished_strikethrough = current_strikethrough.take();
}
}
@ -211,6 +246,14 @@ fn paint_line(
);
}
if let Some((strikethrough_origin, strikethrough_style)) = finished_strikethrough {
cx.paint_strikethrough(
strikethrough_origin,
glyph_origin.x - strikethrough_origin.x,
&strikethrough_style,
);
}
let max_glyph_bounds = Bounds {
origin: glyph_origin,
size: max_glyph_size,
@ -263,5 +306,13 @@ fn paint_line(
);
}
if let Some((strikethrough_start, strikethrough_style)) = current_strikethrough.take() {
cx.paint_strikethrough(
strikethrough_start,
last_line_end_x - strikethrough_start.x,
&strikethrough_style,
);
}
Ok(())
}

View file

@ -225,6 +225,7 @@ mod tests {
font: font("Helvetica"),
color: Default::default(),
underline: Default::default(),
strikethrough: None,
background_color: None,
};
let bold = TextRun {
@ -232,6 +233,7 @@ mod tests {
font: font("Helvetica").bold(),
color: Default::default(),
underline: Default::default(),
strikethrough: None,
background_color: None,
};