Allow individual corner radii on drop shadows

Nathan Sobo created

Change summary

crates/editor/src/element.rs                       |  6 +-
crates/gpui/src/elements/container.rs              | 21 +++++++-----
crates/gpui/src/platform/mac/renderer.rs           |  6 +++
crates/gpui/src/platform/mac/shaders/shaders.h     |  5 ++
crates/gpui/src/platform/mac/shaders/shaders.metal | 26 ++++++++++++++-
crates/gpui/src/scene.rs                           |  5 +-
6 files changed, 50 insertions(+), 19 deletions(-)

Detailed changes

crates/editor/src/element.rs 🔗

@@ -1129,7 +1129,7 @@ impl EditorElement {
                         bounds,
                         background: Some(color),
                         border,
-                        corner_radii: style.thumb.corner_radius.into(),
+                        corner_radii: style.thumb.corner_radii.into(),
                     })
                 };
                 let background_ranges = editor
@@ -1189,7 +1189,7 @@ impl EditorElement {
                         bounds,
                         background: Some(color),
                         border,
-                        corner_radii: style.thumb.corner_radius.into(),
+                        corner_radii: style.thumb.corner_radii.into(),
                     })
                 }
             }
@@ -1198,7 +1198,7 @@ impl EditorElement {
                 bounds: thumb_bounds,
                 border: style.thumb.border,
                 background: style.thumb.background_color,
-                corner_radii: style.thumb.corner_radius.into(),
+                corner_radii: style.thumb.corner_radii.into(),
             });
         }
 

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

@@ -9,7 +9,7 @@ use crate::{
     },
     json::ToJson,
     platform::CursorStyle,
-    scene::{self, Border, CursorRegion, Quad},
+    scene::{self, Border, CornerRadii, CursorRegion, Quad},
     AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
     ViewContext,
 };
@@ -30,7 +30,7 @@ pub struct ContainerStyle {
     #[serde(default)]
     pub border: Border,
     #[serde(default)]
-    pub corner_radius: f32,
+    pub corner_radii: CornerRadii,
     #[serde(default)]
     pub shadow: Option<Shadow>,
     #[serde(default)]
@@ -133,7 +133,10 @@ impl<V: View> Container<V> {
     }
 
     pub fn with_corner_radius(mut self, radius: f32) -> Self {
-        self.style.corner_radius = radius;
+        self.style.corner_radii.top_left = radius;
+        self.style.corner_radii.top_right = radius;
+        self.style.corner_radii.bottom_right = radius;
+        self.style.corner_radii.bottom_left = radius;
         self
     }
 
@@ -225,7 +228,7 @@ impl<V: View> Element<V> for Container<V> {
         if let Some(shadow) = self.style.shadow.as_ref() {
             scene.push_shadow(scene::Shadow {
                 bounds: quad_bounds + shadow.offset,
-                corner_radius: self.style.corner_radius,
+                corner_radii: self.style.corner_radii,
                 sigma: shadow.blur,
                 color: shadow.color,
             });
@@ -248,7 +251,7 @@ impl<V: View> Element<V> for Container<V> {
                 bounds: quad_bounds,
                 background: self.style.background_color,
                 border: Default::default(),
-                corner_radii: self.style.corner_radius.into(),
+                corner_radii: self.style.corner_radii.into(),
             });
 
             self.child
@@ -259,7 +262,7 @@ impl<V: View> Element<V> for Container<V> {
                 bounds: quad_bounds,
                 background: self.style.overlay_color,
                 border: self.style.border,
-                corner_radii: self.style.corner_radius.into(),
+                corner_radii: self.style.corner_radii.into(),
             });
             scene.pop_layer();
         } else {
@@ -267,7 +270,7 @@ impl<V: View> Element<V> for Container<V> {
                 bounds: quad_bounds,
                 background: self.style.background_color,
                 border: self.style.border,
-                corner_radii: self.style.corner_radius.into(),
+                corner_radii: self.style.corner_radii.into(),
             });
 
             let child_origin = child_origin
@@ -284,7 +287,7 @@ impl<V: View> Element<V> for Container<V> {
                     bounds: quad_bounds,
                     background: self.style.overlay_color,
                     border: Default::default(),
-                    corner_radii: self.style.corner_radius.into(),
+                    corner_radii: self.style.corner_radii.into(),
                 });
                 scene.pop_layer();
             }
