Skip to content

Commit

Permalink
gpui & ui: Use shader for dashed dividers (#23839)
Browse files Browse the repository at this point in the history
TODO:
- [x] BackgroundOrientation
- [x] PatternDash
- [x] `pattern_horizontal_dash` & `pattern_vertical_dash`
- [x] Metal dash shader
- [x] Blade dash shader
- [x] Update ui::Divider to use new pattern

---

This PR introduces proper dashed dividers using the new `PatternDash`
background shader.

![CleanShot 2025-01-29 at 09 33
06@2x](https://github.com/user-attachments/assets/2db5af58-1aa9-4ad7-aa52-b9046fbf8584)

Before this we were using 128 elements to create a dashed divider, which
is both expensive, and would not scale beyond a certain size. This
allows us to simplify the divider element as well.

Changes:

- Adds `BackgroundOrientation` to `gpui::color::Background` to allow
specifying a direction for a pattern
- Adds the PatternDash pattern variant
- Updates `ui::Divider`'s dashed variants to be more efficient

Misc:
- Documents the `ui::Divider` component
- Treat `.metal` files as `C` in the Zed project until we get some metal
syntax highlighting.

Release Notes:

- N/A
  • Loading branch information
iamnbutler authored Jan 29, 2025
1 parent 8442e2b commit e594397
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 72 deletions.
1 change: 1 addition & 0 deletions .zed/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
}
},
"file_types": {
"C": ["metal"],
"Dockerfile": ["Dockerfile*[!dockerignore]"],
"Git Ignore": ["dockerignore"]
},
Expand Down
57 changes: 55 additions & 2 deletions crates/gpui/examples/pattern.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use gpui::{
div, linear_color_stop, linear_gradient, pattern_slash, prelude::*, px, rgb, size, App,
AppContext, Application, Bounds, Context, Window, WindowBounds, WindowOptions,
div, linear_color_stop, linear_gradient, pattern_horizontal_dash, pattern_slash,
pattern_vertical_dash, prelude::*, px, rgb, size, App, AppContext, Application, Bounds,
Context, Window, WindowBounds, WindowOptions,
};

struct PatternExample;
Expand All @@ -19,6 +20,58 @@ impl Render for PatternExample {
.text_xl()
.text_color(rgb(0x000000))
.child("Pattern Example")
.child(
div()
.flex()
.gap_4()
.child(
div()
.flex()
.flex_col()
.gap_1()
.child(
div()
.w(px(160.0))
.h(px(1.0))
.bg(pattern_horizontal_dash(gpui::red())),
)
.child(
div()
.w(px(160.0))
.h(px(4.0))
.bg(pattern_horizontal_dash(gpui::red())),
)
.child(
div()
.w(px(160.0))
.h(px(8.0))
.bg(pattern_horizontal_dash(gpui::red())),
),
)
.child(
div()
.flex()
.gap_1()
.child(
div()
.w(px(1.0))
.h(px(160.0))
.bg(pattern_vertical_dash(gpui::blue())),
)
.child(
div()
.w(px(4.0))
.h(px(160.0))
.bg(pattern_vertical_dash(gpui::blue())),
)
.child(
div()
.w(px(8.0))
.h(px(160.0))
.bg(pattern_vertical_dash(gpui::blue())),
),
),
)
.child(
div()
.flex()
Expand Down
44 changes: 44 additions & 0 deletions crates/gpui/src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,12 +548,33 @@ impl<'de> Deserialize<'de> for Hsla {
}
}

/// The orientation of a background.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum BackgroundOrientation {
/// The background is oriented horizontally.
#[default]
Horizontal = 0,
/// The background is oriented vertically.
Vertical = 1,
}

impl Display for BackgroundOrientation {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
BackgroundOrientation::Horizontal => write!(f, "Horizontal"),
BackgroundOrientation::Vertical => write!(f, "Vertical"),
}
}
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub(crate) enum BackgroundTag {
Solid = 0,
LinearGradient = 1,
PatternSlash = 2,
PatternDash = 3,
}

