Detailed changes
@@ -962,6 +962,7 @@ impl AssistantPanel {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
};
EditorElement::new(
@@ -3166,6 +3167,7 @@ impl InlineAssistant {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
};
EditorElement::new(
@@ -360,6 +360,7 @@ impl Render for MessageEditor {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
};
@@ -2068,6 +2068,7 @@ impl CollabPanel {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
};
@@ -9495,6 +9495,7 @@ impl Render for Editor {
line_height: relative(settings.buffer_line_height.value()),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
},
@@ -9508,6 +9509,7 @@ impl Render for Editor {
line_height: relative(settings.buffer_line_height.value()),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
},
};
@@ -1073,6 +1073,7 @@ impl EditorElement {
font: self.style.text.font(),
color: self.style.background,
background_color: None,
+ strikethrough: None,
underline: None,
}],
)
@@ -1713,6 +1714,7 @@ impl EditorElement {
color: Hsla::default(),
background_color: None,
underline: None,
+ strikethrough: None,
}],
)
.unwrap();
@@ -1849,6 +1851,7 @@ impl EditorElement {
color,
background_color: None,
underline: None,
+ strikethrough: None,
};
let shaped_line = cx
.text_system()
@@ -1906,6 +1909,7 @@ impl EditorElement {
color: placeholder_color,
background_color: None,
underline: Default::default(),
+ strikethrough: None,
};
cx.text_system()
.shape_line(line.to_string().into(), font_size, &[run])
@@ -2321,6 +2325,7 @@ impl EditorElement {
color: cx.theme().colors().editor_invisible,
background_color: None,
underline: None,
+ strikethrough: None,
}],
)
.unwrap();
@@ -2335,6 +2340,7 @@ impl EditorElement {
color: cx.theme().colors().editor_invisible,
background_color: None,
underline: None,
+ strikethrough: None,
}],
)
.unwrap();
@@ -2868,6 +2874,7 @@ impl LineWithInvisibles {
color: text_style.color,
background_color: text_style.background_color,
underline: text_style.underline,
+ strikethrough: text_style.strikethrough,
});
if editor_mode == EditorMode::Full {
@@ -3281,6 +3288,7 @@ fn layout_line(
color: Hsla::default(),
background_color: None,
underline: None,
+ strikethrough: None,
}],
)
}
@@ -197,6 +197,9 @@ pub struct TextStyle {
/// The underline style of the text
pub underline: Option<UnderlineStyle>,
+ /// The strikethrough style of the text
+ pub strikethrough: Option<StrikethroughStyle>,
+
/// How to handle whitespace in the text
pub white_space: WhiteSpace,
}
@@ -214,6 +217,7 @@ impl Default for TextStyle {
font_style: FontStyle::default(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
}
}
@@ -246,6 +250,10 @@ impl TextStyle {
self.underline = Some(underline);
}
+ if let Some(strikethrough) = style.strikethrough {
+ self.strikethrough = Some(strikethrough);
+ }
+
self
}
@@ -277,6 +285,7 @@ impl TextStyle {
color: self.color,
background_color: self.background_color,
underline: self.underline,
+ strikethrough: self.strikethrough,
}
}
}
@@ -300,6 +309,9 @@ pub struct HighlightStyle {
/// The underline style of the text
pub underline: Option<UnderlineStyle>,
+ /// The underline style of the text
+ pub strikethrough: Option<StrikethroughStyle>,
+
/// Similar to the CSS `opacity` property, this will cause the text to be less vibrant.
pub fade_out: Option<f32>,
}
@@ -553,6 +565,17 @@ pub struct UnderlineStyle {
pub wavy: bool,
}
+/// The properties that can be applied to a strikethrough.
+#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
+#[refineable(Debug)]
+pub struct StrikethroughStyle {
+ /// The thickness of the strikethrough.
+ pub thickness: Pixels,
+
+ /// The color of the strikethrough.
+ pub color: Option<Hsla>,
+}
+
/// The kinds of fill that can be applied to a shape.
#[derive(Clone, Debug)]
pub enum Fill {
@@ -601,6 +624,7 @@ impl From<&TextStyle> for HighlightStyle {
font_style: Some(other.font_style),
background_color: other.background_color,
underline: other.underline,
+ strikethrough: other.strikethrough,
fade_out: None,
}
}
@@ -636,6 +660,10 @@ impl HighlightStyle {
self.underline = other.underline;
}
+ if other.strikethrough.is_some() {
+ self.strikethrough = other.strikethrough;
+ }
+
match (other.fade_out, self.fade_out) {
(Some(source_fade), None) => self.fade_out = Some(source_fade),
(Some(source_fade), Some(dest_fade)) => {
@@ -10,7 +10,7 @@ pub use line_wrapper::*;
use crate::{
px, Bounds, DevicePixels, EntityId, Hsla, Pixels, PlatformTextSystem, Point, Result,
- SharedString, Size, UnderlineStyle,
+ SharedString, Size, StrikethroughStyle, UnderlineStyle,
};
use anyhow::anyhow;
use collections::{BTreeSet, FxHashMap, FxHashSet};
@@ -317,6 +317,7 @@ impl WindowTextSystem {
if let Some(last_run) = decoration_runs.last_mut() {
if last_run.color == run.color
&& last_run.underline == run.underline
+ && last_run.strikethrough == run.strikethrough
&& last_run.background_color == run.background_color
{
last_run.len += run.len as u32;
@@ -328,6 +329,7 @@ impl WindowTextSystem {
color: run.color,
background_color: run.background_color,
underline: run.underline,
+ strikethrough: run.strikethrough,
});
}
@@ -382,6 +384,7 @@ impl WindowTextSystem {
if decoration_runs.last().map_or(false, |last_run| {
last_run.color == run.color
&& last_run.underline == run.underline
+ && last_run.strikethrough == run.strikethrough
&& last_run.background_color == run.background_color
}) {
decoration_runs.last_mut().unwrap().len += run_len_within_line as u32;
@@ -391,6 +394,7 @@ impl WindowTextSystem {
color: run.color,
background_color: run.background_color,
underline: run.underline,
+ strikethrough: run.strikethrough,
});
}
@@ -406,6 +410,7 @@ impl WindowTextSystem {
let layout = self
.line_layout_cache
.layout_wrapped_line(&line_text, font_size, &font_runs, wrap_width);
+
lines.push(WrappedLine {
layout,
decoration_runs,
@@ -599,6 +604,8 @@ pub struct TextRun {
pub background_color: Option<Hsla>,
/// The underline style (if any)
pub underline: Option<UnderlineStyle>,
+ /// The strikethrough style (if any)
+ pub strikethrough: Option<StrikethroughStyle>,
}
/// An identifier for a specific glyph, as returned by [`TextSystem::layout_line`].
@@ -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(())
}
@@ -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,
};
@@ -34,8 +34,8 @@ use crate::{
InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad,
Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams,
RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingContext,
- StackingOrder, Style, Surface, TextStyleRefinement, Underline, UnderlineStyle, Window,
- WindowContext, SUBPIXEL_VARIANTS,
+ StackingOrder, StrikethroughStyle, Style, Surface, TextStyleRefinement, Underline,
+ UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS,
};
type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
@@ -758,6 +758,38 @@ impl<'a> ElementContext<'a> {
);
}
+ /// Paint a strikethrough into the scene for the next frame at the current z-index.
+ pub fn paint_strikethrough(
+ &mut self,
+ origin: Point<Pixels>,
+ width: Pixels,
+ style: &StrikethroughStyle,
+ ) {
+ let scale_factor = self.scale_factor();
+ let height = style.thickness;
+ let bounds = Bounds {
+ origin,
+ size: size(width, height),
+ };
+ let content_mask = self.content_mask();
+ let view_id = self.parent_view_id();
+
+ let window = &mut *self.window;
+ window.next_frame.scene.insert(
+ &window.next_frame.z_index_stack,
+ Underline {
+ view_id: view_id.into(),
+ layer_id: 0,
+ order: 0,
+ bounds: bounds.scale(scale_factor),
+ content_mask: content_mask.scale(scale_factor),
+ thickness: style.thickness.scale(scale_factor),
+ color: style.color.unwrap_or_default(),
+ wavy: false,
+ },
+ );
+ }
+
/// Paints a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
///
/// The y component of the origin is the baseline of the glyph.
@@ -282,6 +282,7 @@ impl PickerDelegate for OutlineViewDelegate {
line_height: relative(1.).into(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
};
@@ -88,6 +88,7 @@ impl BufferSearchBar {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
};
@@ -1632,6 +1632,7 @@ impl ProjectSearchBar {
line_height: relative(1.3).into(),
background_color: None,
underline: None,
+ strikethrough: None,
white_space: WhiteSpace::Normal,
};
@@ -362,6 +362,7 @@ impl TerminalElement {
..text_style.font()
},
underline,
+ strikethrough: None,
};
if let Some((style, range)) = hyperlink {
@@ -414,6 +415,7 @@ impl TerminalElement {
color: Some(theme.colors().link_text_hover),
wavy: false,
}),
+ strikethrough: None,
fade_out: None,
};
@@ -427,6 +429,7 @@ impl TerminalElement {
white_space: WhiteSpace::Normal,
// These are going to be overridden per-cell
underline: None,
+ strikethrough: None,
color: theme.colors().text,
font_weight: FontWeight::NORMAL,
};
@@ -545,6 +548,7 @@ impl TerminalElement {
color: theme.colors().terminal_background,
background_color: None,
underline: Default::default(),
+ strikethrough: None,
}],
)
.unwrap()