Introduce layer clipping

Antonio Scandurra created

Change summary

gpui/src/elements/stack.rs        | 11 ++++++-----
gpui/src/elements/uniform_list.rs |  8 +++-----
gpui/src/platform/mac/renderer.rs | 20 +++++++++++++++++++-
gpui/src/scene.rs                 | 19 ++++++++++++++++---
zed/src/editor/buffer_element.rs  |  6 +++---
5 files changed, 47 insertions(+), 17 deletions(-)

Detailed changes

gpui/src/elements/stack.rs 🔗

@@ -1,6 +1,7 @@
 use crate::{
-    geometry::vector::Vector2F, AfterLayoutContext, Element, ElementBox, Event, EventContext,
-    LayoutContext, PaintContext, SizeConstraint,
+    geometry::{rect::RectF, vector::Vector2F},
+    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
+    SizeConstraint,
 };
 
 pub struct Stack {
@@ -44,12 +45,12 @@ impl Element for Stack {
 
     fn paint(
         &mut self,
-        bounds: pathfinder_geometry::rect::RectF,
+        bounds: RectF,
         _: &mut Self::LayoutState,
         ctx: &mut PaintContext,
     ) -> Self::PaintState {
         for child in &mut self.children {
-            ctx.scene.push_layer();
+            ctx.scene.push_layer(None);
             child.paint(bounds.origin(), ctx);
             ctx.scene.pop_layer();
         }
@@ -58,7 +59,7 @@ impl Element for Stack {
     fn dispatch_event(
         &mut self,
         event: &Event,
-        _: pathfinder_geometry::rect::RectF,
+        _: RectF,
         _: &mut Self::LayoutState,
         _: &mut Self::PaintState,
         ctx: &mut EventContext,

gpui/src/elements/uniform_list.rs 🔗

@@ -195,10 +195,7 @@ where
         layout: &mut Self::LayoutState,
         ctx: &mut PaintContext,
     ) -> Self::PaintState {
-        // ctx.canvas.save();
-        // let mut clip_path = Path2D::new();
-        // clip_path.rect(RectF::new(origin, self.size.unwrap()));
-        // ctx.canvas.clip_path(clip_path, FillRule::Winding);
+        ctx.scene.push_layer(Some(bounds));
 
         let mut item_origin =
             bounds.origin() - vec2f(0.0, self.state.scroll_top() % layout.item_height);
@@ -207,7 +204,8 @@ where
             item.paint(item_origin, ctx);
             item_origin += vec2f(0.0, layout.item_height);
         }
-        // ctx.canvas.restore();
+
+        ctx.scene.pop_layer();
     }
 
     fn dispatch_event(

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

@@ -1,12 +1,16 @@
 use super::{sprite_cache::SpriteCache, window::RenderContext};
 use crate::{
     color::ColorU,
-    geometry::vector::{vec2i, Vector2I},
+    geometry::{
+        rect::RectF,
+        vector::{vec2f, vec2i, Vector2I},
+    },
     platform,
     scene::Layer,
     Scene,
 };
 use anyhow::{anyhow, Result};
+use cocoa::foundation::NSUInteger;
 use metal::{MTLResourceOptions, NSRange};
 use shaders::{ToFloat2 as _, ToUchar4 as _};
 use std::{collections::HashMap, ffi::c_void, mem, sync::Arc};
@@ -96,12 +100,26 @@ impl Renderer {
 
         let mut offset = 0;
         for layer in scene.layers() {
+            self.clip(scene, layer, ctx);
             self.render_shadows(scene, layer, &mut offset, ctx);
             self.render_quads(scene, layer, &mut offset, ctx);
             self.render_sprites(scene, layer, &mut offset, ctx);
         }
     }
 
+    fn clip(&mut self, scene: &Scene, layer: &Layer, ctx: &RenderContext) {
+        let clip_bounds = layer.clip_bounds().unwrap_or(RectF::new(
+            vec2f(0., 0.),
+            ctx.drawable_size / scene.scale_factor(),
+        )) * scene.scale_factor();
+        ctx.command_encoder.set_scissor_rect(metal::MTLScissorRect {
+            x: clip_bounds.origin_x() as NSUInteger,
+            y: clip_bounds.origin_y() as NSUInteger,
+            width: clip_bounds.width() as NSUInteger,
+            height: clip_bounds.height() as NSUInteger,
+        });
+    }
+
     fn render_shadows(
         &mut self,
         scene: &Scene,

gpui/src/scene.rs 🔗

@@ -57,7 +57,7 @@ impl Scene {
     pub fn new(scale_factor: f32) -> Self {
         Scene {
             scale_factor,
-            layers: vec![Layer::default()],
+            layers: vec![Layer::new(None)],
             active_layer_stack: vec![0],
         }
     }
@@ -70,9 +70,9 @@ impl Scene {
         self.layers.as_slice()
     }
 
-    pub fn push_layer(&mut self) {
+    pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
         let ix = self.layers.len();
-        self.layers.push(Layer::default());
+        self.layers.push(Layer::new(clip_bounds));
         self.active_layer_stack.push(ix);
     }
 
@@ -99,6 +99,19 @@ impl Scene {
 }
 
 impl Layer {
+    pub fn new(clip_bounds: Option<RectF>) -> Self {
+        Self {
+            clip_bounds,
+            quads: Vec::new(),
+            shadows: Vec::new(),
+            glyphs: Vec::new(),
+        }
+    }
+
+    pub fn clip_bounds(&self) -> Option<RectF> {
+        self.clip_bounds
+    }
+
     fn push_quad(&mut self, quad: Quad) {
         self.quads.push(quad);
     }

zed/src/editor/buffer_element.rs 🔗

@@ -168,7 +168,7 @@ impl BufferElement {
         let line_height = view.line_height(ctx.font_cache);
         let scroll_top = view.scroll_position().y() * line_height;
 
-        ctx.scene.push_layer();
+        ctx.scene.push_layer(Some(rect));
         ctx.scene.push_quad(Quad {
             bounds: rect,
             background: Some(ColorU::white()),
@@ -202,7 +202,7 @@ impl BufferElement {
         let max_glyph_width = view.em_width(ctx.font_cache);
         let scroll_left = view.scroll_position().x() * max_glyph_width;
 
-        ctx.scene.push_layer();
+        ctx.scene.push_layer(Some(bounds));
         ctx.scene.push_quad(Quad {
             bounds,
             background: Some(ColorU::white()),
@@ -290,7 +290,7 @@ impl BufferElement {
             );
         }
 
-        ctx.scene.push_layer();
+        ctx.scene.push_layer(Some(bounds));
         for cursor in cursors {
             cursor.paint(ctx);
         }