Revert "gpui: Fix overflow_hidden to support clip with border radius" (#37480)

localcc created

This reverts commit 40199266b6634cc3165f3842abae1d562ef4dcca.

The issue with the commit is: ContentMask<Pixels>::intersect is doing
intersection of corner radii which makes inner containers use the max
corner radius out of all the parents when it should be more complex to
correctly clip children (clip sorting..?)

Release Notes:

- N/A

Change summary

crates/editor/src/element.rs                  |  25 -
crates/gpui/examples/content_mask.rs          | 228 ---------------------
crates/gpui/src/elements/list.rs              |  78 ++----
crates/gpui/src/elements/uniform_list.rs      |   5 
crates/gpui/src/platform/blade/shaders.wgsl   |  60 +---
crates/gpui/src/platform/mac/shaders.metal    |  24 -
crates/gpui/src/platform/windows/shaders.hlsl |  49 +---
crates/gpui/src/style.rs                      |  66 ++++-
crates/gpui/src/window.rs                     |  23 --
crates/terminal_view/src/terminal_element.rs  |   2 
crates/ui/src/components/scrollbar.rs         |  16 -
11 files changed, 130 insertions(+), 446 deletions(-)

Detailed changes

crates/editor/src/element.rs 🔗

@@ -6043,7 +6043,6 @@ impl EditorElement {
         window.with_content_mask(
             Some(ContentMask {
                 bounds: layout.position_map.text_hitbox.bounds,
-                ..Default::default()
             }),
             |window| {
                 let editor = self.editor.read(cx);
@@ -6986,15 +6985,9 @@ impl EditorElement {
             } else {
                 let mut bounds = layout.hitbox.bounds;
                 bounds.origin.x += layout.gutter_hitbox.bounds.size.width;
-                window.with_content_mask(
-                    Some(ContentMask {
-                        bounds,
-                        ..Default::default()
-                    }),
-                    |window| {
-                        block.element.paint(window, cx);
-                    },
-                )
+                window.with_content_mask(Some(ContentMask { bounds }), |window| {
+                    block.element.paint(window, cx);
+                })
             }
         }
     }
@@ -8297,13 +8290,9 @@ impl Element for EditorElement {
         }
 
         let rem_size = self.rem_size(cx);
-        let content_mask = ContentMask {
-            bounds,
-            ..Default::default()
-        };
         window.with_rem_size(rem_size, |window| {
             window.with_text_style(Some(text_style), |window| {
-                window.with_content_mask(Some(content_mask), |window| {
+                window.with_content_mask(Some(ContentMask { bounds }), |window| {
                     let (mut snapshot, is_read_only) = self.editor.update(cx, |editor, cx| {
                         (editor.snapshot(window, cx), editor.read_only(cx))
                     });
@@ -9411,13 +9400,9 @@ impl Element for EditorElement {
             ..Default::default()
         };
         let rem_size = self.rem_size(cx);
-        let content_mask = ContentMask {
-            bounds,
-            ..Default::default()
-        };
         window.with_rem_size(rem_size, |window| {
             window.with_text_style(Some(text_style), |window| {
-                window.with_content_mask(Some(content_mask), |window| {
+                window.with_content_mask(Some(ContentMask { bounds }), |window| {
                     self.paint_mouse_listeners(layout, window, cx);
                     self.paint_background(layout, window, cx);
                     self.paint_indent_guides(layout, window, cx);

crates/gpui/examples/content_mask.rs 🔗

@@ -1,228 +0,0 @@
-use gpui::{
-    App, Application, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px,
-    rgb, size,
-};
-
-struct Example {}
-
-impl Render for Example {
-    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
-        div()
-            .font_family(".SystemUIFont")
-            .flex()
-            .flex_col()
-            .size_full()
-            .p_4()
-            .gap_4()
-            .bg(rgb(0x505050))
-            .justify_center()
-            .items_center()
-            .text_center()
-            .shadow_lg()
-            .text_sm()
-            .text_color(rgb(0xffffff))
-            .child(
-                div()
-                    .overflow_hidden()
-                    .rounded(px(32.))
-                    .border(px(8.))
-                    .border_color(gpui::white())
-                    .text_color(gpui::white())
-                    .child(
-                        div()
-                            .bg(gpui::black())
-                            .py_2()
-                            .px_7()
-                            .border_l_2()
-                            .border_r_2()
-                            .border_b_3()
-                            .border_color(gpui::red())
-                            .child("Let build applications with GPUI"),
-                    )
-                    .child(
-                        div()
-                            .bg(rgb(0x222222))
-                            .text_sm()
-                            .py_1()
-                            .px_7()
-                            .border_l_3()
-                            .border_r_3()
-                            .border_color(gpui::green())
-                            .child("The fast, productive UI framework for Rust"),
-                    )
-                    .child(
-                        div()
-                            .bg(rgb(0x222222))
-                            .w_full()
-                            .flex()
-                            .flex_row()
-                            .text_sm()
-                            .text_color(rgb(0xc0c0c0))
-                            .child(
-                                div()
-                                    .flex_1()
-                                    .p_2()
-                                    .border_3()
-                                    .border_dashed()
-                                    .border_color(gpui::blue())
-                                    .child("Rust"),
-                            )
-                            .child(
-                                div()
-                                    .flex_1()
-                                    .p_2()
-                                    .border_t_3()
-                                    .border_r_3()
-                                    .border_b_3()
-                                    .border_dashed()
-                                    .border_color(gpui::blue())
-                                    .child("GPU Rendering"),
-                            ),
-                    ),
-            )
-            .child(
-                div()
-                    .flex()
-                    .flex_col()
-                    .w(px(320.))
-                    .gap_1()
-                    .overflow_hidden()
-                    .rounded(px(16.))
-                    .child(
-                        div()
-                            .w_full()
-                            .p_2()
-                            .bg(gpui::red())
-                            .child("Clip background"),
-                    ),
-            )
-            .child(
-                div()
-                    .flex()
-                    .flex_col()
-                    .w(px(320.))
-                    .gap_1()
-                    .rounded(px(16.))
-                    .child(
-                        div()
-                            .w_full()
-                            .p_2()
-                            .bg(gpui::yellow())
-                            .text_color(gpui::black())
-                            .child("No content mask"),
-                    ),
-            )
-            .child(
-                div()
-                    .flex()
-                    .flex_col()
-                    .w(px(320.))
-                    .gap_1()
-                    .overflow_hidden()
-                    .rounded(px(16.))
-                    .child(
-                        div()
-                            .w_full()
-                            .p_2()
-                            .border_4()
-                            .border_color(gpui::blue())
-                            .bg(gpui::blue().alpha(0.4))
-                            .child("Clip borders"),
-                    ),
-            )
-            .child(
-                div()
-                    .flex()
-                    .flex_col()
-                    .w(px(320.))
-                    .gap_1()
-                    .overflow_hidden()
-                    .rounded(px(20.))
-                    .child(
-                        div().w_full().border_2().border_color(gpui::black()).child(
-                            div()
-                                .size_full()
-                                .bg(gpui::green().alpha(0.4))
-                                .p_2()
-                                .border_8()
-                                .border_color(gpui::green())
-                                .child("Clip nested elements"),
-                        ),
-                    ),
-            )
-            .child(
-                div()
-                    .flex()
-                    .flex_col()
-                    .w(px(320.))
-                    .gap_1()
-                    .overflow_hidden()
-                    .rounded(px(32.))
-                    .child(
-                        div()
-                            .w_full()
-                            .p_2()
-                            .bg(gpui::black())
-                            .border_2()
-                            .border_dashed()
-                            .rounded_lg()
-                            .border_color(gpui::white())
-                            .child("dash border full and rounded"),
-                    )
-                    .child(
-                        div()
-                            .w_full()
-                            .flex()
-                            .flex_row()
-                            .gap_2()
-                            .child(
-                                div()
-                                    .w_full()
-                                    .p_2()
-                                    .bg(gpui::black())
-                                    .border_x_2()
-                                    .border_dashed()
-                                    .rounded_lg()
-                                    .border_color(gpui::white())
-                                    .child("border x"),
-                            )
-                            .child(
-                                div()
-                                    .w_full()
-                                    .p_2()
-                                    .bg(gpui::black())
-                                    .border_y_2()
-                                    .border_dashed()
-                                    .rounded_lg()
-                                    .border_color(gpui::white())
-                                    .child("border y"),
-                            ),
-                    )
-                    .child(
-                        div()
-                            .w_full()
-                            .p_2()
-                            .bg(gpui::black())
-                            .border_2()
-                            .border_dashed()
-                            .border_color(gpui::white())
-                            .child("border full and no rounded"),
-                    ),
-            )
-    }
-}
-
-fn main() {
-    Application::new().run(|cx: &mut App| {
-        let bounds = Bounds::centered(None, size(px(800.), px(600.)), cx);
-        cx.open_window(
-            WindowOptions {
-                window_bounds: Some(WindowBounds::Windowed(bounds)),
-                ..Default::default()
-            },
-            |_, cx| cx.new(|_| Example {}),
-        )
-        .unwrap();
-        cx.activate(true);
-    });
-}

crates/gpui/src/elements/list.rs 🔗

@@ -8,10 +8,10 @@
 //! If all of your elements are the same height, see [`crate::UniformList`] for a simpler API
 
 use crate::{
-    AnyElement, App, AvailableSpace, Bounds, ContentMask, Corners, DispatchPhase, Edges, Element,
-    EntityId, FocusHandle, GlobalElementId, Hitbox, HitboxBehavior, InspectorElementId,
-    IntoElement, Overflow, Pixels, Point, ScrollDelta, ScrollWheelEvent, Size, Style,
-    StyleRefinement, Styled, Window, point, px, size,
+    AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, Element, EntityId,
+    FocusHandle, GlobalElementId, Hitbox, HitboxBehavior, InspectorElementId, IntoElement,
+    Overflow, Pixels, Point, ScrollDelta, ScrollWheelEvent, Size, Style, StyleRefinement, Styled,
+    Window, point, px, size,
 };
 use collections::VecDeque;
 use refineable::Refineable as _;
@@ -705,7 +705,6 @@ impl StateInner {
         &mut self,
         bounds: Bounds<Pixels>,
         padding: Edges<Pixels>,
-        corner_radii: Corners<Pixels>,
         autoscroll: bool,
         render_item: &mut RenderItemFn,
         window: &mut Window,
@@ -729,15 +728,9 @@ impl StateInner {
                 let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
                 item_origin.y -= layout_response.scroll_top.offset_in_item;
                 for item in &mut layout_response.item_layouts {
-                    window.with_content_mask(
-                        Some(ContentMask {
-                            bounds,
-                            corner_radii,
-                        }),
-                        |window| {
-                            item.element.prepaint_at(item_origin, window, cx);
-                        },
-                    );
+                    window.with_content_mask(Some(ContentMask { bounds }), |window| {
+                        item.element.prepaint_at(item_origin, window, cx);
+                    });
 
                     if let Some(autoscroll_bounds) = window.take_autoscroll()
                         && autoscroll
@@ -959,34 +952,19 @@ impl Element for List {
             state.items = new_items;
         }
 
-        let rem_size = window.rem_size();
-        let padding = style.padding.to_pixels(bounds.size.into(), rem_size);
-        let corner_radii = style.corner_radii.to_pixels(rem_size);
-        let layout = match state.prepaint_items(
-            bounds,
-            padding,
-            corner_radii,
-            true,
-            &mut self.render_item,
-            window,
-            cx,
-        ) {
-            Ok(layout) => layout,
-            Err(autoscroll_request) => {
-                state.logical_scroll_top = Some(autoscroll_request);
-                state
-                    .prepaint_items(
-                        bounds,
-                        padding,
-                        corner_radii,
-                        false,
-                        &mut self.render_item,
-                        window,
-                        cx,
-                    )
-                    .unwrap()
-            }
-        };
+        let padding = style
+            .padding
+            .to_pixels(bounds.size.into(), window.rem_size());
+        let layout =
+            match state.prepaint_items(bounds, padding, true, &mut self.render_item, window, cx) {
+                Ok(layout) => layout,
+                Err(autoscroll_request) => {
+                    state.logical_scroll_top = Some(autoscroll_request);
+                    state
+                        .prepaint_items(bounds, padding, false, &mut self.render_item, window, cx)
+                        .unwrap()
+                }
+            };
 
         state.last_layout_bounds = Some(bounds);
         state.last_padding = Some(padding);
@@ -1004,17 +982,11 @@ impl Element for List {
         cx: &mut App,
     ) {
         let current_view = window.current_view();
-        window.with_content_mask(
-            Some(ContentMask {
-                bounds,
-                ..Default::default()
-            }),
-            |window| {
-                for item in &mut prepaint.layout.item_layouts {
-                    item.element.paint(window, cx);
-                }
-            },
-        );
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
+            for item in &mut prepaint.layout.item_layouts {
+                item.element.paint(window, cx);
+            }
+        });
 
         let list_state = self.state.clone();
         let height = bounds.size.height;

crates/gpui/src/elements/uniform_list.rs 🔗

@@ -411,10 +411,7 @@ impl Element for UniformList {
                         (self.render_items)(visible_range.clone(), window, cx)
                     };
 
-                    let content_mask = ContentMask {
-                        bounds,
-                        ..Default::default()
-                    };
+                    let content_mask = ContentMask { bounds };
                     window.with_content_mask(Some(content_mask), |window| {
                         for (mut item, ix) in items.into_iter().zip(visible_range.clone()) {
                             let item_origin = padded_bounds.origin

crates/gpui/src/platform/blade/shaders.wgsl 🔗

@@ -53,11 +53,6 @@ struct Corners {
     bottom_left: f32,
 }
 
-struct ContentMask {
-    bounds: Bounds,
-    corner_radii: Corners,
-}
-
 struct Edges {
     top: f32,
     right: f32,
@@ -445,7 +440,7 @@ struct Quad {
     order: u32,
     border_style: u32,
     bounds: Bounds,
-    content_mask: ContentMask,
+    content_mask: Bounds,
     background: Background,
     border_color: Hsla,
     corner_radii: Corners,
@@ -483,7 +478,7 @@ fn vs_quad(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) insta
     out.background_color1 = gradient.color1;
     out.border_color = hsla_to_rgba(quad.border_color);
     out.quad_id = instance_id;
-    out.clip_distances = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask.bounds);
+    out.clip_distances = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask);
     return out;
 }
 
@@ -496,19 +491,8 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
 
     let quad = b_quads[input.quad_id];
 
-    // Signed distance field threshold for inclusion of pixels. 0.5 is the
-    // minimum distance between the center of the pixel and the edge.
-    let antialias_threshold = 0.5;
-
-    var background_color = gradient_color(quad.background, input.position.xy, quad.bounds,
+    let background_color = gradient_color(quad.background, input.position.xy, quad.bounds,
         input.background_solid, input.background_color0, input.background_color1);
-    var border_color = input.border_color;
-
-    // Apply content_mask corner radii clipping
-    let clip_sdf = quad_sdf(input.position.xy, quad.content_mask.bounds, quad.content_mask.corner_radii);
-    let clip_alpha = saturate(antialias_threshold - clip_sdf);
-    background_color.a *= clip_alpha;
-    border_color.a *= clip_alpha;
 
     let unrounded = quad.corner_radii.top_left == 0.0 &&
         quad.corner_radii.bottom_left == 0.0 &&
@@ -529,6 +513,10 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
     let point = input.position.xy - quad.bounds.origin;
     let center_to_point = point - half_size;
 
+    // Signed distance field threshold for inclusion of pixels. 0.5 is the
+    // minimum distance between the center of the pixel and the edge.
+    let antialias_threshold = 0.5;
+
     // Radius of the nearest corner
     let corner_radius = pick_corner_radius(center_to_point, quad.corner_radii);
 
@@ -619,6 +607,8 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
 
     var color = background_color;
     if (border_sdf < antialias_threshold) {
+        var border_color = input.border_color;
+
         // Dashed border logic when border_style == 1
         if (quad.border_style == 1) {
             // Position along the perimeter in "dash space", where each dash
@@ -654,11 +644,7 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
                 let is_horizontal =
                         corner_center_to_point.x <
                         corner_center_to_point.y;
-                var border_width = select(border.y, border.x, is_horizontal);
-                // When border width of some side is 0, we need to use the other side width for dash velocity.
-                if (border_width == 0.0) {
-                    border_width = select(border.x, border.y, is_horizontal);
-                }
+                let border_width = select(border.y, border.x, is_horizontal);
                 dash_velocity = dv_numerator / border_width;
                 t = select(point.y, point.x, is_horizontal) * dash_velocity;
                 max_t = select(size.y, size.x, is_horizontal) * dash_velocity;
@@ -870,7 +856,7 @@ struct Shadow {
     blur_radius: f32,
     bounds: Bounds,
     corner_radii: Corners,
-    content_mask: ContentMask,
+    content_mask: Bounds,
     color: Hsla,
 }
 var<storage, read> b_shadows: array<Shadow>;
@@ -898,7 +884,7 @@ fn vs_shadow(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) ins
     out.position = to_device_position(unit_vertex, shadow.bounds);
     out.color = hsla_to_rgba(shadow.color);
     out.shadow_id = instance_id;
-    out.clip_distances = distance_from_clip_rect(unit_vertex, shadow.bounds, shadow.content_mask.bounds);
+    out.clip_distances = distance_from_clip_rect(unit_vertex, shadow.bounds, shadow.content_mask);
     return out;
 }
 
@@ -913,6 +899,7 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4<f32> {
     let half_size = shadow.bounds.size / 2.0;
     let center = shadow.bounds.origin + half_size;
     let center_to_point = input.position.xy - center;
+
     let corner_radius = pick_corner_radius(center_to_point, shadow.corner_radii);
 
     // The signal is only non-zero in a limited range, so don't waste samples
@@ -1040,7 +1027,7 @@ struct Underline {
     order: u32,
     pad: u32,
     bounds: Bounds,
-    content_mask: ContentMask,
+    content_mask: Bounds,
     color: Hsla,
     thickness: f32,
     wavy: u32,
@@ -1064,7 +1051,7 @@ fn vs_underline(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index)
     out.position = to_device_position(unit_vertex, underline.bounds);
     out.color = hsla_to_rgba(underline.color);
     out.underline_id = instance_id;
-    out.clip_distances = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask.bounds);
+    out.clip_distances = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask);
     return out;
 }
 
@@ -1106,7 +1093,7 @@ struct MonochromeSprite {
     order: u32,
     pad: u32,
     bounds: Bounds,
-    content_mask: ContentMask,
+    content_mask: Bounds,
     color: Hsla,
     tile: AtlasTile,
     transformation: TransformationMatrix,
@@ -1130,7 +1117,7 @@ fn vs_mono_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index
 
     out.tile_position = to_tile_position(unit_vertex, sprite.tile);
     out.color = hsla_to_rgba(sprite.color);
-    out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask.bounds);
+    out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask);
     return out;
 }
 
@@ -1152,7 +1139,7 @@ struct PolychromeSprite {
     grayscale: u32,
     opacity: f32,
     bounds: Bounds,
-    content_mask: ContentMask,
+    content_mask: Bounds,
     corner_radii: Corners,
     tile: AtlasTile,
 }
@@ -1174,7 +1161,7 @@ fn vs_poly_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index
     out.position = to_device_position(unit_vertex, sprite.bounds);
     out.tile_position = to_tile_position(unit_vertex, sprite.tile);
     out.sprite_id = instance_id;
-    out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask.bounds);
+    out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask);
     return out;
 }
 
@@ -1247,12 +1234,3 @@ fn fs_surface(input: SurfaceVarying) -> @location(0) vec4<f32> {
 
     return ycbcr_to_RGB * y_cb_cr;
 }
-
-fn max_corner_radii(a: Corners, b: Corners) -> Corners {
-    return Corners(
-        max(a.top_left, b.top_left),
-        max(a.top_right, b.top_right),
-        max(a.bottom_right, b.bottom_right),
-        max(a.bottom_left, b.bottom_left)
-    );
-}

crates/gpui/src/platform/mac/shaders.metal 🔗

@@ -99,21 +99,8 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
                               constant Quad *quads
                               [[buffer(QuadInputIndex_Quads)]]) {
   Quad quad = quads[input.quad_id];
-
-  // Signed distance field threshold for inclusion of pixels. 0.5 is the
-  // minimum distance between the center of the pixel and the edge.
-  const float antialias_threshold = 0.5;
-
   float4 background_color = fill_color(quad.background, input.position.xy, quad.bounds,
     input.background_solid, input.background_color0, input.background_color1);
-  float4 border_color = input.border_color;
-
-  // Apply content_mask corner radii clipping
-  float clip_sdf = quad_sdf(input.position.xy, quad.content_mask.bounds,
-    quad.content_mask.corner_radii);
-  float clip_alpha = saturate(antialias_threshold - clip_sdf);
-  background_color.a *= clip_alpha;
-  border_color *= clip_alpha;
 
   bool unrounded = quad.corner_radii.top_left == 0.0 &&
     quad.corner_radii.bottom_left == 0.0 &&
@@ -134,6 +121,10 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
   float2 point = input.position.xy - float2(quad.bounds.origin.x, quad.bounds.origin.y);
   float2 center_to_point = point - half_size;
 
+  // Signed distance field threshold for inclusion of pixels. 0.5 is the
+  // minimum distance between the center of the pixel and the edge.
+  const float antialias_threshold = 0.5;
+
   // Radius of the nearest corner
   float corner_radius = pick_corner_radius(center_to_point, quad.corner_radii);
 
@@ -173,6 +164,7 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
     straight_border_inner_corner_to_point.x > 0.0 ||
     straight_border_inner_corner_to_point.y > 0.0;
 
+
   // Whether the point is far enough inside the quad, such that the pixels are
   // not affected by the straight border.
   bool is_within_inner_straight_border =
@@ -216,6 +208,8 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
 
   float4 color = background_color;
   if (border_sdf < antialias_threshold) {
+    float4 border_color = input.border_color;
+
     // Dashed border logic when border_style == 1
     if (quad.border_style == 1) {
       // Position along the perimeter in "dash space", where each dash
@@ -250,10 +244,6 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
         // perimeter. This way each line starts and ends with a dash.
         bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y;
         float border_width = is_horizontal ? border.x : border.y;
-        // When border width of some side is 0, we need to use the other side width for dash velocity.
-        if (border_width == 0.0) {
-            border_width = is_horizontal ? border.y : border.x;
-        }
         dash_velocity = dv_numerator / border_width;
         t = is_horizontal ? point.x : point.y;
         t *= dash_velocity;

crates/gpui/src/platform/windows/shaders.hlsl 🔗

@@ -453,16 +453,11 @@ float quarter_ellipse_sdf(float2 pt, float2 radii) {
 **
 */
 
-struct ContentMask {
-    Bounds bounds;
-    Corners corner_radii;
-};
-
 struct Quad {
     uint order;
     uint border_style;
     Bounds bounds;
-    ContentMask content_mask;
+    Bounds content_mask;
     Background background;
     Hsla border_color;
     Corners corner_radii;
@@ -501,7 +496,7 @@ QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_Insta
         quad.background.solid,
         quad.background.colors
     );
-    float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask.bounds);
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask);
     float4 border_color = hsla_to_rgba(quad.border_color);
 
     QuadVertexOutput output;
@@ -517,21 +512,8 @@ QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_Insta
 
 float4 quad_fragment(QuadFragmentInput input): SV_Target {
     Quad quad = quads[input.quad_id];
-
-    // Signed distance field threshold for inclusion of pixels. 0.5 is the
-    // minimum distance between the center of the pixel and the edge.
-    const float antialias_threshold = 0.5;
-
     float4 background_color = gradient_color(quad.background, input.position.xy, quad.bounds,
-        input.background_solid, input.background_color0, input.background_color1);
-    float4 border_color = input.border_color;
-
-    // Apply content_mask corner radii clipping
-    float clip_sdf = quad_sdf(input.position.xy, quad.content_mask.bounds,
-        quad.content_mask.corner_radii);
-    float clip_alpha = saturate(antialias_threshold - clip_sdf);
-    background_color.a *= clip_alpha;
-    border_color *= clip_alpha;
+    input.background_solid, input.background_color0, input.background_color1);
 
     bool unrounded = quad.corner_radii.top_left == 0.0 &&
         quad.corner_radii.top_right == 0.0 &&
@@ -552,6 +534,10 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target {
     float2 the_point = input.position.xy - quad.bounds.origin;
     float2 center_to_point = the_point - half_size;
 
+    // Signed distance field threshold for inclusion of pixels. 0.5 is the
+    // minimum distance between the center of the pixel and the edge.
+    const float antialias_threshold = 0.5;
+
     // Radius of the nearest corner
     float corner_radius = pick_corner_radius(center_to_point, quad.corner_radii);
 
@@ -634,6 +620,7 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target {
 
     float4 color = background_color;
     if (border_sdf < antialias_threshold) {
+        float4 border_color = input.border_color;
         // Dashed border logic when border_style == 1
         if (quad.border_style == 1) {
             // Position along the perimeter in "dash space", where each dash
@@ -668,10 +655,6 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target {
                 // perimeter. This way each line starts and ends with a dash.
                 bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y;
                 float border_width = is_horizontal ? border.x : border.y;
-                // When border width of some side is 0, we need to use the other side width for dash velocity.
-                if (border_width == 0.0) {
-                    border_width = is_horizontal ? border.y : border.x;
-                }
                 dash_velocity = dv_numerator / border_width;
                 t = is_horizontal ? the_point.x : the_point.y;
                 t *= dash_velocity;
@@ -822,7 +805,7 @@ struct Shadow {
     float blur_radius;
     Bounds bounds;
     Corners corner_radii;
-    ContentMask content_mask;
+    Bounds content_mask;
     Hsla color;
 };
 
@@ -851,7 +834,7 @@ ShadowVertexOutput shadow_vertex(uint vertex_id: SV_VertexID, uint shadow_id: SV
     bounds.size += 2.0 * margin;
 
     float4 device_position = to_device_position(unit_vertex, bounds);
-    float4 clip_distance = distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask.bounds);
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask);
     float4 color = hsla_to_rgba(shadow.color);
 
     ShadowVertexOutput output;
@@ -1004,7 +987,7 @@ struct Underline {
     uint order;
     uint pad;
     Bounds bounds;
-    ContentMask content_mask;
+    Bounds content_mask;
     Hsla color;
     float thickness;
     uint wavy;
@@ -1030,7 +1013,7 @@ UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underli
     Underline underline = underlines[underline_id];
     float4 device_position = to_device_position(unit_vertex, underline.bounds);
     float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds,
-                                                    underline.content_mask.bounds);
+                                                    underline.content_mask);
     float4 color = hsla_to_rgba(underline.color);
 
     UnderlineVertexOutput output;
@@ -1078,7 +1061,7 @@ struct MonochromeSprite {
     uint order;
     uint pad;
     Bounds bounds;
-    ContentMask content_mask;
+    Bounds content_mask;
     Hsla color;
     AtlasTile tile;
     TransformationMatrix transformation;
@@ -1105,7 +1088,7 @@ MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexI
     MonochromeSprite sprite = mono_sprites[sprite_id];
     float4 device_position =
         to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation);
-    float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask.bounds);
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask);
     float2 tile_position = to_tile_position(unit_vertex, sprite.tile);
     float4 color = hsla_to_rgba(sprite.color);
 
@@ -1135,7 +1118,7 @@ struct PolychromeSprite {
     uint grayscale;
     float opacity;
     Bounds bounds;
-    ContentMask content_mask;
+    Bounds content_mask;
     Corners corner_radii;
     AtlasTile tile;
 };
@@ -1160,7 +1143,7 @@ PolychromeSpriteVertexOutput polychrome_sprite_vertex(uint vertex_id: SV_VertexI
     PolychromeSprite sprite = poly_sprites[sprite_id];
     float4 device_position = to_device_position(unit_vertex, sprite.bounds);
     float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds,
-                                                    sprite.content_mask.bounds);
+                                                    sprite.content_mask);
     float2 tile_position = to_tile_position(unit_vertex, sprite.tile);
 
     PolychromeSpriteVertexOutput output;

crates/gpui/src/style.rs 🔗

@@ -601,19 +601,7 @@ impl Style {
                     (false, false) => Bounds::from_corners(min, max),
                 };
 
-                let corner_radii = self.corner_radii.to_pixels(rem_size);
-                let border_widths = self.border_widths.to_pixels(rem_size);
-                Some(ContentMask {
-                    bounds: Bounds {
-                        origin: bounds.origin - point(border_widths.left, border_widths.top),
-                        size: bounds.size
-                            + size(
-                                border_widths.left + border_widths.right,
-                                border_widths.top + border_widths.bottom,
-                            ),
-                    },
-                    corner_radii,
-                })
+                Some(ContentMask { bounds })
             }
         }
     }
@@ -673,16 +661,64 @@ impl Style {
 
         if self.is_border_visible() {
             let border_widths = self.border_widths.to_pixels(rem_size);
+            let max_border_width = border_widths.max();
+            let max_corner_radius = corner_radii.max();
+
+            let top_bounds = Bounds::from_corners(
+                bounds.origin,
+                bounds.top_right() + point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
+            );
+            let bottom_bounds = Bounds::from_corners(
+                bounds.bottom_left() - point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
+                bounds.bottom_right(),
+            );
+            let left_bounds = Bounds::from_corners(
+                top_bounds.bottom_left(),
+                bottom_bounds.origin + point(max_border_width, Pixels::ZERO),
+            );
+            let right_bounds = Bounds::from_corners(
+                top_bounds.bottom_right() - point(max_border_width, Pixels::ZERO),
+                bottom_bounds.top_right(),
+            );
+
             let mut background = self.border_color.unwrap_or_default();
             background.a = 0.;
-            window.paint_quad(quad(
+            let quad = quad(
                 bounds,
                 corner_radii,
                 background,
                 border_widths,
                 self.border_color.unwrap_or_default(),
                 self.border_style,
-            ));
+            );
+
+            window.with_content_mask(Some(ContentMask { bounds: top_bounds }), |window| {
+                window.paint_quad(quad.clone());
+            });
+            window.with_content_mask(
+                Some(ContentMask {
+                    bounds: right_bounds,
+                }),
+                |window| {
+                    window.paint_quad(quad.clone());
+                },
+            );
+            window.with_content_mask(
+                Some(ContentMask {
+                    bounds: bottom_bounds,
+                }),
+                |window| {
+                    window.paint_quad(quad.clone());
+                },
+            );
+            window.with_content_mask(
+                Some(ContentMask {
+                    bounds: left_bounds,
+                }),
+                |window| {
+                    window.paint_quad(quad);
+                },
+            );
         }
 
         #[cfg(debug_assertions)]

crates/gpui/src/window.rs 🔗

@@ -1283,8 +1283,6 @@ pub(crate) struct DispatchEventResult {
 pub struct ContentMask<P: Clone + Debug + Default + PartialEq> {
     /// The bounds
     pub bounds: Bounds<P>,
-    /// The corner radii of the content mask.
-    pub corner_radii: Corners<P>,
 }
 
 impl ContentMask<Pixels> {
@@ -1292,31 +1290,13 @@ impl ContentMask<Pixels> {
     pub fn scale(&self, factor: f32) -> ContentMask<ScaledPixels> {
         ContentMask {
             bounds: self.bounds.scale(factor),
-            corner_radii: self.corner_radii.scale(factor),
         }
     }
 
     /// Intersect the content mask with the given content mask.
     pub fn intersect(&self, other: &Self) -> Self {
         let bounds = self.bounds.intersect(&other.bounds);
-        ContentMask {
-            bounds,
-            corner_radii: Corners {
-                top_left: self.corner_radii.top_left.max(other.corner_radii.top_left),
-                top_right: self
-                    .corner_radii
-                    .top_right
-                    .max(other.corner_radii.top_right),
-                bottom_right: self
-                    .corner_radii
-                    .bottom_right
-                    .max(other.corner_radii.bottom_right),
-                bottom_left: self
-                    .corner_radii
-                    .bottom_left
-                    .max(other.corner_radii.bottom_left),
-            },
-        }
+        ContentMask { bounds }
     }
 }
 
@@ -2577,7 +2557,6 @@ impl Window {
                     origin: Point::default(),
                     size: self.viewport_size,
                 },
-                ..Default::default()
             })
     }
 

crates/terminal_view/src/terminal_element.rs 🔗

@@ -1184,7 +1184,7 @@ impl Element for TerminalElement {
         cx: &mut App,
     ) {
         let paint_start = Instant::now();
-        window.with_content_mask(Some(ContentMask { bounds, ..Default::default() }), |window| {
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
             let scroll_top = self.terminal_view.read(cx).scroll_top;
 
             window.paint_quad(fill(bounds, layout.background_color));

crates/ui/src/components/scrollbar.rs 🔗

@@ -303,13 +303,9 @@ impl Element for Scrollbar {
         window: &mut Window,
         _: &mut App,
     ) -> Self::PrepaintState {
-        window.with_content_mask(
-            Some(ContentMask {
-                bounds,
-                ..Default::default()
-            }),
-            |window| window.insert_hitbox(bounds, HitboxBehavior::Normal),
-        )
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
+            window.insert_hitbox(bounds, HitboxBehavior::Normal)
+        })
     }
 
     fn paint(
@@ -323,11 +319,7 @@ impl Element for Scrollbar {
         cx: &mut App,
     ) {
         const EXTRA_PADDING: Pixels = px(5.0);
-        let content_mask = ContentMask {
-            bounds,
-            ..Default::default()
-        };
-        window.with_content_mask(Some(content_mask), |window| {
+        window.with_content_mask(Some(ContentMask { bounds }), |window| {
             let axis = self.kind;
             let colors = cx.theme().colors();
             let thumb_state = self.state.thumb_state.get();