Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/build.rs                       |   3 
crates/gpui3/src/elements/div.rs            |  33 ++----
crates/gpui3/src/geometry.rs                |  36 +++++++
crates/gpui3/src/gpui3.rs                   |  12 +-
crates/gpui3/src/platform/mac/shaders.metal |  18 +-
crates/gpui3/src/scene.rs                   |   5 
crates/gpui3/src/style.rs                   |  59 +++++++++++
crates/gpui3/src/util.rs                    |   6 -
crates/gpui3/src/window.rs                  | 114 +++++++++++++++++-----
crates/storybook2/src/theme.rs              |   4 
10 files changed, 212 insertions(+), 78 deletions(-)

Detailed changes

crates/gpui3/build.rs 🔗

@@ -45,6 +45,7 @@ fn generate_shader_bindings() -> PathBuf {
         "Pixels".into(),
         "PointF".into(),
         "Hsla".into(),
+        "ScaledContentMask".into(),
         "Uniforms".into(),
         "AtlasTile".into(),
         "Quad".into(),
@@ -58,12 +59,14 @@ fn generate_shader_bindings() -> PathBuf {
         .with_src(crate_dir.join("src/scene.rs"))
         .with_src(crate_dir.join("src/geometry.rs"))
         .with_src(crate_dir.join("src/color.rs"))
+        .with_src(crate_dir.join("src/window.rs"))
         .with_src(crate_dir.join("src/platform.rs"))
         .with_src(crate_dir.join("src/platform/mac/metal_renderer.rs"))
         .with_config(config)
         .generate()
         .expect("Unable to generate bindings")
         .write_to_file(&output_path);
+
     output_path
 }
 

crates/gpui3/src/elements/div.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     AnyElement, Bounds, Element, Layout, LayoutId, Overflow, ParentElement, Pixels, Point,
-    Refineable, RefinementCascade, Result, StackContext, Style, StyleHelpers, Styled, ViewContext,
+    Refineable, RefinementCascade, Result, Style, StyleHelpers, Styled, ViewContext,
 };
 use parking_lot::Mutex;
 use smallvec::SmallVec;
