Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/src/elements/text.rs                 |   8 
crates/gpui3/src/geometry.rs                      |  13 
crates/gpui3/src/platform/mac/shaders.metal       | 314 ++++++++-------
crates/gpui3/src/scene.rs                         |  41 +
crates/gpui3/src/text_system.rs                   |  21 
crates/gpui3/src/text_system/text_layout_cache.rs | 327 ----------------
crates/gpui3/src/window.rs                        |  10 
7 files changed, 229 insertions(+), 505 deletions(-)

Detailed changes

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

@@ -3,7 +3,7 @@ use crate::{
 };
 use parking_lot::Mutex;
 use std::{marker::PhantomData, sync::Arc};
-use util::{arc_cow::ArcCow, ResultExt};
+use util::arc_cow::ArcCow;
 
 impl<S: 'static> IntoAnyElement<S> for ArcCow<'static, str> {
     fn into_any(self) -> AnyElement<S> {
@@ -92,8 +92,6 @@ impl<S: 'static> Element for Text<S> {
         paint_state: &mut Self::FrameState,
         cx: &mut ViewContext<S>,
     ) -> Result<()> {
-        let bounds = layout.bounds;
-
         let line;
         let line_height;
         {
@@ -108,8 +106,8 @@ impl<S: 'static> Element for Text<S> {
         let _text_style = cx.text_style();
 
         // todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder.");
-        let visible_bounds = bounds;
-        line.paint(bounds.origin, visible_bounds, line_height, cx)?;
+        let visible_bounds = layout.bounds;
+        line.paint(&layout, visible_bounds, line_height, cx)?;
 
         Ok(())
     }

crates/gpui3/src/geometry.rs 🔗

@@ -425,7 +425,9 @@ unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Corners<T> {}
 
 unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Corners<T> {}
 
-#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
+#[derive(
+    Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd, Zeroable, Pod,
+)]
 #[repr(transparent)]
 pub struct Pixels(pub(crate) f32);
 
@@ -445,6 +447,12 @@ impl Mul<Pixels> for f32 {
     }
 }
 
+impl MulAssign<f32> for Pixels {
+    fn mul_assign(&mut self, other: f32) {
+        self.0 *= other;
+    }
+}
+
 impl Pixels {
     pub fn round(&self) -> Self {
         Self(self.0.round())
@@ -489,9 +497,6 @@ impl From<f32> for Pixels {
     }
 }
 
-unsafe impl bytemuck::Pod for Pixels {}
-unsafe impl bytemuck::Zeroable for Pixels {}
-
 impl Debug for Pixels {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "{} px", self.0)

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

@@ -7,174 +7,194 @@ float4 hsla_to_rgba(Hsla hsla);
 float4 to_device_position(float2 pixel_position, float2 viewport_size);
 
 struct QuadVertexOutput {
-    float4 position [[position]];
-    float4 background_color;
-    float4 border_color;
-    uint quad_id;
+  float4 position [[position]];
+  float4 background_color;
+  float4 border_color;
+  uint quad_id;
 };
 
 vertex QuadVertexOutput quad_vertex(
-    uint unit_vertex_id [[vertex_id]],
-    uint quad_id [[instance_id]],
+    uint unit_vertex_id [[vertex_id]], uint quad_id [[instance_id]],
     constant float2 *unit_vertices [[buffer(QuadInputIndex_Vertices)]],
     constant Quad *quads [[buffer(QuadInputIndex_Quads)]],
-    constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]
-) {
-    float2 unit_vertex = unit_vertices[unit_vertex_id];
-    Quad quad = quads[quad_id];
-    float2 position_2d = unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) + float2(quad.bounds.origin.x, quad.bounds.origin.y);
-    position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x);
-    position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width, position_2d.x);
-    position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y);
-    position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height, position_2d.y);
-
-    float2 viewport_size = float2((float)uniforms->viewport_size.width, (float)uniforms->viewport_size.height);
-    float4 device_position = to_device_position(position_2d, viewport_size);
-    float4 background_color = hsla_to_rgba(quad.background);
-    float4 border_color = hsla_to_rgba(quad.border_color);
-    return QuadVertexOutput {
-        device_position,
-        background_color,
-        border_color,
-        quad_id
-    };
+    constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]) {
+  float2 unit_vertex = unit_vertices[unit_vertex_id];
+  Quad quad = quads[quad_id];
+  float2 position_2d =
+      unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) +
+      float2(quad.bounds.origin.x, quad.bounds.origin.y);
+  position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x);
+  position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width,
+                      position_2d.x);
+  position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y);
+  position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height,
+                      position_2d.y);
+
+  float2 viewport_size = float2((float)uniforms->viewport_size.width,
+                                (float)uniforms->viewport_size.height);
+  float4 device_position = to_device_position(position_2d, viewport_size);
+  float4 background_color = hsla_to_rgba(quad.background);
+  float4 border_color = hsla_to_rgba(quad.border_color);
+  return QuadVertexOutput{device_position, background_color, border_color,
+                          quad_id};
 }
 
