Introduce a `visible_bounds` parameter to `Element::paint`

Antonio Scandurra created

We're not using this yet but this will be useful to avoid rendering
unnecessary portions of text.

Change summary

gpui/examples/text.rs                    |  1 +
gpui/src/elements.rs                     | 19 +++++++++++++------
gpui/src/elements/align.rs               | 13 +++++++++----
gpui/src/elements/canvas.rs              | 11 +++++------
gpui/src/elements/constrained_box.rs     |  3 ++-
gpui/src/elements/container.rs           |  3 ++-
gpui/src/elements/empty.rs               |  1 +
gpui/src/elements/event_handler.rs       |  3 ++-
gpui/src/elements/flex.rs                |  6 ++++--
gpui/src/elements/label.rs               |  1 +
gpui/src/elements/line_box.rs            | 12 ++++++++----
gpui/src/elements/list.rs                | 12 +++++++++---
gpui/src/elements/mouse_event_handler.rs |  3 ++-
gpui/src/elements/overlay.rs             | 10 ++++++++--
gpui/src/elements/stack.rs               |  3 ++-
gpui/src/elements/svg.rs                 |  8 +++++++-
gpui/src/elements/text.rs                |  1 +
gpui/src/elements/uniform_list.rs        |  3 ++-
gpui/src/presenter.rs                    | 20 +++++++++++++-------
zed/src/editor/element.rs                |  1 +
zed/src/workspace/pane.rs                |  2 +-
21 files changed, 94 insertions(+), 42 deletions(-)

Detailed changes

gpui/examples/text.rs 🔗