@@ -33,16 +33,9 @@ impl<S: 'static + Send + Sync> Element for Div<S> {
         cx: &mut ViewContext<S>,
     ) -> Result<(LayoutId, Self::FrameState)> {
         let style = self.computed_style();
-        let child_layout_ids = if let Some(text_style) = style.text_style(cx) {
-            cx.with_text_style(text_style.clone(), |cx| self.layout_children(view, cx))?
-        } else {
-            self.layout_children(view, cx)?
-        };
-
-        Ok((
-            cx.request_layout(style.into(), child_layout_ids.clone())?,
-            child_layout_ids,
-        ))
+        let child_layout_ids = style.apply_text_style(cx, |cx| self.layout_children(view, cx))?;
+        let layout_id = cx.request_layout(style.into(), child_layout_ids.clone())?;
+        Ok((layout_id, child_layout_ids))
     }
 
     fn paint(
@@ -56,20 +49,18 @@ impl<S: 'static + Send + Sync> Element for Div<S> {
 
         let style = self.computed_style();
         style.paint(order, bounds, cx);
-        let overflow = &style.overflow;
+
         // // todo!("support only one dimension being hidden")
+        let overflow = &style.overflow;
         // if style.overflow.y != Overflow::Visible || style.overflow.x != Overflow::Visible {
-        //     cx.scene().push_layer(Some(bounds));
-        //     pop_layer = true;
+        //     cx.clip(layout.bounds, style.corner_radii, || )
         // }
-        if let Some(text_style) = style.text_style(cx) {
-            cx.with_text_style(text_style.clone(), |cx| {
-                self.paint_children(overflow, state, cx)
-            })?;
-        } else {
-            self.paint_children(overflow, state, cx)?;
-        }
 
+        style.apply_text_style(cx, |cx| {
+            style.apply_overflow(layout.bounds, cx, |cx| {
+                self.paint_children(overflow, state, cx)
+            })
+        })?;
         self.handle_scroll(order, bounds, style.overflow.clone(), child_layouts, cx);
 
         // todo!("enable inspector")

crates/gpui3/src/geometry.rs 🔗

@@ -225,6 +225,20 @@ pub struct Bounds<T: Clone + Debug> {
     pub size: Size<T>,
 }
 
+impl<T: Clone + Debug + Sub<Output = T>> Bounds<T> {
+    pub fn from_corners(upper_left: Point<T>, lower_right: Point<T>) -> Self {
+        let origin = Point {
+            x: upper_left.x.clone(),
+            y: upper_left.y.clone(),
+        };
+        let size = Size {
+            width: lower_right.x - upper_left.x,
+            height: lower_right.y - upper_left.y,
+        };
+        Bounds { origin, size }
+    }
+}
+
 impl<T, Rhs> Mul<Rhs> for Bounds<T>
 where
     T: Mul<Rhs, Output = Rhs> + Clone + Debug,
@@ -418,6 +432,28 @@ pub struct Corners<T: Clone + Debug> {
     pub bottom_left: T,
 }
 
+impl Corners<AbsoluteLength> {
+    pub fn to_pixels(&self, rem_size: Pixels) -> Corners<Pixels> {
+        Corners {
+            top_left: self.top_left.to_pixels(rem_size),
+            top_right: self.top_right.to_pixels(rem_size),
+            bottom_right: self.bottom_right.to_pixels(rem_size),
+            bottom_left: self.bottom_left.to_pixels(rem_size),
+        }
+    }
+}
+
+impl Corners<Pixels> {
+    pub fn scale(&self, factor: f32) -> Corners<ScaledPixels> {
+        Corners {
+            top_left: self.top_left.scale(factor),
+            top_right: self.top_right.scale(factor),
+            bottom_right: self.bottom_right.scale(factor),
+            bottom_left: self.bottom_left.scale(factor),
+        }
+    }
+}
+
 impl<T: Clone + Debug> Corners<T> {
     pub fn map<U: Clone + Debug, F: Fn(&T) -> U>(&self, f: F) -> Corners<U> {
         Corners {

crates/gpui3/src/gpui3.rs 🔗

@@ -84,16 +84,16 @@ impl<T> DerefMut for MainThread<T> {
     }
 }
 
-pub trait StackContext {
-    fn app(&mut self) -> &mut AppContext;
+pub trait BorrowAppContext {
+    fn app_mut(&mut self) -> &mut AppContext;
 
     fn with_text_style<F, R>(&mut self, style: TextStyleRefinement, f: F) -> R
     where
         F: FnOnce(&mut Self) -> R,
     {
-        self.app().push_text_style(style);
+        self.app_mut().push_text_style(style);
         let result = f(self);
-        self.app().pop_text_style();
+        self.app_mut().pop_text_style();
         result
     }
 
@@ -101,9 +101,9 @@ pub trait StackContext {
     where
         F: FnOnce(&mut Self) -> R,
     {
-        self.app().push_state(state);
+        self.app_mut().push_state(state);
         let result = f(self);
-        self.app().pop_state::<T>();
+        self.app_mut().pop_state::<T>();
         result
     }
 }

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

@@ -4,10 +4,10 @@
 using namespace metal;
 
 float4 hsla_to_rgba(Hsla hsla);
-float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds,
-                          Bounds_Pixels clip_bounds,
+float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds,
+                          Bounds_ScaledPixels clip_bounds,
                           constant Size_DevicePixels *viewport_size);
-float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels corner_radii);
+float quad_sdf(float2 point, Bounds_ScaledPixels bounds, Corners_ScaledPixels corner_radii);
 
 struct QuadVertexOutput {
   float4 position [[position]];
@@ -131,7 +131,7 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
   float2 unit_vertex = unit_vertices[unit_vertex_id];
   MonochromeSprite sprite = sprites[sprite_id];
   float4 device_position = to_device_position(
-      unit_vertex, sprite.bounds, sprite.clip_bounds, viewport_size);
+      unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
 
   float2 tile_origin =
       float2(sprite.tile.bounds.origin.x, sprite.tile.bounds.origin.y);
@@ -157,7 +157,7 @@ fragment float4 monochrome_sprite_fragment(
   float4 sample =
       atlas_texture.sample(atlas_texture_sampler, input.tile_position);
   float clip_distance =
-      quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii);
+      quad_sdf(input.position.xy, sprite.content_mask.bounds, sprite.content_mask.corner_radii);
   float4 color = input.color;
   color.a *= sample.a * saturate(0.5 - clip_distance);
   return color;
@@ -211,8 +211,8 @@ float4 hsla_to_rgba(Hsla hsla) {
   return rgba;
 }
 
-float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds,
-                          Bounds_Pixels clip_bounds,
+float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds,
+                          Bounds_ScaledPixels clip_bounds,
                           constant Size_DevicePixels *input_viewport_size) {
   float2 position =
       unit_vertex * float2(bounds.size.width, bounds.size.height) +
@@ -229,8 +229,8 @@ float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds,
   return float4(device_position, 0., 1.);
 }
 
-float quad_sdf(float2 point, Bounds_Pixels bounds,
-               Corners_Pixels corner_radii) {
+float quad_sdf(float2 point, Bounds_ScaledPixels bounds,
+               Corners_ScaledPixels corner_radii) {
   float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.;
   float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size;
   float2 center_to_point = point - center;

crates/gpui3/src/scene.rs 🔗

@@ -1,7 +1,7 @@
 use std::{iter::Peekable, mem};
 
 use super::{Bounds, Hsla, Point};
-use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels};
+use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledContentMask, ScaledPixels};
 use collections::BTreeMap;
 use smallvec::SmallVec;
 
@@ -234,8 +234,7 @@ impl From<Quad> for Primitive {
 pub struct MonochromeSprite {
     pub order: u32,
     pub bounds: Bounds<ScaledPixels>,
-    pub clip_bounds: Bounds<ScaledPixels>,
-    pub clip_corner_radii: Corners<ScaledPixels>,
+    pub content_mask: ScaledContentMask,
     pub color: Hsla,
     pub tile: AtlasTile,
 }

crates/gpui3/src/style.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
-    phi, rems, AbsoluteLength, Bounds, Corners, CornersRefinement, DefiniteLength, Edges,
-    EdgesRefinement, Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point,
-    PointRefinement, Quad, Rems, Result, RunStyle, SharedString, Size, SizeRefinement, ViewContext,
-    WindowContext,
+    phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners,
+    CornersRefinement, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, FontStyle,
+    FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Quad, Rems, Result, RunStyle,
+    SharedString, Size, SizeRefinement, ViewContext, WindowContext,
 };
 use refineable::Refineable;
 pub use taffy::style::{
@@ -179,6 +179,57 @@ impl Style {
         }
     }
 
+    pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
+    where
+        C: BorrowAppContext,
+        F: FnOnce(&mut C) -> R,
+    {
+        if self.text.is_some() {
+            cx.with_text_style(self.text.clone(), f)
+        } else {
+            f(cx)
+        }
+    }
+
+    /// Apply overflow to content mask
+    pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R
+    where
+        C: BorrowWindow,
+        F: FnOnce(&mut C) -> R,
+    {
+        let current_mask = cx.content_mask();
+
+        let min = current_mask.bounds.origin;
+        let max = current_mask.bounds.lower_right();
+
+        let mask_corner_radii = Corners::default();
+        let mask_bounds = match (
+            self.overflow.x == Overflow::Visible,
+            self.overflow.y == Overflow::Visible,
+        ) {
+            // x and y both visible
+            (true, true) => return f(cx),
+            // x visible, y hidden
+            (true, false) => Bounds::from_corners(
+                point(min.x, bounds.origin.y),
+                point(max.x, bounds.lower_right().y),
+            ),
+            // x hidden, y visible
+            (false, true) => Bounds::from_corners(
+                point(bounds.origin.x, min.y),
+                point(bounds.lower_right().x, max.y),
+            ),
+            // both hidden
+            (false, false) => bounds,
+        };
+        let mask = ContentMask {
+            bounds: mask_bounds,
+            corner_radii: mask_corner_radii,
+        };
+
+        cx.with_content_mask(mask, f)
+    }
+
     /// Paints the background of an element styled with this style.
     pub fn paint<V: 'static>(&self, order: u32, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
         let rem_size = cx.rem_size();

crates/gpui3/src/util.rs 🔗

@@ -2,12 +2,6 @@ use smol::future::FutureExt;
 use std::{future::Future, time::Duration};
 pub use util::*;
 
-pub fn post_inc(value: &mut usize) -> usize {
-    let prev = *value;
-    *value += 1;
-    prev
-}
-
 pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
 where
     F: Future<Output = T>,

crates/gpui3/src/window.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
-    px, AnyView, AppContext, AvailableSpace, Bounds, Context, Corners, Effect, Element, EntityId,
-    FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId, MainThread,
-    MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference,
-    Scene, Size, StackContext, Style, TaffyLayoutEngine, WeakHandle, WindowOptions,
+    px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners, Effect,
+    Element, EntityId, FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId,
+    MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point,
+    Reference, ScaledPixels, Scene, Size, Style, TaffyLayoutEngine, WeakHandle, WindowOptions,
     SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
@@ -74,8 +74,24 @@ impl Window {
 
 #[derive(Clone, Debug)]
 pub struct ContentMask {
-    bounds: Bounds<Pixels>,
-    corner_radii: Corners<Pixels>,
+    pub bounds: Bounds<Pixels>,
+    pub corner_radii: Corners<Pixels>,
+}
+
+impl ContentMask {
+    pub fn scale(&self, factor: f32) -> ScaledContentMask {
+        ScaledContentMask {
+            bounds: self.bounds.scale(factor),
+            corner_radii: self.corner_radii.scale(factor),
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[repr(C)]
+pub struct ScaledContentMask {
+    bounds: Bounds<ScaledPixels>,
+    corner_radii: Corners<ScaledPixels>,
 }
 
 pub struct WindowContext<'a, 'w> {
@@ -174,20 +190,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.current_layer_id.clone()
     }
 
-    pub fn current_clipping_mask(&self) -> ContentMask {
-        self.window
-            .content_mask_stack
-            .last()
-            .cloned()
-            .unwrap_or_else(|| ContentMask {
-                bounds: Bounds {
-                    origin: Point::default(),
-                    size: self.window.content_size,
-                },
-                corner_radii: Default::default(),
-            })
-    }
-
     pub fn run_on_main<R>(
         &self,
         f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
@@ -239,14 +241,14 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
                 size: tile.bounds.size.map(Into::into),
             };
+            let content_mask = self.content_mask().scale(scale_factor);
 
             self.window.scene.insert(
                 layer_id,
                 MonochromeSprite {
                     order,
                     bounds,
-                    clip_bounds: bounds,
-                    clip_corner_radii: Default::default(),
+                    content_mask,
                     color,
                     tile,
                 },
@@ -330,8 +332,60 @@ impl<'a, 'w> std::ops::DerefMut for WindowContext<'a, 'w> {
     }
 }
 
-impl<S> StackContext for ViewContext<'_, '_, S> {
-    fn app(&mut self) -> &mut AppContext {
+impl BorrowAppContext for WindowContext<'_, '_> {
+    fn app_mut(&mut self) -> &mut AppContext {
+        &mut *self.app
+    }
+}
+
+pub trait BorrowWindow: BorrowAppContext {
+    fn window(&self) -> &Window;
+    fn window_mut(&mut self) -> &mut Window;
+
+    fn with_content_mask<R>(&mut self, mask: ContentMask, f: impl FnOnce(&mut Self) -> R) -> R {
+        self.window_mut().content_mask_stack.push(mask);
+        let result = f(self);
+        self.window_mut().content_mask_stack.pop();
+        result
+    }
+
+    fn content_mask(&self) -> ContentMask {
+        self.window()
+            .content_mask_stack
+            .last()
+            .cloned()
+            .unwrap_or_else(|| ContentMask {
+                bounds: Bounds {
+                    origin: Point::default(),
+                    size: self.window().content_size,
+                },
+                corner_radii: Default::default(),
+            })
+    }
+
+    fn rem_size(&self) -> Pixels {
+        self.window().rem_size
+    }
+}
+
+impl BorrowWindow for WindowContext<'_, '_> {
+    fn window(&self) -> &Window {
+        &*self.window
+    }
+
+    fn window_mut(&mut self) -> &mut Window {
+        &mut *self.window
+    }
+}
+
+pub struct ViewContext<'a, 'w, S> {
+    window_cx: WindowContext<'a, 'w>,
+    entity_type: PhantomData<S>,
+    entity_id: EntityId,
+}
+
+impl<S> BorrowAppContext for ViewContext<'_, '_, S> {
+    fn app_mut(&mut self) -> &mut AppContext {
         &mut *self.window_cx.app
     }
 
@@ -356,10 +410,14 @@ impl<S> StackContext for ViewContext<'_, '_, S> {
     }
 }
 
-pub struct ViewContext<'a, 'w, S> {
-    window_cx: WindowContext<'a, 'w>,
-    entity_type: PhantomData<S>,
-    entity_id: EntityId,
+impl<S> BorrowWindow for ViewContext<'_, '_, S> {
+    fn window(&self) -> &Window {
+        &self.window_cx.window
+    }
+
+    fn window_mut(&mut self) -> &mut Window {
+        &mut *self.window_cx.window
+    }
 }
 
 impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {

crates/storybook2/src/theme.rs 🔗

@@ -1,4 +1,6 @@
-use gpui3::{Element, Hsla, Layout, LayoutId, Result, StackContext, ViewContext, WindowContext};
+use gpui3::{
+    BorrowAppContext, Element, Hsla, Layout, LayoutId, Result, ViewContext, WindowContext,
+};
 use serde::{de::Visitor, Deserialize, Deserializer};
 use std::{collections::HashMap, fmt};