Detailed changes
@@ -8,7 +8,7 @@ use collections::{BTreeMap, HashMap};
use gpui::{
color::Color,
elements::layout_highlighted_chunks,
- fonts::HighlightStyle,
+ fonts::{HighlightStyle, Underline},
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
@@ -540,12 +540,12 @@ impl EditorElement {
.chunks(rows.clone(), Some(&style.syntax))
.map(|chunk| {
let highlight = if let Some(severity) = chunk.diagnostic {
- let underline = Some(
- super::diagnostic_style(severity, true, style)
- .message
- .text
- .color,
- );
+ let diagnostic_style = super::diagnostic_style(severity, true, style);
+ let underline = Some(Underline {
+ color: diagnostic_style.message.text.color,
+ thickness: 1.0.into(),
+ squiggly: true,
+ });
if let Some(mut highlight) = chunk.highlight_style {
highlight.underline = underline;
Some(highlight)
@@ -10,6 +10,7 @@ pub use font_kit::{
metrics::Metrics,
properties::{Properties, Stretch, Style, Weight},
};
+use ordered_float::OrderedFloat;
use serde::{de, Deserialize};
use serde_json::Value;
use std::{cell::RefCell, sync::Arc};
@@ -27,14 +28,21 @@ pub struct TextStyle {
pub font_id: FontId,
pub font_size: f32,
pub font_properties: Properties,
- pub underline: Option<Color>,
+ pub underline: Option<Underline>,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct HighlightStyle {
pub color: Color,
pub font_properties: Properties,
- pub underline: Option<Color>,
+ pub underline: Option<Underline>,
+}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub struct Underline {
+ pub color: Color,
+ pub thickness: OrderedFloat<f32>,
+ pub squiggly: bool,
}
#[allow(non_camel_case_types)]
@@ -81,7 +89,14 @@ struct HighlightStyleJson {
#[serde(untagged)]
enum UnderlineStyleJson {
Underlined(bool),
- UnderlinedWithColor(Color),
+ UnderlinedWithProperties {
+ #[serde(default)]
+ color: Option<Color>,
+ #[serde(default)]
+ thickness: Option<f32>,
+ #[serde(default)]
+ squiggly: bool,
+ },
}
impl TextStyle {
@@ -89,7 +104,7 @@ impl TextStyle {
font_family_name: impl Into<Arc<str>>,
font_size: f32,
font_properties: Properties,
- underline: Option<Color>,
+ underline: Option<Underline>,
color: Color,
font_cache: &FontCache,
) -> anyhow::Result<Self> {
@@ -276,11 +291,23 @@ impl<'de> Deserialize<'de> for HighlightStyle {
}
}
-fn underline_from_json(json: UnderlineStyleJson, text_color: Color) -> Option<Color> {
+fn underline_from_json(json: UnderlineStyleJson, text_color: Color) -> Option<Underline> {
match json {
UnderlineStyleJson::Underlined(false) => None,
- UnderlineStyleJson::Underlined(true) => Some(text_color),
- UnderlineStyleJson::UnderlinedWithColor(color) => Some(color),
+ UnderlineStyleJson::Underlined(true) => Some(Underline {
+ color: text_color,
+ thickness: 1.0.into(),
+ squiggly: false,
+ }),
+ UnderlineStyleJson::UnderlinedWithProperties {
+ color,
+ thickness,
+ squiggly,
+ } => Some(Underline {
+ color: color.unwrap_or(text_color),
+ thickness: thickness.unwrap_or(1.).into(),
+ squiggly,
+ }),
}
}
@@ -6,7 +6,7 @@ use crate::{
vector::{vec2f, vec2i, Vector2F},
},
platform,
- scene::{Glyph, Icon, Image, Layer, Quad, Scene, Shadow},
+ scene::{Glyph, Icon, Image, Layer, Quad, Scene, Shadow, Underline},
};
use cocoa::foundation::NSUInteger;
use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
@@ -334,23 +334,23 @@ impl Renderer {
drawable_size,
command_encoder,
);
- self.render_sprites(
- layer.glyphs(),
- layer.icons(),
+ self.render_underlines(
+ layer.underlines(),
scale_factor,
offset,
drawable_size,
command_encoder,
);
- self.render_images(
- layer.images(),
+ self.render_sprites(
+ layer.glyphs(),
+ layer.icons(),
scale_factor,
offset,
drawable_size,
command_encoder,
);
- self.render_underlines(
- layer.underlines(),
+ self.render_images(
+ layer.images(),
scale_factor,
offset,
drawable_size,
@@ -834,7 +834,7 @@ impl Renderer {
fn render_underlines(
&mut self,
- underlines: &[Quad],
+ underlines: &[Underline],
scale_factor: f32,
offset: &mut usize,
drawable_size: Vector2F,
@@ -874,19 +874,22 @@ impl Renderer {
(self.instances.contents() as *mut u8).offset(*offset as isize)
as *mut shaders::GPUIUnderline
};
- for (ix, quad) in underlines.iter().enumerate() {
- let bounds = quad.bounds * scale_factor;
- let shader_quad = shaders::GPUIUnderline {
- origin: bounds.origin().round().to_float2(),
- size: bounds.size().round().to_float2(),
- thickness: 1. * scale_factor,
- color: quad
- .background
- .unwrap_or(Color::transparent_black())
- .to_uchar4(),
+ for (ix, underline) in underlines.iter().enumerate() {
+ let origin = underline.origin * scale_factor;
+ let mut height = underline.thickness;
+ if underline.squiggly {
+ height *= 3.;
+ }
+ let size = vec2f(underline.width, height) * scale_factor;
+ let shader_underline = shaders::GPUIUnderline {
+ origin: origin.round().to_float2(),
+ size: size.round().to_float2(),
+ thickness: underline.thickness * scale_factor,
+ color: underline.color.to_uchar4(),
+ squiggly: underline.squiggly as u8,
};
unsafe {
- *(buffer_contents.offset(ix as isize)) = shader_quad;
+ *(buffer_contents.offset(ix as isize)) = shader_underline;
}
}
@@ -118,4 +118,5 @@ typedef struct
vector_float2 size;
float thickness;
vector_uchar4 color;
+ uint8_t squiggly;
} GPUIUnderline;
@@ -311,6 +311,7 @@ struct UnderlineFragmentInput {
float2 size;
float thickness;
float4 color;
+ bool squiggly;
};
vertex UnderlineFragmentInput underline_vertex(
@@ -331,22 +332,27 @@ vertex UnderlineFragmentInput underline_vertex(
underline.size,
underline.thickness,
coloru_to_colorf(underline.color),
+ underline.squiggly != 0,
};
}
fragment float4 underline_fragment(
UnderlineFragmentInput input [[stage_in]]
) {
- float half_thickness = input.thickness * 0.5;
- float2 st = ((input.position.xy - input.origin) / input.size.y) - float2(0., 0.5);
- float frequency = M_PI_F * 0.75;
- float amplitude = 0.3;
- float sine = sin(st.x * frequency) * amplitude;
- float dSine = cos(st.x * frequency) * amplitude * frequency;
- float distance = (st.y - sine) / sqrt(1. + dSine * dSine);
- float distance_in_pixels = distance * input.size.y;
- float distance_from_top_border = distance_in_pixels - half_thickness;
- float distance_from_bottom_border = distance_in_pixels + half_thickness;
- float alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border));
- return input.color * float4(1., 1., 1., alpha);
+ if (input.squiggly) {
+ float half_thickness = input.thickness * 0.5;
+ float2 st = ((input.position.xy - input.origin) / input.size.y) - float2(0., 0.5);
+ float frequency = (M_PI_F * (3. * input.thickness)) / 8.;
+ float amplitude = 1. / (2. * input.thickness);
+ float sine = sin(st.x * frequency) * amplitude;
+ float dSine = cos(st.x * frequency) * amplitude * frequency;
+ float distance = (st.y - sine) / sqrt(1. + dSine * dSine);
+ float distance_in_pixels = distance * input.size.y;
+ float distance_from_top_border = distance_in_pixels - half_thickness;
+ float distance_from_bottom_border = distance_in_pixels + half_thickness;
+ float alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border));
+ return input.color * float4(1., 1., 1., alpha);
+ } else {
+ return input.color;
+ }
}
@@ -25,7 +25,7 @@ struct StackingContext {
pub struct Layer {
clip_bounds: Option<RectF>,
quads: Vec<Quad>,
- underlines: Vec<Quad>,
+ underlines: Vec<Underline>,
images: Vec<Image>,
shadows: Vec<Shadow>,
glyphs: Vec<Glyph>,
@@ -76,6 +76,15 @@ pub struct Border {
pub left: bool,
}
+#[derive(Clone, Copy, Default, Debug)]
+pub struct Underline {
+ pub origin: Vector2F,
+ pub width: f32,
+ pub thickness: f32,
+ pub color: Color,
+ pub squiggly: bool,
+}
+
impl<'de> Deserialize<'de> for Border {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -183,7 +192,7 @@ impl Scene {
self.active_layer().push_image(image)
}
- pub fn push_underline(&mut self, underline: Quad) {
+ pub fn push_underline(&mut self, underline: Underline) {
self.active_layer().push_underline(underline)
}
@@ -277,11 +286,11 @@ impl Layer {
self.quads.as_slice()
}
- fn push_underline(&mut self, underline: Quad) {
+ fn push_underline(&mut self, underline: Underline) {
self.underlines.push(underline);
}
- pub fn underlines(&self) -> &[Quad] {
+ pub fn underlines(&self) -> &[Underline] {
self.underlines.as_slice()
}
@@ -1,6 +1,6 @@
use crate::{
color::Color,
- fonts::{FontId, GlyphId},
+ fonts::{FontId, GlyphId, Underline},
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
@@ -28,7 +28,7 @@ pub struct TextLayoutCache {
pub struct RunStyle {
pub color: Color,
pub font_id: FontId,
- pub underline: Option<Color>,
+ pub underline: Option<Underline>,
}
impl TextLayoutCache {
@@ -167,7 +167,7 @@ impl<'a> Hash for CacheKeyRef<'a> {
#[derive(Default, Debug)]
pub struct Line {
layout: Arc<LineLayout>,
- style_runs: SmallVec<[(u32, Color, Option<Color>); 32]>,
+ style_runs: SmallVec<[(u32, Color, Option<Underline>); 32]>,
}
#[derive(Default, Debug)]
@@ -265,14 +265,14 @@ impl Line {
let mut finished_underline = None;
if glyph.index >= run_end {
- if let Some((run_len, run_color, run_underline_color)) = style_runs.next() {
- if let Some((_, underline_color)) = underline {
- if *run_underline_color != Some(underline_color) {
+ if let Some((run_len, run_color, run_underline)) = style_runs.next() {
+ if let Some((_, underline_style)) = underline {
+ if *run_underline != Some(underline_style) {
finished_underline = underline.take();
}
}
- if let Some(run_underline_color) = run_underline_color {
- underline.get_or_insert((glyph_origin, *run_underline_color));
+ if let Some(run_underline) = run_underline {
+ underline.get_or_insert((glyph_origin, *run_underline));
}
run_end += *run_len as usize;
@@ -288,12 +288,13 @@ impl Line {
continue;
}
- if let Some((underline_origin, underline_color)) = finished_underline {
- cx.scene.push_underline(scene::Quad {
- bounds: RectF::from_points(underline_origin, glyph_origin + vec2f(0., 3.)),
- background: Some(underline_color),
- border: Default::default(),
- corner_radius: 0.,
+ if let Some((underline_origin, underline_style)) = finished_underline {
+ cx.scene.push_underline(scene::Underline {
+ origin: underline_origin,
+ width: glyph_origin.x() - underline_origin.x(),
+ thickness: underline_style.thickness.into(),
+ color: underline_style.color,
+ squiggly: underline_style.squiggly,
});
}
@@ -307,14 +308,14 @@ impl Line {
}
}
- if let Some((underline_start, underline_color)) = underline.take() {
- let line_end = origin + baseline_offset + vec2f(self.layout.width, 0.);
-
- cx.scene.push_underline(scene::Quad {
- bounds: RectF::from_points(underline_start, line_end + vec2f(0., 3.)),
- background: Some(underline_color),
- border: Default::default(),
- corner_radius: 0.,
+ if let Some((underline_start, underline_style)) = underline.take() {
+ let line_end_x = origin.x() + self.layout.width;
+ cx.scene.push_underline(scene::Underline {
+ origin: underline_start,
+ width: line_end_x - underline_start.x(),
+ color: underline_style.color,
+ thickness: underline_style.thickness.into(),
+ squiggly: underline_style.squiggly,
});
}
}