Add 'overlay' property to border

Max Brunsfeld and Nathan Sobo created

For containers, this causes the border to be drawn on top of the child element.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

gpui/src/elements/container.rs | 54 ++++++++++++++++++++++++++---------
gpui/src/scene.rs              |  6 ++++
2 files changed, 46 insertions(+), 14 deletions(-)

Detailed changes

gpui/src/elements/container.rs 🔗

@@ -167,7 +167,10 @@ impl Element for Container {
         constraint: SizeConstraint,
         cx: &mut LayoutContext,
     ) -> (Vector2F, Self::LayoutState) {
-        let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
+        let mut size_buffer = self.margin_size() + self.padding_size();
+        if !self.style.border.overlay {
+            size_buffer += self.border_size();
+        }
         let child_constraint = SizeConstraint {
             min: (constraint.min - size_buffer).max(Vector2F::zero()),
             max: (constraint.max - size_buffer).max(Vector2F::zero()),
@@ -196,20 +199,43 @@ impl Element for Container {
                 color: shadow.color,
             });
         }
-        cx.scene.push_quad(Quad {
-            bounds: quad_bounds,
-            background: self.style.background_color,
-            border: self.style.border,
-            corner_radius: self.style.corner_radius,
-        });
 
-        let child_origin = quad_bounds.origin()
-            + vec2f(self.style.padding.left, self.style.padding.top)
-            + vec2f(
-                self.style.border.left_width(),
-                self.style.border.top_width(),
-            );
-        self.child.paint(child_origin, visible_bounds, cx);
+        let child_origin =
+            quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
+
+        if self.style.border.overlay {
+            cx.scene.push_quad(Quad {
+                bounds: quad_bounds,
+                background: self.style.background_color,
+                border: Default::default(),
+                corner_radius: self.style.corner_radius,
+            });
+
+            self.child.paint(child_origin, visible_bounds, cx);
+
+            cx.scene.push_layer(None);
+            cx.scene.push_quad(Quad {
+                bounds: quad_bounds,
+                background: Default::default(),
+                border: self.style.border,
+                corner_radius: self.style.corner_radius,
+            });
+            cx.scene.pop_layer();
+        } else {
+            cx.scene.push_quad(Quad {
+                bounds: quad_bounds,
+                background: self.style.background_color,
+                border: self.style.border,
+                corner_radius: self.style.corner_radius,
+            });
+
+            let child_origin = child_origin
+                + vec2f(
+                    self.style.border.left_width(),
+                    self.style.border.top_width(),
+                );
+            self.child.paint(child_origin, visible_bounds, cx);
+        }
     }
 
     fn dispatch_event(

gpui/src/scene.rs 🔗

@@ -69,6 +69,7 @@ pub struct Icon {
 pub struct Border {
     pub width: f32,
     pub color: Color,
+    pub overlay: bool,
     pub top: bool,
     pub right: bool,
     pub bottom: bool,
@@ -85,6 +86,8 @@ impl<'de> Deserialize<'de> for Border {
             pub width: f32,
             pub color: Color,
             #[serde(default)]
+            pub overlay: bool,
+            #[serde(default)]
             pub top: bool,
             #[serde(default)]
             pub right: bool,
@@ -98,6 +101,7 @@ impl<'de> Deserialize<'de> for Border {
         let mut border = Border {
             width: data.width,
             color: data.color,
+            overlay: data.overlay,
             top: data.top,
             bottom: data.bottom,
             left: data.left,
@@ -329,6 +333,7 @@ impl Border {
         Self {
             width,
             color,
+            overlay: false,
             top: false,
             left: false,
             bottom: false,
@@ -340,6 +345,7 @@ impl Border {
         Self {
             width,
             color,
+            overlay: false,
             top: true,
             left: true,
             bottom: true,