Detailed changes
@@ -47,8 +47,8 @@ use gpui::{
MouseDownEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent, PaintQuad, ParentElement,
Pixels, PressureStage, ScrollDelta, ScrollHandle, ScrollWheelEvent, ShapedLine, SharedString,
Size, StatefulInteractiveElement, Style, Styled, StyledText, TextAlign, TextRun,
- TextStyleRefinement, WeakEntity, Window, anchored, deferred, div, fill, linear_color_stop,
- linear_gradient, outline, pattern_slash, point, px, quad, relative, size, solid_background,
+ TextStyleRefinement, WeakEntity, Window, anchored, checkerboard, deferred, div, fill,
+ linear_color_stop, linear_gradient, outline, point, px, quad, relative, size, solid_background,
transparent_black,
};
use itertools::Itertools;
@@ -60,6 +60,7 @@ use multi_buffer::{
};
use edit_prediction_types::EditPredictionGranularity;
+
use project::{
DisableAiSettings, Entry, ProjectPath,
debugger::breakpoint_store::{Breakpoint, BreakpointSessionState},
@@ -4007,11 +4008,11 @@ impl EditorElement {
.id(block_id)
.w_full()
.h((*height as f32) * line_height)
- .bg(pattern_slash(
- cx.theme().colors().panel_background,
- 8.0,
- 8.0,
- ))
+ .bg(checkerboard(cx.theme().colors().panel_background, {
+ let target_size = 16.0;
+ let scale = window.scale_factor();
+ Self::checkerboard_size(f32::from(line_height) * scale, target_size * scale)
+ }))
.into_any(),
};
@@ -4073,6 +4074,24 @@ impl EditorElement {
Some((element, final_size, row, x_offset))
}
+ /// The checkerboard pattern height must be an even factor of the line
+ /// height, so that two consecutive spacer blocks can render contiguously
+ /// without an obvious break in the pattern.
+ fn checkerboard_size(line_height: f32, target_height: f32) -> f32 {
+ let k_approx = line_height / (2.0 * target_height);
+ let k_floor = (k_approx.floor() as u32).max(1);
+ let k_ceil = (k_approx.ceil() as u32).max(1);
+
+ let size_floor = line_height / (2 * k_floor) as f32;
+ let size_ceil = line_height / (2 * k_ceil) as f32;
+
+ if (size_floor - target_height).abs() <= (size_ceil - target_height).abs() {
+ size_floor
+ } else {
+ size_ceil
+ }
+ }
+
fn render_buffer_header(
&self,
for_excerpt: &ExcerptInfo,
@@ -12123,6 +12142,7 @@ mod tests {
use gpui::{TestAppContext, VisualTestContext};
use language::{Buffer, language_settings, tree_sitter_python};
use log::info;
+ use rand::{RngCore, rngs::StdRng};
use std::num::NonZeroU32;
use util::test::sample_text;
@@ -13224,4 +13244,31 @@ mod tests {
assert_eq!(out[3].color, adjusted_bg1);
}
}
+
+ #[test]
+ fn test_checkerboard_size() {
+ // line height is smaller than target height, so we just return half the line height
+ assert_eq!(EditorElement::checkerboard_size(10.0, 20.0), 5.0);
+
+ // line height is exactly half the target height, perfect match
+ assert_eq!(EditorElement::checkerboard_size(20.0, 10.0), 10.0);
+
+ // line height is close to half the target height
+ assert_eq!(EditorElement::checkerboard_size(20.0, 9.0), 10.0);
+
+ // line height is close to 1/4 the target height
+ assert_eq!(EditorElement::checkerboard_size(20.0, 4.8), 5.0);
+ }
+
+ #[gpui::test(iterations = 100)]
+ fn test_random_checkerboard_size(mut rng: StdRng) {
+ let line_height = rng.next_u32() as f32;
+ let target_height = rng.next_u32() as f32;
+
+ let result = EditorElement::checkerboard_size(line_height, target_height);
+
+ let k = line_height / result;
+ assert!(k - k.round() < 0.0000001); // approximately integer
+ assert!((k.round() as u32).is_multiple_of(2));
+ }
}
@@ -658,6 +658,7 @@ pub(crate) enum BackgroundTag {
Solid = 0,
LinearGradient = 1,
PatternSlash = 2,
+ Checkerboard = 3,
}
/// A color space for color interpolation.
@@ -701,20 +702,21 @@ impl std::fmt::Debug for Background {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.tag {
BackgroundTag::Solid => write!(f, "Solid({:?})", self.solid),
- BackgroundTag::LinearGradient => {
- write!(
- f,
- "LinearGradient({}, {:?}, {:?})",
- self.gradient_angle_or_pattern_height, self.colors[0], self.colors[1]
- )
- }
- BackgroundTag::PatternSlash => {
- write!(
- f,
- "PatternSlash({:?}, {})",
- self.solid, self.gradient_angle_or_pattern_height
- )
- }
+ BackgroundTag::LinearGradient => write!(
+ f,
+ "LinearGradient({}, {:?}, {:?})",
+ self.gradient_angle_or_pattern_height, self.colors[0], self.colors[1]
+ ),
+ BackgroundTag::PatternSlash => write!(
+ f,
+ "PatternSlash({:?}, {})",
+ self.solid, self.gradient_angle_or_pattern_height
+ ),
+ BackgroundTag::Checkerboard => write!(
+ f,
+ "Checkerboard({:?}, {})",
+ self.solid, self.gradient_angle_or_pattern_height
+ ),
}
}
}
@@ -747,6 +749,16 @@ pub fn pattern_slash(color: impl Into<Hsla>, width: f32, interval: f32) -> Backg
}
}
+/// Creates a checkerboard pattern background
+pub fn checkerboard(color: impl Into<Hsla>, size: f32) -> Background {
+ Background {
+ tag: BackgroundTag::Checkerboard,
+ solid: color.into(),
+ gradient_angle_or_pattern_height: size,
+ ..Default::default()
+ }
+}
+
/// Creates a solid background color.
pub fn solid_background(color: impl Into<Hsla>) -> Background {
Background {
@@ -833,6 +845,7 @@ impl Background {
BackgroundTag::Solid => self.solid.is_transparent(),
BackgroundTag::LinearGradient => self.colors.iter().all(|c| c.color.is_transparent()),
BackgroundTag::PatternSlash => self.solid.is_transparent(),
+ BackgroundTag::Checkerboard => self.solid.is_transparent(),
}
}
}
@@ -129,6 +129,7 @@ struct Background {
// 0u is Solid
// 1u is LinearGradient
// 2u is PatternSlash
+ // 3u is Checkerboard
tag: u32,
// 0u is sRGB linear color
// 1u is Oklab color
@@ -399,7 +400,7 @@ fn prepare_gradient_color(tag: u32, color_space: u32,
solid: Hsla, colors: array<LinearColorStop, 2>) -> GradientColor {
var result = GradientColor();
- if (tag == 0u || tag == 2u) {
+ if (tag == 0u || tag == 2u || tag == 3u) {
result.solid = hsla_to_rgba(solid);
} else if (tag == 1u) {
// The hsla_to_rgba is returns a linear sRGB color
@@ -473,6 +474,7 @@ fn gradient_color(background: Background, position: vec2<f32>, bounds: Bounds,
}
}
case 2u: {
+ // pattern slash
let gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height;
let pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f;
let pattern_interval = (gradient_angle_or_pattern_height % 65535.0f) / 255.0f;
@@ -490,6 +492,18 @@ fn gradient_color(background: Background, position: vec2<f32>, bounds: Bounds,
background_color = solid_color;
background_color.a *= saturate(0.5 - distance);
}
+ case 3u: {
+ // checkerboard
+ let size = background.gradient_angle_or_pattern_height;
+ let relative_position = position - bounds.origin;
+
+ let x_index = floor(relative_position.x / size);
+ let y_index = floor(relative_position.y / size);
+ let should_be_colored = (x_index + y_index) % 2.0;
+
+ background_color = solid_color;
+ background_color.a *= saturate(should_be_colored);
+ }
}
return background_color;
@@ -1140,7 +1140,7 @@ float4 over(float4 below, float4 above) {
GradientColor prepare_fill_color(uint tag, uint color_space, Hsla solid,
Hsla color0, Hsla color1) {
GradientColor out;
- if (tag == 0 || tag == 2) {
+ if (tag == 0 || tag == 2 || tag == 3) {
out.solid = hsla_to_rgba(solid);
} else if (tag == 1) {
out.color0 = hsla_to_rgba(color0);
@@ -1233,6 +1233,19 @@ float4 fill_color(Background background,
color.a *= saturate(0.5 - distance);
break;
}
+ case 3: {
+ // checkerboard
+ float size = background.gradient_angle_or_pattern_height;
+ float2 relative_position = position - float2(bounds.origin.x, bounds.origin.y);
+
+ float x_index = floor(relative_position.x / size);
+ float y_index = floor(relative_position.y / size);
+ float should_be_colored = fmod(x_index + y_index, 2.0);
+
+ color = solid_color;
+ color.a *= saturate(should_be_colored);
+ break;
+ }
}
return color;
@@ -309,7 +309,7 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) {
GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, LinearColorStop colors[2]) {
GradientColor output;
- if (tag == 0 || tag == 2) {
+ if (tag == 0 || tag == 2 || tag == 3) {
output.solid = hsla_to_rgba(solid);
} else if (tag == 1) {
output.color0 = hsla_to_rgba(colors[0].color);
@@ -402,6 +402,19 @@ float4 gradient_color(Background background,
color.a *= saturate(0.5 - distance);
break;
}
+ case 3: {
+ // checkerboard
+ float size = background.gradient_angle_or_pattern_height;
+ float2 relative_position = position - bounds.origin;
+
+ float x_index = floor(relative_position.x / size);
+ float y_index = floor(relative_position.y / size);
+ float should_be_colored = (x_index + y_index) % 2.0;
+
+ color = solid_color;
+ color.a *= saturate(should_be_colored);
+ break;
+ }
}
return color;
@@ -639,13 +639,15 @@ impl Style {
if background_color.is_some_and(|color| !color.is_transparent()) {
let mut border_color = match background_color {
Some(color) => match color.tag {
- BackgroundTag::Solid => color.solid,
+ BackgroundTag::Solid
+ | BackgroundTag::PatternSlash
+ | BackgroundTag::Checkerboard => color.solid,
+
BackgroundTag::LinearGradient => color
.colors
.first()
.map(|stop| stop.color)
.unwrap_or_default(),
- BackgroundTag::PatternSlash => color.solid,
},
None => Hsla::default(),
};