/// A color space for color interpolation.
Expand Down Expand Up @@ -589,6 +610,7 @@ pub struct Background {
pub(crate) solid: Hsla,
pub(crate) angle: f32,
pub(crate) colors: [LinearColorStop; 2],
pub(crate) orientation: BackgroundOrientation,
/// Padding for alignment for repr(C) layout.
pad: u32,
}
Expand All @@ -602,6 +624,7 @@ impl Default for Background {
color_space: ColorSpace::default(),
angle: 0.0,
colors: [LinearColorStop::default(), LinearColorStop::default()],
orientation: BackgroundOrientation::default(),
pad: 0,
}
}
Expand All @@ -616,6 +639,26 @@ pub fn pattern_slash(color: Hsla) -> Background {
}
}

/// Creates a dash pattern background
pub fn pattern_horizontal_dash(color: Hsla) -> Background {
Background {
tag: BackgroundTag::PatternDash,
orientation: BackgroundOrientation::Horizontal,
solid: color,
..Default::default()
}
}

/// Creates a vertical dash pattern background
pub fn pattern_vertical_dash(color: Hsla) -> Background {
Background {
tag: BackgroundTag::PatternDash,
solid: color,
orientation: BackgroundOrientation::Vertical,
..Default::default()
}
}

/// Creates a LinearGradient background color.
///
/// The gradient line's angle of direction. A value of `0.` is equivalent to to top; increasing values rotate clockwise from there.
Expand Down Expand Up @@ -694,6 +737,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::PatternDash => self.solid.is_transparent(),
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions crates/gpui/src/platform/blade/shaders.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ fn gradient_color(background: Background, position: vec2<f32>, bounds: Bounds,
}
}
case 2u: {
// Slash pattern
let base_pattern_size = bounds.size.y / 5.0;
let width = base_pattern_size * 0.5;
let slash_spacing = 0.89;
Expand All @@ -374,6 +375,21 @@ fn gradient_color(background: Background, position: vec2<f32>, bounds: Bounds,
background_color = sold_color;
background_color.a *= saturate(0.5 - distance);
}
case 3u: {
// Dash pattern
let dash_width = 8.0;
let gap_width = 8.0;
let pattern_width = dash_width + gap_width;
let relative_position = position - bounds.origin;

// Use a dot product to select x or y based on orientation
let orientation_vector = vec2<f32>(1.0 - f32(background.angle != 0.0), f32(background.angle != 0.0));
let pattern_position = fmod(dot(relative_position, orientation_vector), pattern_width);

let distance = pattern_position - dash_width;
background_color = sold_color;
background_color.a *= step(-distance, 0.0);
}
}

return background_color;
Expand Down
25 changes: 19 additions & 6 deletions crates/gpui/src/platform/mac/shaders.metal
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,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);
Expand Down Expand Up @@ -874,13 +874,10 @@ float4 fill_color(Background background,
break;
}
case 2: {
// This pattern is full of magic numbers to make it line up perfectly
// when vertically stacked. Make sure you know what you are doing
// if you change this!

// Slash pattern
float base_pattern_size = bounds.size.height / 5;
float width = base_pattern_size * 0.5;
float slash_spacing = .89;
float slash_spacing = .89; // exact number to make vertical elements line up
float radians = M_PI_F / 4.0;
float2x2 rotation = rotate2d(radians);
float2 relative_position = position - float2(bounds.origin.x, bounds.origin.y);
Expand All @@ -891,6 +888,22 @@ float4 fill_color(Background background,
color.a *= saturate(0.5 - distance);
break;
}
case 3: {
// Dash pattern
float dash_width = 8.0;
float gap_width = 8.0;
float pattern_width = dash_width + gap_width;
float2 relative_position = position - float2(bounds.origin.x, bounds.origin.y);

// Use a dot product to select x or y based on orientation
float2 orientation_vector = float2(1.0 - background.orientation, background.orientation);
float pattern_position = fmod(dot(relative_position, orientation_vector), pattern_width);

float distance = pattern_position - dash_width;
color = solid_color;
color.a *= step(-distance, 0.0);
break;
}
}

return color;
Expand Down
1 change: 1 addition & 0 deletions crates/gpui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ impl Style {
.map(|stop| stop.color)
.unwrap_or_default(),
BackgroundTag::PatternSlash => color.solid,
BackgroundTag::PatternDash => color.solid,
},
None => Hsla::default(),
};
Expand Down
Loading

0 comments on commit e594397

Please sign in to comment.