-float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels 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;
-    float corner_radius;
-    if (center_to_point.x < 0.) {
-        if (center_to_point.y < 0.) {
-            corner_radius = corner_radii.top_left;
-        } else {
-            corner_radius = corner_radii.bottom_left;
-        }
+float quad_sdf(float2 point, Bounds_Pixels bounds,
+               Corners_Pixels 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;
+  float corner_radius;
+  if (center_to_point.x < 0.) {
+    if (center_to_point.y < 0.) {
+      corner_radius = corner_radii.top_left;
     } else {
-        if (center_to_point.y < 0.) {
-            corner_radius = corner_radii.top_right;
-        } else {
-            corner_radius = corner_radii.bottom_right;
-        }
+      corner_radius = corner_radii.bottom_left;
     }
+  } else {
+    if (center_to_point.y < 0.) {
+      corner_radius = corner_radii.top_right;
+    } else {
+      corner_radius = corner_radii.bottom_right;
+    }
+  }
 
-    float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius;
-    float distance = length(max(0., rounded_edge_to_point))
-                     + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y))
-                     - corner_radius;
+  float2 rounded_edge_to_point =
+      abs(center_to_point) - half_size + corner_radius;
+  float distance =
+      length(max(0., rounded_edge_to_point)) +
+      min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
+      corner_radius;
 
-    return distance;
+  return distance;
 }
 
-fragment float4 quad_fragment(
-    QuadVertexOutput input [[stage_in]],
-    constant Quad *quads [[buffer(QuadInputIndex_Quads)]]
-) {
-    Quad quad = quads[input.quad_id];
-    float2 half_size = float2(quad.bounds.size.width, quad.bounds.size.height) / 2.;
-    float2 center = float2(quad.bounds.origin.x, quad.bounds.origin.y) + half_size;
-    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 = quad.corner_radii.top_left;
-        } else {
-            corner_radius = quad.corner_radii.bottom_left;
-        }
-    } else {
-        if (center_to_point.y < 0.) {
-            corner_radius = quad.corner_radii.top_right;
-        } else {
-            corner_radius = quad.corner_radii.bottom_right;
-        }
-    }
-
-    float2 rounded_edge_to_point = fabs(center_to_point) - half_size + corner_radius;
-    float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius;
-
-    float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left : quad.border_widths.right;
-    float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top : quad.border_widths.bottom;
-    float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
-    float2 point_to_inset_corner = fabs(center_to_point) - inset_size;
-    float border_width;
-    if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
-        border_width = 0.;
-    } else if (point_to_inset_corner.y > point_to_inset_corner.x) {
-        border_width = horizontal_border;
+fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
+                              constant Quad *quads
+                              [[buffer(QuadInputIndex_Quads)]]) {
+  Quad quad = quads[input.quad_id];
+  float2 half_size =
+      float2(quad.bounds.size.width, quad.bounds.size.height) / 2.;
+  float2 center =
+      float2(quad.bounds.origin.x, quad.bounds.origin.y) + half_size;
+  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 = quad.corner_radii.top_left;
     } else {
-        border_width = vertical_border;
+      corner_radius = quad.corner_radii.bottom_left;
     }
-
-    float4 color;
-    if (border_width == 0.) {
-        color = input.background_color;
+  } else {
+    if (center_to_point.y < 0.) {
+      corner_radius = quad.corner_radii.top_right;
     } else {
-        float inset_distance = distance + border_width;
-
-        // Decrease border's opacity as we move inside the background.
-        input.border_color.a *= 1. - saturate(0.5 - inset_distance);
-
-        // Alpha-blend the border and the background.
-        float output_alpha = quad.border_color.a + quad.background.a * (1. - quad.border_color.a);
-        float3 premultiplied_border_rgb = input.border_color.rgb * quad.border_color.a;
-        float3 premultiplied_background_rgb = input.background_color.rgb * input.background_color.a;
-        float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - input.border_color.a);
-        color = float4(premultiplied_output_rgb, output_alpha);
+      corner_radius = quad.corner_radii.bottom_right;
     }