@@ -328,7 +331,7 @@ impl ToJson for ContainerStyle {
             "padding": self.padding.to_json(),
             "background_color": self.background_color.to_json(),
             "border": self.border.to_json(),
-            "corner_radius": self.corner_radius,
+            "corner_radius": self.corner_radii,
             "shadow": self.shadow.to_json(),
         })
     }

crates/gpui/src/platform/mac/renderer.rs 🔗

@@ -509,10 +509,14 @@ impl Renderer {
         };
         for (ix, shadow) in shadows.iter().enumerate() {
             let shape_bounds = shadow.bounds * scale_factor;
+            let corner_radii = shadow.corner_radii * scale_factor;
             let shader_shadow = shaders::GPUIShadow {
                 origin: shape_bounds.origin().to_float2(),
                 size: shape_bounds.size().to_float2(),
-                corner_radius: shadow.corner_radius * scale_factor,
+                corner_radius_top_left: corner_radii.top_left,
+                corner_radius_top_right: corner_radii.top_right,
+                corner_radius_bottom_right: corner_radii.bottom_right,
+                corner_radius_bottom_left: corner_radii.bottom_left,
                 sigma: shadow.sigma,
                 color: shadow.color.to_uchar4(),
             };

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

@@ -34,7 +34,10 @@ typedef enum {
 typedef struct {
   vector_float2 origin;
   vector_float2 size;
-  float corner_radius;
+  float corner_radius_top_left;
+  float corner_radius_top_right;
+  float corner_radius_bottom_right;
+  float corner_radius_bottom_left;
   float sigma;
   vector_uchar4 color;
 } GPUIShadow;

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

@@ -146,7 +146,10 @@ struct ShadowFragmentInput {
     float4 position [[position]];
     vector_float2 origin;
     vector_float2 size;
-    float corner_radius;
+    float corner_radius_top_left;
+    float corner_radius_top_right;
+    float corner_radius_bottom_right;
+    float corner_radius_bottom_left;
     float sigma;
     vector_uchar4 color;
 };
@@ -169,7 +172,10 @@ vertex ShadowFragmentInput shadow_vertex(
         device_position,
         shadow.origin,
         shadow.size,
-        shadow.corner_radius,
+        shadow.corner_radius_top_left,
+        shadow.corner_radius_top_right,
+        shadow.corner_radius_bottom_right,
+        shadow.corner_radius_bottom_left,
         shadow.sigma,
         shadow.color,
     };
@@ -179,10 +185,24 @@ fragment float4 shadow_fragment(
     ShadowFragmentInput input [[stage_in]]
 ) {
     float sigma = input.sigma;
-    float corner_radius = input.corner_radius;
     float2 half_size = input.size / 2.;
     float2 center = input.origin + half_size;
     float2 point = input.position.xy - center;
+    float2 center_to_point = input.position.xy - center;
+    float corner_radius;
+    if (center_to_point.x < 0.) {
+        if (center_to_point.y < 0.) {
+            corner_radius = input.corner_radius_top_left;
+        } else {
+            corner_radius = input.corner_radius_bottom_left;
+        }
+    } else {
+        if (center_to_point.y < 0.) {
+            corner_radius = input.corner_radius_top_right;
+        } else {
+            corner_radius = input.corner_radius_bottom_right;
+        }
+    }
 
     // The signal is only non-zero in a limited range, so don't waste samples
     float low = point.y - half_size.y;

crates/gpui/src/scene.rs 🔗

@@ -6,6 +6,7 @@ use collections::HashSet;
 use derive_more::Mul;
 use schemars::JsonSchema;
 use serde::Deserialize;
+use serde_derive::Serialize;
 use serde_json::json;
 use std::{borrow::Cow, sync::Arc};
 
@@ -69,7 +70,7 @@ pub struct Quad {
     pub corner_radii: CornerRadii,
 }
 
-#[derive(Default, Debug, Mul, Clone, Copy)]
+#[derive(Default, Debug, Mul, Clone, Copy, Deserialize, Serialize, JsonSchema)]
 pub struct CornerRadii {
     pub top_left: f32,
     pub top_right: f32,
@@ -91,7 +92,7 @@ impl From<f32> for CornerRadii {
 #[derive(Debug)]
 pub struct Shadow {
     pub bounds: RectF,
-    pub corner_radius: f32,
+    pub corner_radii: CornerRadii,
     pub sigma: f32,
     pub color: Color,
 }