From bb2d833373400d4debf416e90d5b2870f75dde96 Mon Sep 17 00:00:00 2001 From: localcc Date: Wed, 3 Sep 2025 21:52:47 +0200 Subject: [PATCH] Revert "gpui: Fix overflow_hidden to support clip with border radius" (#37480) This reverts commit 40199266b6634cc3165f3842abae1d562ef4dcca. The issue with the commit is: ContentMask::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 --- 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(-) delete mode 100644 crates/gpui/examples/content_mask.rs diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index c21d31aa5c14e3b596a6089630b29a3e55a3084d..9822ec23d5af41ee6fbfdd7c471f6fcc9437c78b 100644 --- a/crates/editor/src/element.rs +++ b/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); diff --git a/crates/gpui/examples/content_mask.rs b/crates/gpui/examples/content_mask.rs deleted file mode 100644 index 8d40cc5bba7c35395cbeef009f6b648ed68826ed..0000000000000000000000000000000000000000 --- a/crates/gpui/examples/content_mask.rs +++ /dev/null @@ -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) -> 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); - }); -} diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 9ae497cef92c9f1aa68eea4af6aea8bf410a817f..ed4ca64e83513531b9176f05c4c00b0af71aea74 100644 --- a/crates/gpui/src/elements/list.rs +++ b/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, padding: Edges, - corner_radii: Corners, 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; diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index db3b8c88395388bf57eb16a0bb73ce2d6f005779..cdf90d4eb8934de99a21c65b6c9efa2a2fdde258 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/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 diff --git a/crates/gpui/src/platform/blade/shaders.wgsl b/crates/gpui/src/platform/blade/shaders.wgsl index dbab4237e3eb87c570096ae7b6d7328fa1da6ff2..95980b54fe4f25b3936d6b095219c5674211dd0a 100644 --- a/crates/gpui/src/platform/blade/shaders.wgsl +++ b/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 { 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 { 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 { 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 { 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 b_shadows: array; @@ -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 { 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 { 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) - ); -} diff --git a/crates/gpui/src/platform/mac/shaders.metal b/crates/gpui/src/platform/mac/shaders.metal index 6aa1d18ee895ec84a6f19edbcc6618cf713aa5a5..83c978b853443d5c612f514625f94b6d6725be8a 100644 --- a/crates/gpui/src/platform/mac/shaders.metal +++ b/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; diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 296a6c825f8b057b5330d108c21c27ffb4fa1b75..2cef54ae6166e313795eb42210b5f07c1bc378fc 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/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; diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index 09f598f9b0deb4ddd9910e32e4dbe020b54c849a..5b69ce7fa6eb06affc2f77c0d1bdfbe4165c206a 100644 --- a/crates/gpui/src/style.rs +++ b/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)] diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index cebf911b734798a3965857db0f3fd6fe2be2b011..0ec73c4b0040e6c65cd8819ecf5d20a9ec1900d0 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1283,8 +1283,6 @@ pub(crate) struct DispatchEventResult { pub struct ContentMask { /// The bounds pub bounds: Bounds

, - /// The corner radii of the content mask. - pub corner_radii: Corners

, } impl ContentMask { @@ -1292,31 +1290,13 @@ impl ContentMask { pub fn scale(&self, factor: f32) -> ContentMask { 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() }) } diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index e4d7f226912cdb6123081dd5cfb38f205d9a9333..5bbf5ad36b3de89514d92ce9e305988817cec32f 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/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)); diff --git a/crates/ui/src/components/scrollbar.rs b/crates/ui/src/components/scrollbar.rs index 475575a483ea4659b572f8f82e13faf8b539fb37..605028202fffa37d67bbdb4a9f33a97459390dfa 100644 --- a/crates/ui/src/components/scrollbar.rs +++ b/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();