-
-    float clip_distance = quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii);
-    return color * float4(1., 1., 1., saturate(0.5 - distance) * saturate(0.5 - clip_distance));
+  }
+
+  float2 rounded_edge_to_point =
+      fabs(center_to_point) - half_size + corner_radius;
+  float distance =
+      length(max(0., rounded_edge_to_point)) +
+      min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
+      corner_radius;
+
+  float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left
+                                                  : quad.border_widths.right;
+  float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top
+                                                    : quad.border_widths.bottom;
+  float2 inset_size =
+      half_size - corner_radius - float2(vertical_border, horizontal_border);
+  float2 point_to_inset_corner = fabs(center_to_point) - inset_size;
+  float border_width;
+  if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
+    border_width = 0.;
+  } else if (point_to_inset_corner.y > point_to_inset_corner.x) {
+    border_width = horizontal_border;
+  } else {
+    border_width = vertical_border;
+  }
+
+  float4 color;
+  if (border_width == 0.) {
+    color = input.background_color;
+  } else {
+    float inset_distance = distance + border_width;
+
+    // Decrease border's opacity as we move inside the background.
+    input.border_color.a *= 1. - saturate(0.5 - inset_distance);
+
+    // Alpha-blend the border and the background.
+    float output_alpha =
+        quad.border_color.a + quad.background.a * (1. - quad.border_color.a);
+    float3 premultiplied_border_rgb =
+        input.border_color.rgb * quad.border_color.a;
+    float3 premultiplied_background_rgb =
+        input.background_color.rgb * input.background_color.a;
+    float3 premultiplied_output_rgb =
+        premultiplied_border_rgb +
+        premultiplied_background_rgb * (1. - input.border_color.a);
+    color = float4(premultiplied_output_rgb, output_alpha);
+  }
+
+  float clip_distance =
+      quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii);
+  return color *
+         float4(1., 1., 1.,
+                saturate(0.5 - distance) * saturate(0.5 - clip_distance));
 }
 
 float4 hsla_to_rgba(Hsla hsla) {
-    float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
-    float s = hsla.s;
-    float l = hsla.l;
-    float a = hsla.a;
-
-    float c = (1.0 - fabs(2.0*l - 1.0)) * s;
-    float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0));
-    float m = l - c/2.0;
-
-    float r = 0.0;
-    float g = 0.0;
-    float b = 0.0;
-
-    if (h >= 0.0 && h < 1.0) {
-        r = c;
-        g = x;
-        b = 0.0;
-    } else if (h >= 1.0 && h < 2.0) {
-        r = x;
-        g = c;
-        b = 0.0;
-    } else if (h >= 2.0 && h < 3.0) {
-        r = 0.0;
-        g = c;
-        b = x;
-    } else if (h >= 3.0 && h < 4.0) {
-        r = 0.0;
-        g = x;
-        b = c;
-    } else if (h >= 4.0 && h < 5.0) {
-        r = x;
-        g = 0.0;
-        b = c;
-    } else {
-        r = c;
-        g = 0.0;
-        b = x;
-    }
-
-    float4 rgba;
-    rgba.x = (r + m);
-    rgba.y = (g + m);
-    rgba.z = (b + m);
-    rgba.w = a;
-    return rgba;
+  float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
+  float s = hsla.s;
+  float l = hsla.l;
+  float a = hsla.a;
+
+  float c = (1.0 - fabs(2.0 * l - 1.0)) * s;
+  float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0));
+  float m = l - c / 2.0;
+
+  float r = 0.0;
+  float g = 0.0;
+  float b = 0.0;
+
+  if (h >= 0.0 && h < 1.0) {
+    r = c;
+    g = x;
+    b = 0.0;
+  } else if (h >= 1.0 && h < 2.0) {
+    r = x;
+    g = c;
+    b = 0.0;
+  } else if (h >= 2.0 && h < 3.0) {
+    r = 0.0;
+    g = c;
+    b = x;
+  } else if (h >= 3.0 && h < 4.0) {
+    r = 0.0;
+    g = x;
+    b = c;
+  } else if (h >= 4.0 && h < 5.0) {
+    r = x;
+    g = 0.0;
+    b = c;
+  } else {
+    r = c;
+    g = 0.0;
+    b = x;
+  }
+
+  float4 rgba;
+  rgba.x = (r + m);
+  rgba.y = (g + m);
+  rgba.z = (b + m);
+  rgba.w = a;
+  return rgba;
 }
 
 float4 to_device_position(float2 pixel_position, float2 viewport_size) {
-    return float4(pixel_position / viewport_size * float2(2., -2.) + float2(-1., 1.), 0., 1.);
+  return float4(pixel_position / viewport_size * float2(2., -2.) +
+                    float2(-1., 1.),
+                0., 1.);
 }

