mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
gpui: Add support for inset shadow (#57685)
This PR adds support for inset shadows in the box shadow through the `inset: true` field. It includes support for both the macOS as well WGSL shaders. For now, there is no immediate application of it in Zed, so nothing should change in the app. <img width="600" alt="Screenshot 2026-05-25 at 8 46@2x" src="https://github.com/user-attachments/assets/db564a6b-8af5-491a-a573-17c060a3647c" /> Run the example above with `cargo run --example shadow -p gpui`. Release Notes: - N/A --------- Co-authored-by: Agus Zubiaga <agus@zed.dev>
This commit is contained in:
parent
b5b52ada0c
commit
eb944cfd7a
18 changed files with 341 additions and 53 deletions
|
|
@ -2608,6 +2608,7 @@ impl ThreadView {
|
|||
offset: point(px(1.), px(-1.)),
|
||||
blur_radius: px(2.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}])
|
||||
.when_some(awaiting_permission, |this, element| this.child(element))
|
||||
.when(
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ impl Render for ApiKeysWithProviders {
|
|||
offset: point(px(1.), px(-1.)),
|
||||
blur_radius: px(3.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}])
|
||||
.child(
|
||||
h_flex()
|
||||
|
|
|
|||
|
|
@ -1980,6 +1980,7 @@ impl Editor {
|
|||
offset: point(px(1.), px(1.)),
|
||||
blur_radius: px(2.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}])
|
||||
.bg(Editor::edit_prediction_line_popover_bg_color(cx))
|
||||
.border(BORDER_WIDTH)
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ impl Render for HelloWorld {
|
|||
blur_radius: px(1.0),
|
||||
spread_radius: px(5.0),
|
||||
offset: point(px(10.0), px(10.0)),
|
||||
inset: false,
|
||||
}])
|
||||
.child(img("image/app-icon.png").size_8())
|
||||
.child("Opacity Panel (Click to test)")
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -119,6 +120,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -129,6 +131,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -139,6 +142,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -149,6 +153,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -185,6 +190,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -194,6 +200,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(2.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -203,6 +210,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(4.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -212,6 +220,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -221,6 +230,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(16.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -237,6 +247,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -246,6 +257,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -255,6 +267,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(4.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -264,6 +277,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(8.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -273,6 +287,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(16.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -289,6 +304,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -298,6 +314,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(8.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -307,6 +324,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(16.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -323,6 +341,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -332,6 +351,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(8.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -341,6 +361,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(16.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -357,6 +378,7 @@ impl Render for Shadow {
|
|||
offset: point(px(-8.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -366,6 +388,7 @@ impl Render for Shadow {
|
|||
offset: point(px(8.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -375,6 +398,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(-8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -384,6 +408,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -400,6 +425,7 @@ impl Render for Shadow {
|
|||
offset: point(px(-8.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -409,6 +435,7 @@ impl Render for Shadow {
|
|||
offset: point(px(8.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -418,6 +445,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(-8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -427,6 +455,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -443,6 +472,7 @@ impl Render for Shadow {
|
|||
offset: point(px(-8.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -452,6 +482,7 @@ impl Render for Shadow {
|
|||
offset: point(px(8.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -461,6 +492,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(-8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
|
|
@ -470,6 +502,7 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
|
|
@ -487,24 +520,28 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(-12.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(60.0 / 360., 1.0, 0.5, 0.3), // Yellow
|
||||
offset: point(px(12.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(120.0 / 360., 1.0, 0.5, 0.3), // Green
|
||||
offset: point(px(0.), px(12.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(240.0 / 360., 1.0, 0.5, 0.3), // Blue
|
||||
offset: point(px(-12.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
]),
|
||||
),
|
||||
|
|
@ -516,24 +553,28 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(-12.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(60.0 / 360., 1.0, 0.5, 0.3), // Yellow
|
||||
offset: point(px(12.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(120.0 / 360., 1.0, 0.5, 0.3), // Green
|
||||
offset: point(px(0.), px(12.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(240.0 / 360., 1.0, 0.5, 0.3), // Blue
|
||||
offset: point(px(-12.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
]),
|
||||
),
|
||||
|
|
@ -545,24 +586,113 @@ impl Render for Shadow {
|
|||
offset: point(px(0.), px(-12.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(60.0 / 360., 1.0, 0.5, 0.3), // Yellow
|
||||
offset: point(px(12.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(120.0 / 360., 1.0, 0.5, 0.3), // Green
|
||||
offset: point(px(0.), px(12.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(240.0 / 360., 1.0, 0.5, 0.3), // Blue
|
||||
offset: point(px(-12.), px(0.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(2.),
|
||||
inset: false,
|
||||
},
|
||||
]),
|
||||
),
|
||||
]),
|
||||
// Inset shadows (CSS `box-shadow: inset ...`).
|
||||
div()
|
||||
.border_b_1()
|
||||
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
|
||||
.flex()
|
||||
.w_full()
|
||||
.children(vec![
|
||||
example(
|
||||
"Inset basic",
|
||||
Shadow::base().shadow(vec![BoxShadow {
|
||||
color: hsla(0.0, 0.0, 0.0, 0.5),
|
||||
offset: point(px(0.), px(0.)),
|
||||
blur_radius: px(12.),
|
||||
spread_radius: px(0.),
|
||||
inset: true,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
"Inset offset",
|
||||
Shadow::base().shadow(vec![BoxShadow {
|
||||
color: hsla(0.0, 0.0, 0.0, 0.5),
|
||||
offset: point(px(6.), px(6.)),
|
||||
blur_radius: px(8.),
|
||||
spread_radius: px(0.),
|
||||
inset: true,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
"Inset spread",
|
||||
Shadow::base().shadow(vec![BoxShadow {
|
||||
color: hsla(0.0, 0.0, 0.0, 0.5),
|
||||
offset: point(px(0.), px(0.)),
|
||||
blur_radius: px(4.),
|
||||
spread_radius: px(8.),
|
||||
inset: true,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
"Inset rounded",
|
||||
Shadow::rounded_large().shadow(vec![BoxShadow {
|
||||
color: hsla(0.0, 0.0, 0.0, 0.5),
|
||||
offset: point(px(0.), px(4.)),
|
||||
blur_radius: px(10.),
|
||||
spread_radius: px(2.),
|
||||
inset: true,
|
||||
}]),
|
||||
),
|
||||
example(
|
||||
"Inset sharp",
|
||||
Shadow::square().shadow(vec![BoxShadow {
|
||||
color: hsla(0.0, 0.0, 0.0, 0.6),
|
||||
offset: point(px(0.), px(0.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(6.),
|
||||
inset: true,
|
||||
}]),
|
||||
),
|
||||
]),
|
||||
// Combined: drop + inset shadows on the same element.
|
||||
div()
|
||||
.border_b_1()
|
||||
.border_color(hsla(0.0, 0.0, 0.0, 1.0))
|
||||
.flex()
|
||||
.w_full()
|
||||
.children(vec![
|
||||
example(
|
||||
"Drop + Inset",
|
||||
Shadow::rounded_medium().shadow(vec![
|
||||
BoxShadow {
|
||||
color: hsla(0.0, 0.0, 0.0, 0.25),
|
||||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(12.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0.0, 0.0, 0.0, 0.4),
|
||||
offset: point(px(0.), px(2.)),
|
||||
blur_radius: px(4.),
|
||||
spread_radius: px(0.),
|
||||
inset: true,
|
||||
},
|
||||
]),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ impl Render for WindowShadow {
|
|||
},
|
||||
blur_radius: shadow_size / 2.,
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
offset: point(px(0.0), px(0.0)),
|
||||
}])
|
||||
}),
|
||||
|
|
@ -156,6 +157,7 @@ impl Render for WindowShadow {
|
|||
},
|
||||
blur_radius: px(20.0),
|
||||
spread_radius: px(0.0),
|
||||
inset: false,
|
||||
offset: point(px(0.0), px(0.0)),
|
||||
}])
|
||||
.map(|div| match decorations {
|
||||
|
|
|
|||
|
|
@ -530,6 +530,11 @@ pub struct Shadow {
|
|||
pub corner_radii: Corners<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub color: Hsla,
|
||||
pub element_bounds: Bounds<ScaledPixels>,
|
||||
pub element_corner_radii: Corners<ScaledPixels>,
|
||||
/// 0 = drop shadow (rendered outside the element), 1 = inset shadow (rendered inside).
|
||||
pub inset: u32,
|
||||
pub pad: u32, // align to 8 bytes
|
||||
}
|
||||
|
||||
impl From<Shadow> for Primitive {
|
||||
|
|
|
|||
|
|
@ -351,6 +351,8 @@ pub struct BoxShadow {
|
|||
pub blur_radius: Pixels,
|
||||
/// How much should the shadow spread?
|
||||
pub spread_radius: Pixels,
|
||||
/// Whether this is an inset shadow (drawn inside the element's bounds).
|
||||
pub inset: bool,
|
||||
}
|
||||
|
||||
/// How to handle whitespace in text
|
||||
|
|
@ -665,7 +667,7 @@ impl Style {
|
|||
.to_pixels(rem_size)
|
||||
.clamp_radii_for_quad_size(bounds.size);
|
||||
|
||||
window.paint_shadows(bounds, corner_radii, &self.box_shadow);
|
||||
window.paint_drop_shadows(bounds, corner_radii, &self.box_shadow);
|
||||
|
||||
let background_color = self.background.as_ref().and_then(Fill::color);
|
||||
if background_color.is_some_and(|color| !color.is_transparent()) {
|
||||
|
|
@ -694,6 +696,8 @@ impl Style {
|
|||
));
|
||||
}
|
||||
|
||||
window.paint_inset_shadows(bounds, corner_radii, &self.box_shadow);
|
||||
|
||||
continuation(window, cx);
|
||||
|
||||
if self.is_border_visible() {
|
||||
|
|
|
|||
|
|
@ -3449,10 +3449,12 @@ impl Window {
|
|||
result
|
||||
}
|
||||
|
||||
/// Paint one or more drop shadows into the scene for the next frame at the current z-index.
|
||||
/// Paint the drop (non-inset) shadows from `shadows` into the scene at the current
|
||||
/// z-index. Inset shadows are skipped; paint those with [`Self::paint_inset_shadows`]
|
||||
/// after the element's background so they layer on top of the fill.
|
||||
///
|
||||
/// This method should only be called as part of the paint phase of element drawing.
|
||||
pub fn paint_shadows(
|
||||
pub(crate) fn paint_drop_shadows(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
corner_radii: Corners<Pixels>,
|
||||
|
|
@ -3463,7 +3465,12 @@ impl Window {
|
|||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.snapped_content_mask();
|
||||
let opacity = self.element_opacity();
|
||||
let element_bounds = self.cover_bounds(bounds);
|
||||
let element_corner_radii = corner_radii.scale(scale_factor);
|
||||
for shadow in shadows {
|
||||
if shadow.inset {
|
||||
continue;
|
||||
}
|
||||
let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius);
|
||||
self.next_frame.scene.insert_primitive(Shadow {
|
||||
order: 0,
|
||||
|
|
@ -3472,6 +3479,55 @@ impl Window {
|
|||
content_mask,
|
||||
corner_radii: corner_radii.scale(scale_factor),
|
||||
color: shadow.color.opacity(opacity),
|
||||
element_bounds,
|
||||
element_corner_radii,
|
||||
inset: 0,
|
||||
pad: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Paint the inset shadows from `shadows` into the scene at the current z-index. Should
|
||||
/// be called after the element's background so the shadow layers on top of the fill.
|
||||
/// Drop shadows are skipped; paint those with [`Self::paint_drop_shadows`] before the background.
|
||||
pub(crate) fn paint_inset_shadows(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
corner_radii: Corners<Pixels>,
|
||||
shadows: &[BoxShadow],
|
||||
) {
|
||||
self.invalidator.debug_assert_paint();
|
||||
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.snapped_content_mask();
|
||||
let opacity = self.element_opacity();
|
||||
let element_bounds = self.cover_bounds(bounds);
|
||||
let element_corner_radii = corner_radii.scale(scale_factor);
|
||||
for shadow in shadows {
|
||||
if !shadow.inset {
|
||||
continue;
|
||||
}
|
||||
let hole = (bounds + shadow.offset).dilate(-shadow.spread_radius);
|
||||
// Clamp at zero so a large spread can't produce negative radii, which would
|
||||
// break the SDF in the shader.
|
||||
let zero = Pixels::ZERO;
|
||||
let hole_corner_radii = Corners {
|
||||
top_left: (corner_radii.top_left - shadow.spread_radius).max(zero),
|
||||
top_right: (corner_radii.top_right - shadow.spread_radius).max(zero),
|
||||
bottom_right: (corner_radii.bottom_right - shadow.spread_radius).max(zero),
|
||||
bottom_left: (corner_radii.bottom_left - shadow.spread_radius).max(zero),
|
||||
};
|
||||
self.next_frame.scene.insert_primitive(Shadow {
|
||||
order: 0,
|
||||
blur_radius: shadow.blur_radius.scale(scale_factor),
|
||||
bounds: self.cover_bounds(hole),
|
||||
content_mask,
|
||||
corner_radii: hole_corner_radii.scale(scale_factor),
|
||||
color: shadow.color.opacity(opacity),
|
||||
element_bounds,
|
||||
element_corner_radii,
|
||||
inset: 1,
|
||||
pad: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -468,14 +468,18 @@ vertex ShadowVertexOutput shadow_vertex(
|
|||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||
Shadow shadow = shadows[shadow_id];
|
||||
|
||||
Bounds_ScaledPixels bounds;
|
||||
if (shadow.inset != 0u) {
|
||||
bounds = shadow.element_bounds;
|
||||
} else {
|
||||
// Leave room for the gaussian tail outside the shadow rect.
|
||||
float margin = 3. * shadow.blur_radius;
|
||||
// Set the bounds of the shadow and adjust its size based on the shadow's
|
||||
// spread radius to achieve the spreading effect
|
||||
Bounds_ScaledPixels bounds = shadow.bounds;
|
||||
bounds = shadow.bounds;
|
||||
bounds.origin.x -= margin;
|
||||
bounds.origin.y -= margin;
|
||||
bounds.size.width += 2. * margin;
|
||||
bounds.size.height += 2. * margin;
|
||||
}
|
||||
|
||||
float4 device_position =
|
||||
to_device_position(unit_vertex, bounds, viewport_size);
|
||||
|
|
@ -538,6 +542,15 @@ fragment float4 shadow_fragment(ShadowFragmentInput input [[stage_in]],
|
|||
}
|
||||
}
|
||||
|
||||
if (shadow.inset != 0u) {
|
||||
// The inset shadow is the complement of the (blurred) hole rect, clipped to the element.
|
||||
// `saturate(0.5 - d)` gives a 1-pixel antialiased edge: d <= -0.5 -> 1, d >= 0.5 -> 0.
|
||||
alpha = 1. - alpha;
|
||||
float element_distance = quad_sdf(input.position.xy, shadow.element_bounds,
|
||||
shadow.element_corner_radii);
|
||||
alpha *= saturate(0.5 - element_distance);
|
||||
}
|
||||
|
||||
return input.color * float4(1., 1., 1., alpha);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
|||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]);
|
||||
self
|
||||
}
|
||||
|
|
@ -425,6 +426,7 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
|||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(2.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}]);
|
||||
self
|
||||
}
|
||||
|
|
@ -441,12 +443,14 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
|||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(3.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., 0.1),
|
||||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(2.),
|
||||
spread_radius: px(-1.),
|
||||
inset: false,
|
||||
}
|
||||
]);
|
||||
self
|
||||
|
|
@ -464,12 +468,14 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
|||
offset: point(px(0.), px(4.)),
|
||||
blur_radius: px(6.),
|
||||
spread_radius: px(-1.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., 0.1),
|
||||
offset: point(px(0.), px(2.)),
|
||||
blur_radius: px(4.),
|
||||
spread_radius: px(-2.),
|
||||
inset: false,
|
||||
}
|
||||
]);
|
||||
self
|
||||
|
|
@ -487,12 +493,14 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
|||
offset: point(px(0.), px(10.)),
|
||||
blur_radius: px(15.),
|
||||
spread_radius: px(-3.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., 0.1),
|
||||
offset: point(px(0.), px(4.)),
|
||||
blur_radius: px(6.),
|
||||
spread_radius: px(-4.),
|
||||
inset: false,
|
||||
}
|
||||
]);
|
||||
self
|
||||
|
|
@ -510,12 +518,14 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
|||
offset: point(px(0.), px(20.)),
|
||||
blur_radius: px(25.),
|
||||
spread_radius: px(-5.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., 0.1),
|
||||
offset: point(px(0.), px(8.)),
|
||||
blur_radius: px(10.),
|
||||
spread_radius: px(-6.),
|
||||
inset: false,
|
||||
}
|
||||
]);
|
||||
self
|
||||
|
|
@ -532,6 +542,7 @@ pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
|
|||
offset: point(px(0.), px(25.)),
|
||||
blur_radius: px(50.),
|
||||
spread_radius: px(-12.),
|
||||
inset: false,
|
||||
}]);
|
||||
self
|
||||
}
|
||||
|
|
|
|||
|
|
@ -951,10 +951,18 @@ fn fmod(a: f32, b: f32) -> f32 {
|
|||
struct Shadow {
|
||||
order: u32,
|
||||
blur_radius: f32,
|
||||
// The shadow rect for drop shadows; the "hole" rect for inset shadows.
|
||||
bounds: Bounds,
|
||||
corner_radii: Corners,
|
||||
content_mask: Bounds,
|
||||
color: Hsla,
|
||||
// Only consulted when `inset == 1u`: the element's own bounds, used as a rounded-rect
|
||||
// clip so the shadow never escapes the element.
|
||||
element_bounds: Bounds,
|
||||
element_corner_radii: Corners,
|
||||
// 0 = drop shadow, 1 = inset shadow.
|
||||
inset: u32,
|
||||
pad: u32, // align to 8 bytes
|
||||
}
|
||||
@group(1) @binding(0) var<storage, read> b_shadows: array<Shadow>;
|
||||
|
||||
|
|
@ -971,17 +979,22 @@ fn vs_shadow(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) ins
|
|||
let unit_vertex = vec2<f32>(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u));
|
||||
var shadow = b_shadows[instance_id];
|
||||
|
||||
var geometry: Bounds;
|
||||
if (shadow.inset != 0u) {
|
||||
geometry = shadow.element_bounds;
|
||||
} else {
|
||||
// Leave room for the gaussian tail outside the shadow rect.
|
||||
let margin = 3.0 * shadow.blur_radius;
|
||||
// Set the bounds of the shadow and adjust its size based on the shadow's
|
||||
// spread radius to achieve the spreading effect
|
||||
shadow.bounds.origin -= vec2<f32>(margin);
|
||||
shadow.bounds.size += 2.0 * vec2<f32>(margin);
|
||||
geometry = shadow.bounds;
|
||||
geometry.origin -= vec2<f32>(margin);
|
||||
geometry.size += 2.0 * vec2<f32>(margin);
|
||||
}
|
||||
|
||||
var out = ShadowVarying();
|
||||
out.position = to_device_position(unit_vertex, shadow.bounds);
|
||||
out.position = to_device_position(unit_vertex, geometry);
|
||||
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);
|
||||
out.clip_distances = distance_from_clip_rect(unit_vertex, geometry, shadow.content_mask);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -999,6 +1012,11 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4<f32> {
|
|||
|
||||
let corner_radius = pick_corner_radius(center_to_point, shadow.corner_radii);
|
||||
|
||||
var alpha: f32;
|
||||
if (shadow.blur_radius == 0.0) {
|
||||
let distance = quad_sdf(input.position.xy, shadow.bounds, shadow.corner_radii);
|
||||
alpha = saturate(0.5 - distance);
|
||||
} else {
|
||||
// The signal is only non-zero in a limited range, so don't waste samples
|
||||
let low = center_to_point.y - half_size.y;
|
||||
let high = center_to_point.y + half_size.y;
|
||||
|
|
@ -1008,13 +1026,23 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4<f32> {
|
|||
// Accumulate samples (we can get away with surprisingly few samples)
|
||||
let step = (end - start) / 4.0;
|
||||
var y = start + step * 0.5;
|
||||
var alpha = 0.0;
|
||||
alpha = 0.0;
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
let blur = blur_along_x(center_to_point.x, center_to_point.y - y,
|
||||
shadow.blur_radius, corner_radius, half_size);
|
||||
alpha += blur * gaussian(y, shadow.blur_radius) * step;
|
||||
y += step;
|
||||
}
|
||||
}
|
||||
|
||||
if (shadow.inset != 0u) {
|
||||
// The inset shadow is the complement of the (blurred) hole rect, clipped to the element.
|
||||
// `saturate(0.5 - d)` gives a 1-pixel antialiased edge: d <= -0.5 -> 1, d >= 0.5 -> 0.
|
||||
alpha = 1.0 - alpha;
|
||||
let element_distance = quad_sdf(input.position.xy, shadow.element_bounds,
|
||||
shadow.element_corner_radii);
|
||||
alpha *= saturate(0.5 - element_distance);
|
||||
}
|
||||
|
||||
return blend_color(input.color, alpha);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -854,6 +854,10 @@ struct Shadow {
|
|||
Corners corner_radii;
|
||||
Bounds content_mask;
|
||||
Hsla color;
|
||||
Bounds element_bounds;
|
||||
Corners element_corner_radii;
|
||||
uint inset;
|
||||
uint pad; // align to 8 bytes
|
||||
};
|
||||
|
||||
struct ShadowVertexOutput {
|
||||
|
|
@ -875,10 +879,16 @@ ShadowVertexOutput shadow_vertex(uint vertex_id: SV_VertexID, uint shadow_id: SV
|
|||
float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
|
||||
Shadow shadow = shadows[shadow_id];
|
||||
|
||||
Bounds bounds;
|
||||
if (shadow.inset != 0u) {
|
||||
bounds = shadow.element_bounds;
|
||||
} else {
|
||||
// Leave room for the gaussian tail outside the shadow rect.
|
||||
float margin = 3.0 * shadow.blur_radius;
|
||||
Bounds bounds = shadow.bounds;
|
||||
bounds = shadow.bounds;
|
||||
bounds.origin -= margin;
|
||||
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);
|
||||
|
|
@ -901,6 +911,11 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET {
|
|||
float2 point0 = input.position.xy - center;
|
||||
float corner_radius = pick_corner_radius(point0, shadow.corner_radii);
|
||||
|
||||
float alpha;
|
||||
if (shadow.blur_radius == 0.) {
|
||||
float distance = quad_sdf(input.position.xy, shadow.bounds, shadow.corner_radii);
|
||||
alpha = saturate(0.5 - distance);
|
||||
} else {
|
||||
// The signal is only non-zero in a limited range, so don't waste samples
|
||||
float low = point0.y - half_size.y;
|
||||
float high = point0.y + half_size.y;
|
||||
|
|
@ -910,13 +925,23 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET {
|
|||
// Accumulate samples (we can get away with surprisingly few samples)
|
||||
float step = (end - start) / 4.;
|
||||
float y = start + step * 0.5;
|
||||
float alpha = 0.;
|
||||
alpha = 0.;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
alpha += blur_along_x(point0.x, point0.y - y, shadow.blur_radius,
|
||||
corner_radius, half_size) *
|
||||
gaussian(y, shadow.blur_radius) * step;
|
||||
y += step;
|
||||
}
|
||||
}
|
||||
|
||||
if (shadow.inset != 0u) {
|
||||
// The inset shadow is the complement of the (blurred) hole rect, clipped to the element.
|
||||
// `saturate(0.5 - d)` gives a 1-pixel antialiased edge: d <= -0.5 -> 1, d >= 0.5 -> 0.
|
||||
alpha = 1.0 - alpha;
|
||||
float element_distance = quad_sdf(input.position.xy, shadow.element_bounds,
|
||||
shadow.element_corner_radii);
|
||||
alpha *= saturate(0.5 - element_distance);
|
||||
}
|
||||
|
||||
return input.color * float4(1., 1., 1., alpha);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ impl RenderOnce for SplitButton {
|
|||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}])
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,6 +247,7 @@ impl RenderOnce for KeybindingHint {
|
|||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}])
|
||||
.child(self.keybinding.size(rems_from_px(kb_size))),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ impl RenderOnce for ProgressBar {
|
|||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
}])
|
||||
.child(
|
||||
div()
|
||||
|
|
|
|||
|
|
@ -52,12 +52,14 @@ impl ElevationIndex {
|
|||
offset: point(px(0.), px(2.)),
|
||||
blur_radius: px(3.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., if is_light { 0.03 } else { 0.06 }),
|
||||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
],
|
||||
|
||||
|
|
@ -67,24 +69,28 @@ impl ElevationIndex {
|
|||
offset: point(px(0.), px(2.)),
|
||||
blur_radius: px(3.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., if is_light { 0.06 } else { 0.08 }),
|
||||
offset: point(px(0.), px(3.)),
|
||||
blur_radius: px(6.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., 0.04),
|
||||
offset: point(px(0.), px(6.)),
|
||||
blur_radius: px(12.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
BoxShadow {
|
||||
color: hsla(0., 0., 0., if is_light { 0.04 } else { 0.12 }),
|
||||
offset: point(px(0.), px(1.)),
|
||||
blur_radius: px(0.),
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
},
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -10643,6 +10643,7 @@ pub fn client_side_decorations(
|
|||
},
|
||||
blur_radius: theme::CLIENT_SIDE_DECORATION_SHADOW / 2.,
|
||||
spread_radius: px(0.),
|
||||
inset: false,
|
||||
offset: point(px(0.0), px(0.0)),
|
||||
}])
|
||||
}),
|
||||
|
|
|
|||
Loading…
Reference in a new issue