@@ -49,6 +49,7 @@ impl gpui::Element for TextElement {
     fn paint(
         &mut self,
         bounds: RectF,
+        _: RectF,
         _: &mut Self::LayoutState,
         cx: &mut gpui::PaintContext,
     ) -> Self::PaintState {

gpui/src/elements.rs 🔗

@@ -50,7 +50,7 @@ use std::{
 
 trait AnyElement {
     fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F;
-    fn paint(&mut self, origin: Vector2F, cx: &mut PaintContext);
+    fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext);
     fn dispatch_event(&mut self, event: &Event, cx: &mut EventContext) -> bool;
     fn debug(&self, cx: &DebugContext) -> serde_json::Value;
 
@@ -71,6 +71,7 @@ pub trait Element {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         layout: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState;
@@ -168,7 +169,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
         result
     }
 
-    fn paint(&mut self, origin: Vector2F, cx: &mut PaintContext) {
+    fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
         *self = match mem::take(self) {
             Lifecycle::PostLayout {
                 mut element,
@@ -177,7 +178,10 @@ impl<T: Element> AnyElement for Lifecycle<T> {
                 mut layout,
             } => {
                 let bounds = RectF::new(origin, size);
-                let paint = element.paint(bounds, &mut layout, cx);
+                let visible_bounds = visible_bounds
+                    .intersection(bounds)
+                    .unwrap_or_else(|| RectF::new(bounds.origin(), Vector2F::default()));
+                let paint = element.paint(bounds, visible_bounds, &mut layout, cx);
                 Lifecycle::PostPaint {
                     element,
                     constraint,
@@ -194,7 +198,10 @@ impl<T: Element> AnyElement for Lifecycle<T> {
                 ..
             } => {
                 let bounds = RectF::new(origin, bounds.size());
-                let paint = element.paint(bounds, &mut layout, cx);
+                let visible_bounds = visible_bounds
+                    .intersection(bounds)
+                    .unwrap_or_else(|| RectF::new(bounds.origin(), Vector2F::default()));
+                let paint = element.paint(bounds, visible_bounds, &mut layout, cx);
                 Lifecycle::PostPaint {
                     element,
                     constraint,
@@ -305,8 +312,8 @@ impl ElementRc {
         self.element.borrow_mut().layout(constraint, cx)
     }
 
-    pub fn paint(&mut self, origin: Vector2F, cx: &mut PaintContext) {
-        self.element.borrow_mut().paint(origin, cx);
+    pub fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
+        self.element.borrow_mut().paint(origin, visible_bounds, cx);
     }
 
     pub fn dispatch_event(&mut self, event: &Event, cx: &mut EventContext) -> bool {

gpui/src/elements/align.rs 🔗

@@ -1,9 +1,10 @@
 use crate::{
+    geometry::{rect::RectF, vector::Vector2F},
     json, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
     SizeConstraint,
 };
 use json::ToJson;
-use pathfinder_geometry::vector::Vector2F;
+
 use serde_json::json;
 
 pub struct Align {
@@ -53,7 +54,8 @@ impl Element for Align {
 
     fn paint(
         &mut self,
-        bounds: pathfinder_geometry::rect::RectF,
+        bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
@@ -63,8 +65,11 @@ impl Element for Align {
         let child_center = self.child.size() / 2.;
         let child_target = child_center + child_center * self.alignment;
 
-        self.child
-            .paint(bounds.origin() - (child_target - my_target), cx);
+        self.child.paint(
+            bounds.origin() - (child_target - my_target),
+            visible_bounds,
+            cx,
+        );
     }
 
     fn dispatch_event(

gpui/src/elements/canvas.rs 🔗

@@ -9,13 +9,11 @@ use pathfinder_geometry::{
     vector::{vec2f, Vector2F},
 };
 
-pub struct Canvas<F>(F)
-where
-    F: FnMut(RectF, &mut PaintContext);
+pub struct Canvas<F>(F);
 
 impl<F> Canvas<F>
 where
-    F: FnMut(RectF, &mut PaintContext),
+    F: FnMut(RectF, RectF, &mut PaintContext),
 {
     pub fn new(f: F) -> Self {
         Self(f)
@@ -24,7 +22,7 @@ where
 
 impl<F> Element for Canvas<F>
 where
-    F: FnMut(RectF, &mut PaintContext),
+    F: FnMut(RectF, RectF, &mut PaintContext),
 {
     type LayoutState = ();
     type PaintState = ();
@@ -50,10 +48,11 @@ where
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        self.0(bounds, cx)
+        self.0(bounds, visible_bounds, cx)
     }
 
     fn dispatch_event(

gpui/src/elements/constrained_box.rs 🔗

@@ -70,10 +70,11 @@ impl Element for ConstrainedBox {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        self.child.paint(bounds.origin(), cx);
+        self.child.paint(bounds.origin(), visible_bounds, cx);
     }
 
     fn dispatch_event(

gpui/src/elements/container.rs 🔗

@@ -169,6 +169,7 @@ impl Element for Container {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
@@ -198,7 +199,7 @@ impl Element for Container {
                 self.style.border.left_width(),
                 self.style.border.top_width(),
             );
-        self.child.paint(child_origin, cx);
+        self.child.paint(child_origin, visible_bounds, cx);
     }
 
     fn dispatch_event(

gpui/src/elements/empty.rs 🔗

@@ -42,6 +42,7 @@ impl Element for Empty {
     fn paint(
         &mut self,
         _: RectF,
+        _: RectF,
         _: &mut Self::LayoutState,
         _: &mut PaintContext,
     ) -> Self::PaintState {

gpui/src/elements/event_handler.rs 🔗

@@ -44,10 +44,11 @@ impl Element for EventHandler {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        self.child.paint(bounds.origin(), cx);
+        self.child.paint(bounds.origin(), visible_bounds, cx);
     }
 
     fn dispatch_event(

gpui/src/elements/flex.rs 🔗

@@ -134,12 +134,13 @@ impl Element for Flex {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
         let mut child_origin = bounds.origin();
         for child in &mut self.children {
-            child.paint(child_origin, cx);
+            child.paint(child_origin, visible_bounds, cx);
             match self.axis {
                 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
                 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
@@ -212,10 +213,11 @@ impl Element for Expanded {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        self.child.paint(bounds.origin(), cx)
+        self.child.paint(bounds.origin(), visible_bounds, cx)
     }
 
     fn dispatch_event(

gpui/src/elements/label.rs 🔗

@@ -128,6 +128,7 @@ impl Element for Label {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         line: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {

gpui/src/elements/line_box.rs 🔗

@@ -48,18 +48,22 @@ impl Element for LineBox {
 
     fn paint(
         &mut self,
-        bounds: pathfinder_geometry::rect::RectF,
+        bounds: RectF,
+        visible_bounds: RectF,
         padding_top: &mut f32,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        self.child
-            .paint(bounds.origin() + vec2f(0., *padding_top), cx);
+        self.child.paint(
+            bounds.origin() + vec2f(0., *padding_top),
+            visible_bounds,
+            cx,
+        );
     }
 
     fn dispatch_event(
         &mut self,
         event: &Event,
-        _: pathfinder_geometry::rect::RectF,
+        _: RectF,
         _: &mut Self::LayoutState,
         _: &mut Self::PaintState,
         cx: &mut EventContext,

gpui/src/elements/list.rs 🔗

@@ -230,12 +230,18 @@ impl Element for List {
         (size, scroll_top)
     }
 
-    fn paint(&mut self, bounds: RectF, scroll_top: &mut ListOffset, cx: &mut PaintContext) {
+    fn paint(
+        &mut self,
+        bounds: RectF,
+        visible_bounds: RectF,
+        scroll_top: &mut ListOffset,
+        cx: &mut PaintContext,
+    ) {
         cx.scene.push_layer(Some(bounds));
 
         let state = &mut *self.state.0.borrow_mut();
         for (mut element, origin) in state.visible_elements(bounds, scroll_top) {
-            element.paint(origin, cx);
+            element.paint(origin, visible_bounds, cx);
         }
 
         cx.scene.pop_layer();
@@ -832,7 +838,7 @@ mod tests {
             (self.size, ())
         }
 
-        fn paint(&mut self, _: RectF, _: &mut (), _: &mut PaintContext) {
+        fn paint(&mut self, _: RectF, _: RectF, _: &mut (), _: &mut PaintContext) {
             todo!()
         }
 

gpui/src/elements/mouse_event_handler.rs 🔗

@@ -81,10 +81,11 @@ impl Element for MouseEventHandler {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        self.child.paint(bounds.origin(), cx);
+        self.child.paint(bounds.origin(), visible_bounds, cx);
     }
 
     fn dispatch_event(

gpui/src/elements/overlay.rs 🔗

@@ -27,10 +27,16 @@ impl Element for Overlay {
         (Vector2F::zero(), size)
     }
 
-    fn paint(&mut self, bounds: RectF, size: &mut Self::LayoutState, cx: &mut PaintContext) {
+    fn paint(
+        &mut self,
+        bounds: RectF,
+        visible_bounds: RectF,
+        size: &mut Self::LayoutState,
+        cx: &mut PaintContext,
+    ) {
         let bounds = RectF::new(bounds.origin(), *size);
         cx.scene.push_stacking_context(None);
-        self.child.paint(bounds.origin(), cx);
+        self.child.paint(bounds.origin(), visible_bounds, cx);
         cx.scene.pop_stacking_context();
     }
 

gpui/src/elements/stack.rs 🔗

@@ -36,12 +36,13 @@ impl Element for Stack {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
         for child in &mut self.children {
             cx.scene.push_layer(None);
-            child.paint(bounds.origin(), cx);
+            child.paint(bounds.origin(), visible_bounds, cx);
             cx.scene.pop_layer();
         }
     }

gpui/src/elements/svg.rs 🔗

@@ -65,7 +65,13 @@ impl Element for Svg {
         }
     }
 
-    fn paint(&mut self, bounds: RectF, svg: &mut Self::LayoutState, cx: &mut PaintContext) {
+    fn paint(
+        &mut self,
+        bounds: RectF,
+        _visible_bounds: RectF,
+        svg: &mut Self::LayoutState,
+        cx: &mut PaintContext,
+    ) {
         if let Some(svg) = svg.clone() {
             cx.scene.push_icon(scene::Icon {
                 bounds,

gpui/src/elements/text.rs 🔗

@@ -76,6 +76,7 @@ impl Element for Text {
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         layout: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {

gpui/src/elements/uniform_list.rs 🔗

@@ -165,6 +165,7 @@ where
     fn paint(
         &mut self,
         bounds: RectF,
+        visible_bounds: RectF,
         layout: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
@@ -174,7 +175,7 @@ where
             bounds.origin() - vec2f(0.0, self.state.scroll_top() % layout.item_height);
 
         for item in &mut layout.items {
-            item.paint(item_origin, cx);
+            item.paint(item_origin, visible_bounds, cx);
             item_origin += vec2f(0.0, layout.item_height);
         }
 

gpui/src/presenter.rs 🔗

@@ -2,6 +2,7 @@ use crate::{
     app::{AppContext, MutableAppContext, WindowInvalidation},
     elements::Element,
     font_cache::FontCache,
+    geometry::rect::RectF,
     json::{self, ToJson},
     platform::Event,
     text_layout::TextLayoutCache,
@@ -111,7 +112,11 @@ impl Presenter {
                 rendered_views: &mut self.rendered_views,
                 app: cx.as_ref(),
             };
-            paint_cx.paint(root_view_id, Vector2F::zero());
+            paint_cx.paint(
+                root_view_id,
+                Vector2F::zero(),
+                RectF::new(Vector2F::zero(), window_size),
+            );
             self.text_layout_cache.finish_frame();
 
             if let Some(event) = self.last_mouse_moved_event.clone() {
@@ -273,9 +278,9 @@ pub struct PaintContext<'a> {
 }
 
 impl<'a> PaintContext<'a> {
-    fn paint(&mut self, view_id: usize, origin: Vector2F) {
+    fn paint(&mut self, view_id: usize, origin: Vector2F, visible_bounds: RectF) {
         if let Some(mut tree) = self.rendered_views.remove(&view_id) {
-            tree.paint(origin, self);
+            tree.paint(origin, visible_bounds, self);
             self.rendered_views.insert(view_id, tree);
         }
     }
@@ -455,17 +460,18 @@ impl Element for ChildView {
 
     fn paint(
         &mut self,
-        bounds: pathfinder_geometry::rect::RectF,
+        bounds: RectF,
+        visible_bounds: RectF,
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        cx.paint(self.view_id, bounds.origin());
+        cx.paint(self.view_id, bounds.origin(), visible_bounds);
     }
 
     fn dispatch_event(
         &mut self,
         event: &Event,
-        _: pathfinder_geometry::rect::RectF,
+        _: RectF,
         _: &mut Self::LayoutState,
         _: &mut Self::PaintState,
         cx: &mut EventContext,
@@ -475,7 +481,7 @@ impl Element for ChildView {
 
     fn debug(
         &self,
-        bounds: pathfinder_geometry::rect::RectF,
+        bounds: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
         cx: &DebugContext,

zed/src/editor/element.rs 🔗

@@ -559,6 +559,7 @@ impl Element for EditorElement {
     fn paint(
         &mut self,
         bounds: RectF,
+        _: RectF,
         layout: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {

zed/src/workspace/pane.rs 🔗

@@ -336,7 +336,7 @@ impl Pane {
         } else {
             let diameter = 8.;
             ConstrainedBox::new(
-                Canvas::new(move |bounds, cx| {
+                Canvas::new(move |bounds, _, cx| {
                     if let Some(current_color) = current_color {
                         let square = RectF::new(bounds.origin(), vec2f(diameter, diameter));
                         cx.scene.push_quad(Quad {