crates/gpui3/src/scene.rs 🔗

@@ -1,7 +1,7 @@
 use std::mem;
 
 use super::{Bounds, Hsla, Pixels, Point};
-use crate::{Corners, Edges};
+use crate::{Corners, Edges, FontId, GlyphId};
 use bytemuck::{Pod, Zeroable};
 use collections::BTreeMap;
 
@@ -17,6 +17,7 @@ pub struct Scene {
 #[derive(Default, Debug)]
 pub struct SceneLayer {
     pub quads: Vec<Quad>,
+    pub symbol: Vec<Symbol>,
 }
 
 impl Scene {
@@ -40,6 +41,7 @@ impl Scene {
         let layer = self.layers.entry(primitive.order()).or_default();
         match primitive {
             Primitive::Quad(quad) => layer.quads.push(quad),
+            Primitive::MonochromeGlyph(glyph) => layer.symbol.push(glyph),
         }
     }
 
@@ -51,20 +53,14 @@ impl Scene {
 #[derive(Clone, Debug)]
 pub enum Primitive {
     Quad(Quad),
+    MonochromeGlyph(Symbol),
 }
 
 impl Primitive {
     pub fn order(&self) -> u32 {
         match self {
             Primitive::Quad(quad) => quad.order,
-        }
-    }
-
-    pub fn is_transparent(&self) -> bool {
-        match self {
-            Primitive::Quad(quad) => {
-                quad.background.is_transparent() && quad.border_color.is_transparent()
-            }
+            Primitive::MonochromeGlyph(glyph) => glyph.order,
         }
     }
 
@@ -73,6 +69,9 @@ impl Primitive {
             Primitive::Quad(quad) => {
                 quad.scale(factor);
             }
+            Primitive::MonochromeGlyph(glyph) => {
+                glyph.scale(factor);
+            }
         }
     }
 }
@@ -119,3 +118,27 @@ impl From<Quad> for Primitive {
         Primitive::Quad(quad)
     }
 }
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+pub struct Symbol {
+    pub order: u32,
+    pub origin: Point<Pixels>,
+    pub font_id: FontId,
+    pub font_size: Pixels,
+    pub id: GlyphId,
+    pub color: Hsla,
+}
+
+impl Symbol {
+    pub fn scale(&mut self, factor: f32) {
+        self.font_size *= factor;
+        self.origin *= factor;
+    }
+}
+
+impl From<Symbol> for Primitive {
+    fn from(glyph: Symbol) -> Self {
+        Primitive::MonochromeGlyph(glyph)
+    }
+}

crates/gpui3/src/text_system.rs 🔗

@@ -1,9 +1,12 @@
 mod font_features;
+mod line;
 mod line_wrapper;
 mod text_layout_cache;
 
 use anyhow::anyhow;
+use bytemuck::{Pod, Zeroable};
 pub use font_features::*;
+pub use line::*;
 use line_wrapper::*;
 pub use text_layout_cache::*;
 
@@ -20,7 +23,8 @@ use std::{
     sync::Arc,
 };
 
-#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
+#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Zeroable, Pod)]
+#[repr(C)]
 pub struct FontId(pub usize);
 
 #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
@@ -51,7 +55,6 @@ impl TextSystem {
 
     pub fn font_id(&self, font: &Font) -> Result<FontId> {
         let font_id = self.font_ids_by_font.read().get(font).copied();
-
         if let Some(font_id) = font_id {
             Ok(font_id)
         } else {
@@ -160,29 +163,18 @@ impl TextSystem {
     ) -> Result<Line> {
         let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default();
 
-        dbg!("got font runs from pool");
         let mut last_font: Option<&Font> = None;
         for (len, style) in runs {
-            dbg!(len);
             if let Some(last_font) = last_font.as_ref() {
-                dbg!("a");
                 if **last_font == style.font {
-                    dbg!("b");
                     font_runs.last_mut().unwrap().0 += len;
-                    dbg!("c");
                     continue;
                 }
-                dbg!("d");
             }
-            dbg!("e");
             last_font = Some(&style.font);
-            dbg!("f");
             font_runs.push((*len, self.font_id(&style.font)?));
-            dbg!("g");
         }
 
-        dbg!("built font runs");
-
         let layout = self
             .text_layout_cache
             .layout_line(text, font_size, &font_runs);
@@ -332,7 +324,8 @@ pub struct RunStyle {
     pub underline: Option<UnderlineStyle>,
 }
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Zeroable, Pod)]
+#[repr(C)]
 pub struct GlyphId(u32);
 
 impl From<GlyphId> for u32 {

crates/gpui3/src/text_system/text_layout_cache.rs 🔗

@@ -1,8 +1,4 @@
-use crate::{
-    black, point, px, Bounds, FontId, Glyph, Hsla, LineLayout, Pixels, PlatformTextSystem, Point,
-    Run, RunStyle, UnderlineStyle, WindowContext,
-};
-use anyhow::Result;
+use crate::{FontId, Glyph, LineLayout, Pixels, PlatformTextSystem, Run};
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 use smallvec::SmallVec;
 use std::{
@@ -40,7 +36,6 @@ impl TextLayoutCache {
         font_size: Pixels,
         runs: &[(usize, FontId)],
     ) -> Arc<LineLayout> {
-        dbg!("layout line");
         let key = &CacheKeyRef {
             text,
             font_size,
@@ -145,332 +140,12 @@ impl<'a> Hash for CacheKeyRef<'a> {
     }
 }
 
-#[derive(Default, Debug, Clone)]
-pub struct Line {
-    layout: Arc<LineLayout>,
-    style_runs: SmallVec<[StyleRun; 32]>,
-}
-
-#[derive(Debug, Clone)]
-struct StyleRun {
-    len: u32,
-    color: Hsla,
-    underline: UnderlineStyle,
-}
-
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct ShapedBoundary {
     pub run_ix: usize,
     pub glyph_ix: usize,
 }
 
-impl Line {
-    pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
-        let mut style_runs = SmallVec::new();
-        for (len, style) in runs {
-            style_runs.push(StyleRun {
-                len: *len as u32,
-                color: style.color,
-                underline: style.underline.clone().unwrap_or_default(),
-            });
-        }
-        Self { layout, style_runs }
-    }
-
-    pub fn runs(&self) -> &[Run] {
-        &self.layout.runs
-    }
-
-    pub fn width(&self) -> Pixels {
-        self.layout.width
-    }
-
-    pub fn font_size(&self) -> Pixels {
-        self.layout.font_size
-    }
-
-    pub fn x_for_index(&self, index: usize) -> Pixels {
-        for run in &self.layout.runs {
-            for glyph in &run.glyphs {
-                if glyph.index >= index {
-                    return glyph.position.x;
-                }
-            }
-        }
-        self.layout.width
-    }
-
-    pub fn font_for_index(&self, index: usize) -> Option<FontId> {
-        for run in &self.layout.runs {
-            for glyph in &run.glyphs {
-                if glyph.index >= index {
-                    return Some(run.font_id);
-                }
-            }
-        }
-
-        None
-    }
-
-    pub fn len(&self) -> usize {
-        self.layout.len
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.layout.len == 0
-    }
-
-    pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
-        if x >= self.layout.width {
-            None
-        } else {
-            for run in self.layout.runs.iter().rev() {
-                for glyph in run.glyphs.iter().rev() {
-                    if glyph.position.x <= x {
-                        return Some(glyph.index);
-                    }
-                }
-            }
-            Some(0)
-        }
-    }
-
-    // todo!
-    pub fn paint(
-        &self,
-        origin: Point<Pixels>,
-        visible_bounds: Bounds<Pixels>,
-        line_height: Pixels,
-        cx: &mut WindowContext,
-    ) -> Result<()> {
-        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
-        let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
-
-        let mut style_runs = self.style_runs.iter();
-        let mut run_end = 0;
-        let mut color = black();
-        let mut underline = None;
-
-        for run in &self.layout.runs {
-            cx.text_system().with_font(run.font_id, |system, font| {
-                let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.width;
-
-                for glyph in &run.glyphs {
-                    let glyph_origin = origin + baseline_offset + glyph.position;
-                    if glyph_origin.x > visible_bounds.upper_right().x {
-                        break;
-                    }
-
-                    let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
-                    if glyph.index >= run_end {
-                        if let Some(style_run) = style_runs.next() {
-                            if let Some((_, underline_style)) = &mut underline {
-                                if style_run.underline != *underline_style {
-                                    finished_underline = underline.take();
-                                }
-                            }
-                            if style_run.underline.thickness > px(0.) {
-                                underline.get_or_insert((
-                                    point(
-                                        glyph_origin.x,
-                                        origin.y
-                                            + baseline_offset.y
-                                            + (self.layout.descent * 0.618),
-                                    ),
-                                    UnderlineStyle {
-                                        color: style_run.underline.color,
-                                        thickness: style_run.underline.thickness,
-                                        squiggly: style_run.underline.squiggly,
-                                    },
-                                ));
-                            }
-
-                            run_end += style_run.len as usize;
-                            color = style_run.color;
-                        } else {
-                            run_end = self.layout.len;
-                            finished_underline = underline.take();
-                        }
-                    }
-
-                    if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
-                        continue;
-                    }
-
-                    if let Some((_underline_origin, _underline_style)) = finished_underline {
-                        // cx.scene().insert(Underline {
-                        //     origin: underline_origin,
-                        //     width: glyph_origin.x - underline_origin.x,
-                        //     thickness: underline_style.thickness.into(),
-                        //     color: underline_style.color.unwrap(),
-                        //     squiggly: underline_style.squiggly,
-                        // });
-                    }
-
-                    // if glyph.is_emoji {
-                    //     cx.scene().push_image_glyph(scene::ImageGlyph {
-                    //         font_id: run.font_id,
-                    //         font_size: self.layout.font_size,
-                    //         id: glyph.id,
-                    //         origin: glyph_origin,
-                    //     });
-                    // } else {
-                    //     cx.scene().push_glyph(scene::Glyph {
-                    //         font_id: run.font_id,
-                    //         font_size: self.layout.font_size,
-                    //         id: glyph.id,
-                    //         origin: glyph_origin,
-                    //         color,
-                    //     });
-                    // }
-                }
-
-                anyhow::Ok(())
-            })??;
-        }
-
-        if let Some((_underline_start, _underline_style)) = underline.take() {
-            let _line_end_x = origin.x + self.layout.width;
-            // cx.scene().push_underline(Underline {
-            //     origin: underline_start,
-            //     width: line_end_x - underline_start.x,
-            //     color: underline_style.color,
-            //     thickness: underline_style.thickness.into(),
-            //     squiggly: underline_style.squiggly,
-            // });
-        }
-
-        Ok(())
-    }
-
-    pub fn paint_wrapped(
-        &self,
-        origin: Point<Pixels>,
-        _visible_bounds: Bounds<Pixels>,
-        line_height: Pixels,
-        boundaries: &[ShapedBoundary],
-        cx: &mut WindowContext,
-    ) -> Result<()> {
-        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
-        let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
-
-        let mut boundaries = boundaries.into_iter().peekable();
-        let mut color_runs = self.style_runs.iter();
-        let mut style_run_end = 0;
-        let mut _color = black(); // todo!
-        let mut underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
-
-        let mut glyph_origin = origin;
-        let mut prev_position = px(0.);
-        for (run_ix, run) in self.layout.runs.iter().enumerate() {
-            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
-                glyph_origin.x += glyph.position.x - prev_position;
-
-                if boundaries
-                    .peek()
-                    .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
-                {
-                    boundaries.next();
-                    if let Some((_underline_origin, _underline_style)) = underline.take() {
-                        // cx.scene().push_underline(Underline {
-                        //     origin: underline_origin,
-                        //     width: glyph_origin.x - underline_origin.x,
-                        //     thickness: underline_style.thickness.into(),
-                        //     color: underline_style.color.unwrap(),
-                        //     squiggly: underline_style.squiggly,
-                        // });
-                    }
-
-                    glyph_origin = point(origin.x, glyph_origin.y + line_height);
-                }
-                prev_position = glyph.position.x;
-
-                let mut finished_underline = None;
-                if glyph.index >= style_run_end {
-                    if let Some(style_run) = color_runs.next() {
-                        style_run_end += style_run.len as usize;
-                        _color = style_run.color;
-                        if let Some((_, underline_style)) = &mut underline {
-                            if style_run.underline != *underline_style {
-                                finished_underline = underline.take();
-                            }
-                        }
-                        if style_run.underline.thickness > px(0.) {
-                            underline.get_or_insert((
-                                glyph_origin
-                                    + point(
-                                        px(0.),
-                                        baseline_offset.y + (self.layout.descent * 0.618),
-                                    ),
-                                UnderlineStyle {
-                                    color: Some(
-                                        style_run.underline.color.unwrap_or(style_run.color),
-                                    ),
-                                    thickness: style_run.underline.thickness,
-                                    squiggly: style_run.underline.squiggly,
-                                },
-                            ));
-                        }
-                    } else {
-                        style_run_end = self.layout.len;
-                        _color = black();
-                        finished_underline = underline.take();
-                    }
-                }
-
-                if let Some((_underline_origin, _underline_style)) = finished_underline {
-                    // cx.scene().push_underline(Underline {
-                    //     origin: underline_origin,
-                    //     width: glyph_origin.x - underline_origin.x,
-                    //     thickness: underline_style.thickness.into(),
-                    //     color: underline_style.color.unwrap(),
-                    //     squiggly: underline_style.squiggly,
-                    // });
-                }
-
-                cx.text_system().with_font(run.font_id, |system, font| {
-                    let _glyph_bounds = Bounds {
-                        origin: glyph_origin,
-                        size: system.bounding_box(font, self.layout.font_size)?.size,
-                    };
-                    // if glyph_bounds.intersects(visible_bounds) {
-                    //     if glyph.is_emoji {
-                    //         cx.scene().push_image_glyph(scene::ImageGlyph {
-                    //             font_id: run.font_id,
-                    //             font_size: self.layout.font_size,
-                    //             id: glyph.id,
-                    //             origin: glyph_bounds.origin() + baseline_offset,
-                    //         });
-                    //     } else {
-                    //         cx.scene().push_glyph(scene::Glyph {
-                    //             font_id: run.font_id,
-                    //             font_size: self.layout.font_size,
-                    //             id: glyph.id,
-                    //             origin: glyph_bounds.origin() + baseline_offset,
-                    //             color,
-                    //         });
-                    //     }
-                    // }
-                    anyhow::Ok(())
-                })??;
-            }
-        }
-
-        if let Some((_underline_origin, _underline_style)) = underline.take() {
-            // let line_end_x = glyph_origin.x + self.layout.width - prev_position;
-            // cx.scene().push_underline(Underline {
-            //     origin: underline_origin,
-            //     width: line_end_x - underline_origin.x,
-            //     thickness: underline_style.thickness.into(),
-            //     color: underline_style.color,
-            //     squiggly: underline_style.squiggly,
-            // });
-        }
-
-        Ok(())
-    }
-}
-
 impl Run {
     pub fn glyphs(&self) -> &[Glyph] {
         &self.glyphs

crates/gpui3/src/window.rs 🔗

@@ -5,6 +5,7 @@ use crate::{
 };
 use anyhow::Result;
 use futures::Future;
+use smallvec::SmallVec;
 use std::{any::TypeId, marker::PhantomData, mem, sync::Arc};
 use util::ResultExt;
 
@@ -18,6 +19,7 @@ pub struct Window {
     layout_engine: TaffyLayoutEngine,
     pub(crate) root_view: Option<AnyView<()>>,
     mouse_position: Point<Pixels>,
+    z_index_stack: SmallVec<[u32; 8]>,
     pub(crate) scene: Scene,
     pub(crate) dirty: bool,
 }
@@ -56,6 +58,7 @@ impl Window {
             layout_engine: TaffyLayoutEngine::new(),
             root_view: None,
             mouse_position,
+            z_index_stack: SmallVec::new(),
             scene: Scene::new(scale_factor),
             dirty: true,
         }
@@ -126,6 +129,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         &mut self.window.scene
     }
 
+    pub fn with_z_index<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
+        self.window.z_index_stack.push(z_index);
+        let result = f(self);
+        self.window.z_index_stack.pop();
+        result
+    }
+
     pub fn run_on_main<R>(
         &self,
         f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,