Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/Cargo.toml                         |   3 
crates/gpui3/src/elements/div.rs                |  13 
crates/gpui3/src/elements/img.rs                |   4 
crates/gpui3/src/geometry.rs                    |  46 
crates/gpui3/src/platform/mac/metal_renderer.rs |  71 
crates/gpui3/src/scene.rs                       | 286 ++++-
crates/gpui3/src/style.rs                       |  64 
crates/gpui3/src/style_helpers.rs               |   2 
crates/gpui3/src/window.rs                      |  42 
crates/storybook2/src/collab_panel.rs           |   4 
crates/storybook2/src/themes.rs                 |   4 
crates/storybook2/src/themes/rose_pine.rs       | 843 ++++++++++++++++++
crates/storybook2/src/workspace.rs              |   2 
13 files changed, 1,190 insertions(+), 194 deletions(-)

Detailed changes

crates/gpui3/Cargo.toml πŸ”—

@@ -7,7 +7,7 @@ description = "The next version of Zed's GPU-accelerated UI framework"
 publish = false
 
 [features]
-test = ["backtrace", "dhat", "env_logger", "collections/test-support"]
+test = ["backtrace", "dhat", "env_logger", "collections/test-support", "util/test-support"]
 
 [lib]
 path = "src/gpui3.rs"
@@ -66,6 +66,7 @@ dhat = "0.3"
 env_logger.workspace = true
 png = "0.16"
 simplelog = "0.9"
+util = { path = "../util", features = ["test-support"] }
 
 [build-dependencies]
 bindgen = "0.65.1"

crates/gpui3/src/elements/div.rs πŸ”—

@@ -48,17 +48,14 @@ impl<S: 'static + Send + Sync> Element for Div<S> {
         let Layout { order, bounds } = layout;
 
         let style = self.computed_style();
-        style.paint(order, bounds, cx);
+        cx.stack(0, |cx| style.paint(order, bounds, cx));
 
-        // // todo!("support only one dimension being hidden")
         let overflow = &style.overflow;
-        // if style.overflow.y != Overflow::Visible || style.overflow.x != Overflow::Visible {
-        //     cx.clip(layout.bounds, style.corner_radii, || )
-        // }
-
         style.apply_text_style(cx, |cx| {
-            style.apply_overflow(layout.bounds, cx, |cx| {
-                self.paint_children(overflow, state, cx)
+            cx.stack(1, |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);

crates/gpui3/src/elements/img.rs πŸ”—

@@ -73,7 +73,9 @@ impl<S: Send + Sync + 'static> Element for Img<S> {
                 .and_then(ResultExt::log_err)
             {
                 let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
-                cx.paint_image(bounds, corner_radii, order, data, self.grayscale)?;
+                cx.stack(1, |cx| {
+                    cx.paint_image(bounds, corner_radii, order, data, self.grayscale)
+                })?;
             } else {
                 cx.spawn(|_, mut cx| async move {
                     if image_future.await.log_err().is_some() {

crates/gpui3/src/geometry.rs πŸ”—

@@ -2,7 +2,7 @@ use core::fmt::Debug;
 use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign};
 use refineable::Refineable;
 use std::{
-    cmp,
+    cmp, fmt,
     ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign},
 };
 
@@ -128,7 +128,7 @@ impl<T: Clone + Debug> Clone for Point<T> {
     }
 }
 
-#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div, Hash)]
+#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash)]
 #[refineable(debug)]
 #[repr(C)]
 pub struct Size<T: Clone + Debug> {
@@ -199,14 +199,11 @@ impl<T: Clone + Debug + Mul<S, Output = T>, S: Clone> MulAssign<S> for Size<T> {
 
 impl<T: Eq + Debug + Clone> Eq for Size<T> {}
 
-// impl From<Size<Option<Pixels>>> for Size<Option<f32>> {
-//     fn from(size: Size<Option<Pixels>>) -> Self {
-//         Size {
-//             width: size.width.map(|p| p.0 as f32),
-//             height: size.height.map(|p| p.0 as f32),
-//         }
-//     }
-// }
+impl<T: Clone + Debug> Debug for Size<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Size {{ {:?} Γ— {:?} }}", self.width, self.height)
+    }
+}
 
 impl From<Size<Pixels>> for Size<GlobalPixels> {
     fn from(size: Size<Pixels>) -> Self {
@@ -345,6 +342,13 @@ impl<T: Clone + Debug + Add<T, Output = T>> Bounds<T> {
             y: self.origin.y.clone() + self.size.height.clone(),
         }
     }
+
+    pub fn lower_left(&self) -> Point<T> {
+        Point {
+            x: self.origin.x.clone(),
+            y: self.origin.y.clone() + self.size.height.clone(),
+        }
+    }
 }
 
 impl<T: Clone + Debug + PartialOrd + Add<T, Output = T>> Bounds<T> {
@@ -627,7 +631,7 @@ impl From<f32> for Pixels {
 }
 
 impl Debug for Pixels {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{} px", self.0)
     }
 }
@@ -662,8 +666,8 @@ impl DevicePixels {
     }
 }
 
-impl std::fmt::Debug for DevicePixels {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Debug for DevicePixels {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{} px (device)", self.0)
     }
 }
@@ -721,7 +725,7 @@ impl ScaledPixels {
 impl Eq for ScaledPixels {}
 
 impl Debug for ScaledPixels {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{} px (scaled)", self.0)
     }
 }
@@ -738,12 +742,18 @@ impl From<DevicePixels> for ScaledPixels {
     }
 }
 
+impl From<ScaledPixels> for f64 {
+    fn from(scaled_pixels: ScaledPixels) -> Self {
+        scaled_pixels.0 as f64
+    }
+}
+
 #[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
 #[repr(transparent)]
 pub struct GlobalPixels(pub(crate) f32);
 
 impl Debug for GlobalPixels {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{} px (global coordinate space)", self.0)
     }
 }
@@ -772,7 +782,7 @@ impl Mul<Pixels> for Rems {
 }
 
 impl Debug for Rems {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{} rem", self.0)
     }
 }
@@ -840,7 +850,7 @@ impl DefiniteLength {
 }
 
 impl Debug for DefiniteLength {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             DefiniteLength::Absolute(length) => Debug::fmt(length, f),
             DefiniteLength::Fraction(fract) => write!(f, "{}%", (fract * 100.0) as i32),
@@ -880,7 +890,7 @@ pub enum Length {
 }
 
 impl Debug for Length {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Length::Definite(definite_length) => write!(f, "{:?}", definite_length),
             Length::Auto => write!(f, "auto"),

crates/gpui3/src/platform/mac/metal_renderer.rs πŸ”—

@@ -182,49 +182,42 @@ impl MetalRenderer {
         });
 
         let mut instance_offset = 0;
-        for layer in scene.layers() {
-            for batch in layer.batches() {
-                match batch {
-                    crate::PrimitiveBatch::Quads(quads) => {
-                        self.draw_quads(
-                            quads,
-                            &mut instance_offset,
-                            viewport_size,
-                            command_encoder,
-                        );
-                    }
-                    crate::PrimitiveBatch::Shadows(shadows) => {
-                        self.draw_shadows(
-                            shadows,
-                            &mut instance_offset,
-                            viewport_size,
-                            command_encoder,
-                        );
-                    }
-                    crate::PrimitiveBatch::MonochromeSprites {
+        for batch in scene.batches() {
+            match batch {
+                crate::PrimitiveBatch::Quads(quads) => {
+                    self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder);
+                }
+                crate::PrimitiveBatch::Shadows(shadows) => {
+                    self.draw_shadows(
+                        shadows,
+                        &mut instance_offset,
+                        viewport_size,
+                        command_encoder,
+                    );
+                }
+                crate::PrimitiveBatch::MonochromeSprites {
+                    texture_id,
+                    sprites,
+                } => {
+                    self.draw_monochrome_sprites(
                         texture_id,
                         sprites,
-                    } => {
-                        self.draw_monochrome_sprites(
-                            texture_id,
-                            sprites,
-                            &mut instance_offset,
-                            viewport_size,
-                            command_encoder,
-                        );
-                    }
-                    crate::PrimitiveBatch::PolychromeSprites {
+                        &mut instance_offset,
+                        viewport_size,
+                        command_encoder,
+                    );
+                }
+                crate::PrimitiveBatch::PolychromeSprites {
+                    texture_id,
+                    sprites,
+                } => {
+                    self.draw_polychrome_sprites(
                         texture_id,
                         sprites,
-                    } => {
-                        self.draw_polychrome_sprites(
-                            texture_id,
-                            sprites,
-                            &mut instance_offset,
-                            viewport_size,
-                            command_encoder,
-                        );
-                    }
+                        &mut instance_offset,
+                        viewport_size,
+                        command_encoder,
+                    );
                 }
             }
         }

crates/gpui3/src/scene.rs πŸ”—

@@ -1,18 +1,26 @@
-use std::{iter::Peekable, mem, slice};
-
-use super::{Bounds, Hsla, Point};
-use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledContentMask, ScaledPixels};
+use crate::{
+    AtlasTextureId, AtlasTile, Bounds, Corners, Edges, Hsla, Point, ScaledContentMask, ScaledPixels,
+};
 use collections::BTreeMap;
+use etagere::euclid::{Point3D, Vector3D};
+use plane_split::{BspSplitter, Polygon as BspPolygon};
 use smallvec::SmallVec;
+use std::{iter::Peekable, mem, slice};
 
 // Exported to metal
 pub type PointF = Point<f32>;
-pub type LayerId = SmallVec<[u32; 16]>;
+pub type StackingOrder = SmallVec<[u32; 16]>;
+pub type LayerId = u32;
+pub type DrawOrder = u32;
 
 #[derive(Debug)]
 pub struct Scene {
     pub(crate) scale_factor: f32,
-    pub(crate) layers: BTreeMap<LayerId, SceneLayer>,
+    pub(crate) layers: BTreeMap<StackingOrder, LayerId>,
+    pub quads: Vec<Quad>,
+    pub shadows: Vec<Shadow>,
+    pub monochrome_sprites: Vec<MonochromeSprite>,
+    pub polychrome_sprites: Vec<PolychromeSprite>,
 }
 
 impl Scene {
@@ -20,6 +28,10 @@ impl Scene {
         Scene {
             scale_factor,
             layers: BTreeMap::new(),
+            quads: Vec::new(),
+            shadows: Vec::new(),
+            monochrome_sprites: Vec::new(),
+            polychrome_sprites: Vec::new(),
         }
     }
 
@@ -27,47 +39,95 @@ impl Scene {
         Scene {
             scale_factor: self.scale_factor,
             layers: mem::take(&mut self.layers),
+            quads: mem::take(&mut self.quads),
+            shadows: mem::take(&mut self.shadows),
+            monochrome_sprites: mem::take(&mut self.monochrome_sprites),
+            polychrome_sprites: mem::take(&mut self.polychrome_sprites),
         }
     }
 
-    pub fn insert(&mut self, stacking_order: LayerId, primitive: impl Into<Primitive>) {
-        let layer = self.layers.entry(stacking_order).or_default();
-
+    pub fn insert(&mut self, layer_id: StackingOrder, primitive: impl Into<Primitive>) {
+        let next_id = self.layers.len() as LayerId;
+        let layer_id = *self.layers.entry(layer_id).or_insert(next_id);
         let primitive = primitive.into();
         match primitive {
-            Primitive::Quad(quad) => {
-                layer.quads.push(quad);
+            Primitive::Quad(mut quad) => {
+                quad.order = layer_id;
+                self.quads.push(quad);
             }
-            Primitive::Shadow(shadow) => {
-                layer.shadows.push(shadow);
+            Primitive::Shadow(mut shadow) => {
+                shadow.order = layer_id;
+                self.shadows.push(shadow);
             }
-            Primitive::MonochromeSprite(sprite) => {
-                layer.monochrome_sprites.push(sprite);
+            Primitive::MonochromeSprite(mut sprite) => {
+                sprite.order = layer_id;
+                self.monochrome_sprites.push(sprite);
             }
-            Primitive::PolychromeSprite(sprite) => {
-                layer.polychrome_sprites.push(sprite);
+            Primitive::PolychromeSprite(mut sprite) => {
+                sprite.order = layer_id;
+                self.polychrome_sprites.push(sprite);
             }
         }
     }
 
-    pub(crate) fn layers(&mut self) -> impl Iterator<Item = &mut SceneLayer> {
-        self.layers.values_mut()
-    }
-}
+    pub(crate) fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
+        // Map each layer id to a float between 0. and 1., with 1. closer to the viewer.
+        let mut layer_z_values = vec![0.; self.layers.len()];
+        for (ix, layer_id) in self.layers.values().enumerate() {
+            layer_z_values[*layer_id as usize] = ix as f32 / self.layers.len() as f32;
+        }
 
-#[derive(Debug, Default)]
-pub(crate) struct SceneLayer {
-    pub quads: Vec<Quad>,
-    pub shadows: Vec<Shadow>,
-    pub monochrome_sprites: Vec<MonochromeSprite>,
-    pub polychrome_sprites: Vec<PolychromeSprite>,
-}
+        // Add all primitives to the BSP splitter to determine draw order
+        let mut splitter = BspSplitter::new();
+        for (ix, quad) in self.quads.iter().enumerate() {
+            let z = layer_z_values[quad.order as LayerId as usize];
+            splitter.add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix)));
+        }
+
+        for (ix, shadow) in self.shadows.iter().enumerate() {
+            let z = layer_z_values[shadow.order as LayerId as usize];
+            splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix)));
+        }
+
+        for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() {
+            let z = layer_z_values[monochrome_sprite.order as LayerId as usize];
+            splitter.add(
+                monochrome_sprite
+                    .bounds
+                    .to_bsp_polygon(z, (PrimitiveKind::MonochromeSprite, ix)),
+            );
+        }
 
-impl SceneLayer {
-    pub fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
+        for (ix, polychrome_sprite) in self.polychrome_sprites.iter().enumerate() {
+            let z = layer_z_values[polychrome_sprite.order as LayerId as usize];
+            splitter.add(
+                polychrome_sprite
+                    .bounds
+                    .to_bsp_polygon(z, (PrimitiveKind::PolychromeSprite, ix)),
+            );
+        }
+
+        // Sort all polygons, then reassign the order field of each primitive to `draw_order`
+        // We need primitives to be repr(C), hence the weird reuse of the order field for two different types.
+        for (draw_order, polygon) in splitter.sort(Vector3D::new(0., 0., 1.)).iter().enumerate() {
+            match polygon.anchor {
+                (PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder,
+                (PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder,
+                (PrimitiveKind::MonochromeSprite, ix) => {
+                    self.monochrome_sprites[ix].order = draw_order as DrawOrder
+                }
+                (PrimitiveKind::PolychromeSprite, ix) => {
+                    self.polychrome_sprites[ix].order = draw_order as DrawOrder
+                }
+            }
+        }
+
+        // Sort the primitives
         self.quads.sort_unstable();
+        self.shadows.sort_unstable();
         self.monochrome_sprites.sort_unstable();
         self.polychrome_sprites.sort_unstable();
+
         BatchIterator {
             quads: &self.quads,
             quads_start: 0,
@@ -104,27 +164,27 @@ impl<'a> Iterator for BatchIterator<'a> {
     type Item = PrimitiveBatch<'a>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        let mut kinds_and_orders = [
-            (PrimitiveKind::Quad, self.quads_iter.peek().map(|q| q.order)),
+        let mut orders_and_kinds = [
+            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
             (
-                PrimitiveKind::Shadow,
                 self.shadows_iter.peek().map(|s| s.order),
+                PrimitiveKind::Shadow,
             ),
             (
-                PrimitiveKind::MonochromeSprite,
                 self.monochrome_sprites_iter.peek().map(|s| s.order),
+                PrimitiveKind::MonochromeSprite,
             ),
             (
-                PrimitiveKind::PolychromeSprite,
                 self.polychrome_sprites_iter.peek().map(|s| s.order),
+                PrimitiveKind::PolychromeSprite,
             ),
         ];
-        kinds_and_orders.sort_by_key(|(_, order)| order.unwrap_or(u32::MAX));
+        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
 
-        let first = kinds_and_orders[0];
-        let second = kinds_and_orders[1];
-        let (batch_kind, max_order) = if first.1.is_some() {
-            (first.0, second.1.unwrap_or(u32::MAX))
+        let first = orders_and_kinds[0];
+        let second = orders_and_kinds[1];
+        let (batch_kind, max_order) = if first.0.is_some() {
+            (first.1, second.0.unwrap_or(u32::MAX))
         } else {
             return None;
         };
@@ -132,23 +192,27 @@ impl<'a> Iterator for BatchIterator<'a> {
         match batch_kind {
             PrimitiveKind::Quad => {
                 let quads_start = self.quads_start;
-                let quads_end = quads_start
-                    + self
-                        .quads_iter
-                        .by_ref()
-                        .take_while(|quad| quad.order <= max_order)
-                        .count();
+                let mut quads_end = quads_start;
+                while self
+                    .quads_iter
+                    .next_if(|quad| quad.order <= max_order)
+                    .is_some()
+                {
+                    quads_end += 1;
+                }
                 self.quads_start = quads_end;
                 Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
             }
             PrimitiveKind::Shadow => {
                 let shadows_start = self.shadows_start;
-                let shadows_end = shadows_start
-                    + self
-                        .shadows_iter
-                        .by_ref()
-                        .take_while(|shadow| shadow.order <= max_order)
-                        .count();
+                let mut shadows_end = shadows_start;
+                while self
+                    .shadows_iter
+                    .next_if(|shadow| shadow.order <= max_order)
+                    .is_some()
+                {
+                    shadows_end += 1;
+                }
                 self.shadows_start = shadows_end;
                 Some(PrimitiveBatch::Shadows(
                     &self.shadows[shadows_start..shadows_end],
@@ -157,14 +221,16 @@ impl<'a> Iterator for BatchIterator<'a> {
             PrimitiveKind::MonochromeSprite => {
                 let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
                 let sprites_start = self.monochrome_sprites_start;
-                let sprites_end = sprites_start
-                    + self
-                        .monochrome_sprites_iter
-                        .by_ref()
-                        .take_while(|sprite| {
-                            sprite.order <= max_order && sprite.tile.texture_id == texture_id
-                        })
-                        .count();
+                let mut sprites_end = sprites_start;
+                while self
+                    .monochrome_sprites_iter
+                    .next_if(|sprite| {
+                        sprite.order <= max_order && sprite.tile.texture_id == texture_id
+                    })
+                    .is_some()
+                {
+                    sprites_end += 1;
+                }
                 self.monochrome_sprites_start = sprites_end;
                 Some(PrimitiveBatch::MonochromeSprites {
                     texture_id,
@@ -174,14 +240,16 @@ impl<'a> Iterator for BatchIterator<'a> {
             PrimitiveKind::PolychromeSprite => {
                 let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
                 let sprites_start = self.polychrome_sprites_start;
-                let sprites_end = sprites_start
-                    + self
-                        .polychrome_sprites_iter
-                        .by_ref()
-                        .take_while(|sprite| {
-                            sprite.order <= max_order && sprite.tile.texture_id == texture_id
-                        })
-                        .count();
+                let mut sprites_end = self.polychrome_sprites_start;
+                while self
+                    .polychrome_sprites_iter
+                    .next_if(|sprite| {
+                        sprite.order <= max_order && sprite.tile.texture_id == texture_id
+                    })
+                    .is_some()
+                {
+                    sprites_end += 1;
+                }
                 self.polychrome_sprites_start = sprites_end;
                 Some(PrimitiveBatch::PolychromeSprites {
                     texture_id,
@@ -192,10 +260,11 @@ impl<'a> Iterator for BatchIterator<'a> {
     }
 }
 
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
 pub enum PrimitiveKind {
-    Quad,
     Shadow,
+    #[default]
+    Quad,
     MonochromeSprite,
     PolychromeSprite,
 }
@@ -222,10 +291,10 @@ pub(crate) enum PrimitiveBatch<'a> {
     },
 }
 
-#[derive(Debug, Clone, Eq, PartialEq)]
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
 pub struct Quad {
-    pub order: u32,
+    pub order: u32, // Initially a LayerId, then a DrawOrder.
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ScaledContentMask,
     pub background: Hsla,
@@ -346,3 +415,76 @@ impl From<PolychromeSprite> for Primitive {
 
 #[derive(Copy, Clone, Debug)]
 pub struct AtlasId(pub(crate) usize);
+
+impl Bounds<ScaledPixels> {
+    fn to_bsp_polygon<A: Copy>(&self, z: f32, anchor: A) -> BspPolygon<A> {
+        let upper_left = self.origin;
+        let upper_right = self.upper_right();
+        let lower_right = self.lower_right();
+        let lower_left = self.lower_left();
+
+        BspPolygon::from_points(
+            [
+                Point3D::new(upper_left.x.into(), upper_left.y.into(), z as f64),
+                Point3D::new(upper_right.x.into(), upper_right.y.into(), z as f64),
+                Point3D::new(lower_right.x.into(), lower_right.y.into(), z as f64),
+                Point3D::new(lower_left.x.into(), lower_left.y.into(), z as f64),
+            ],
+            anchor,
+        )
+        .expect("Polygon should not be empty")
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{point, size};
+
+    use super::*;
+    use smallvec::smallvec;
+
+    #[test]
+    fn test_scene() {
+        let mut scene = Scene::new(1.0);
+        assert_eq!(scene.layers.len(), 0);
+
+        scene.insert(smallvec![1], quad());
+        scene.insert(smallvec![2], shadow());
+        scene.insert(smallvec![3], quad());
+
+        let mut batches_count = 0;
+        for _ in scene.batches() {
+            batches_count += 1;
+        }
+        assert_eq!(batches_count, 3);
+    }
+
+    fn quad() -> Quad {
+        Quad {
+            order: 0,
+            bounds: Bounds {
+                origin: point(ScaledPixels(0.), ScaledPixels(0.)),
+                size: size(ScaledPixels(100.), ScaledPixels(100.)),
+            },
+            content_mask: Default::default(),
+            background: Default::default(),
+            border_color: Default::default(),
+            corner_radii: Default::default(),
+            border_widths: Default::default(),
+        }
+    }
+
+    fn shadow() -> Shadow {
+        Shadow {
+            order: Default::default(),
+            bounds: Bounds {
+                origin: point(ScaledPixels(0.), ScaledPixels(0.)),
+                size: size(ScaledPixels(100.), ScaledPixels(100.)),
+            },
+            corner_radii: Default::default(),
+            content_mask: Default::default(),
+            color: Default::default(),
+            blur_radius: Default::default(),
+        }
+    }
+}

crates/gpui3/src/style.rs πŸ”—

@@ -246,46 +246,50 @@ impl Style {
         let scale = cx.scale_factor();
 
         for shadow in &self.box_shadow {
-            let layer_id = cx.current_layer_id();
             let content_mask = cx.content_mask();
             let mut shadow_bounds = bounds;
             shadow_bounds.origin += shadow.offset;
             shadow_bounds.dilate(shadow.spread_radius);
-            cx.scene().insert(
-                layer_id,
-                Shadow {
-                    order,
-                    bounds: shadow_bounds.scale(scale),
-                    content_mask: content_mask.scale(scale),
-                    corner_radii: self
-                        .corner_radii
-                        .to_pixels(shadow_bounds.size, rem_size)
-                        .scale(scale),
-                    color: shadow.color,
-                    blur_radius: shadow.blur_radius.scale(scale),
-                },
-            );
+            cx.stack(0, |cx| {
+                let layer_id = cx.current_stacking_order();
+                cx.scene().insert(
+                    layer_id,
+                    Shadow {
+                        order: 0,
+                        bounds: shadow_bounds.scale(scale),
+                        content_mask: content_mask.scale(scale),
+                        corner_radii: self
+                            .corner_radii
+                            .to_pixels(shadow_bounds.size, rem_size)
+                            .scale(scale),
+                        color: shadow.color,
+                        blur_radius: shadow.blur_radius.scale(scale),
+                    },
+                );
+            })
         }
 
         let background_color = self.fill.as_ref().and_then(Fill::color);
         if background_color.is_some() || self.is_border_visible() {
-            let layer_id = cx.current_layer_id();
             let content_mask = cx.content_mask();
-            cx.scene().insert(
-                layer_id,
-                Quad {
+            cx.stack(1, |cx| {
+                let order = cx.current_stacking_order();
+                cx.scene().insert(
                     order,
-                    bounds: bounds.scale(scale),
-                    content_mask: content_mask.scale(scale),
-                    background: background_color.unwrap_or_default(),
-                    border_color: self.border_color.unwrap_or_default(),
-                    corner_radii: self
-                        .corner_radii
-                        .to_pixels(bounds.size, rem_size)
-                        .scale(scale),
-                    border_widths: self.border_widths.to_pixels(rem_size).scale(scale),
-                },
-            );
+                    Quad {
+                        order: 0,
+                        bounds: bounds.scale(scale),
+                        content_mask: content_mask.scale(scale),
+                        background: background_color.unwrap_or_default(),
+                        border_color: self.border_color.unwrap_or_default(),
+                        corner_radii: self
+                            .corner_radii
+                            .to_pixels(bounds.size, rem_size)
+                            .scale(scale),
+                        border_widths: self.border_widths.to_pixels(rem_size).scale(scale),
+                    },
+                );
+            });
         }
     }
 

crates/gpui3/src/style_helpers.rs πŸ”—

@@ -263,7 +263,7 @@ pub trait StyleHelpers: Styled<Style = Style> {
     {
         self.declared_style().box_shadow = Some(smallvec![
             BoxShadow {
-                color: hsla(0., 0., 0., 0.1),
+                color: hsla(0.5, 0., 0., 1.0),
                 offset: point(px(0.), px(4.)),
                 blur_radius: px(6.),
                 spread_radius: px(-1.),

crates/gpui3/src/window.rs πŸ”—

@@ -1,11 +1,10 @@
 use crate::{
     image_cache::RenderImageParams, px, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
     BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect, Element, EntityId,
-    FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread,
-    MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point,
-    PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene,
-    SharedString, Size, Style, TaffyLayoutEngine, Task, WeakHandle, WindowOptions,
-    SUBPIXEL_VARIANTS,
+    FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread, MainThreadOnly,
+    MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference,
+    RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, StackingOrder,
+    Style, TaffyLayoutEngine, Task, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use smallvec::SmallVec;
@@ -24,7 +23,7 @@ pub struct Window {
     layout_engine: TaffyLayoutEngine,
     pub(crate) root_view: Option<AnyView<()>>,
     mouse_position: Point<Pixels>,
-    current_layer_id: LayerId,
+    current_stacking_order: StackingOrder,
     content_mask_stack: Vec<ContentMask>,
     pub(crate) scene: Scene,
     pub(crate) dirty: bool,
@@ -73,7 +72,7 @@ impl Window {
             layout_engine: TaffyLayoutEngine::new(),
             root_view: None,
             mouse_position,
-            current_layer_id: SmallVec::new(),
+            current_stacking_order: SmallVec::new(),
             content_mask_stack: Vec::new(),
             scene: Scene::new(scale_factor),
             dirty: true,
@@ -99,7 +98,7 @@ impl ContentMask {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
 #[repr(C)]
 pub struct ScaledContentMask {
     bounds: Bounds<ScaledPixels>,
@@ -250,14 +249,14 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     }
 
     pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R {
-        self.window.current_layer_id.push(order);
+        self.window.current_stacking_order.push(order);
         let result = f(self);
-        self.window.current_layer_id.pop();
+        self.window.current_stacking_order.pop();
         result
     }
 
-    pub fn current_layer_id(&self) -> LayerId {
-        self.window.current_layer_id.clone()
+    pub fn current_stacking_order(&self) -> StackingOrder {
+        self.window.current_stacking_order.clone()
     }
 
     pub fn paint_glyph(
@@ -286,7 +285,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
         let raster_bounds = self.text_system().raster_bounds(&params)?;
         if !raster_bounds.is_zero() {
-            let layer_id = self.current_layer_id();
+            let layer_id = self.current_stacking_order();
             let tile =
                 self.window
                     .sprite_atlas
@@ -336,7 +335,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
         let raster_bounds = self.text_system().raster_bounds(&params)?;
         if !raster_bounds.is_zero() {
-            let layer_id = self.current_layer_id();
+            let layer_id = self.current_stacking_order();
             let tile =
                 self.window
                     .sprite_atlas
@@ -382,7 +381,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
         };
 
-        let layer_id = self.current_layer_id();
+        let layer_id = self.current_stacking_order();
         let tile =
             self.window
                 .sprite_atlas
@@ -418,7 +417,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         let bounds = bounds.scale(scale_factor);
         let params = RenderImageParams { image_id: data.id };
 
-        let layer_id = self.current_layer_id();
+        let order = self.current_stacking_order();
         let tile = self
             .window
             .sprite_atlas
@@ -429,9 +428,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         let corner_radii = corner_radii.scale(scale_factor);
 
         self.window.scene.insert(
-            layer_id,
+            order,
             PolychromeSprite {
-                order,
+                order: 0, // Used in in Scene::batches. 0 has no meaning.
                 bounds,
                 content_mask,
                 corner_radii,
@@ -620,6 +619,13 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
         self.entities.weak_handle(self.entity_id)
     }
 
+    pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R {
+        self.window.current_stacking_order.push(order);
+        let result = f(self);
+        self.window.current_stacking_order.pop();
+        result
+    }
+
     pub fn on_next_frame(&mut self, f: impl FnOnce(&mut S, &mut ViewContext<S>) + Send + 'static) {
         let entity = self.handle();
         self.window_cx.on_next_frame(move |cx| {

crates/storybook2/src/collab_panel.rs πŸ”—

@@ -92,7 +92,7 @@ impl CollabPanel {
                                         ),
                                     ]
                                 })
-                                .take(10)
+                                .take(5)
                                 .flatten(),
                             ),
                     ),
@@ -168,7 +168,7 @@ impl CollabPanel {
                             .uri(avatar_uri)
                             .size_3p5()
                             .rounded_full()
-                            // .fill(theme.middle.positive.default.foreground)
+                            .fill(theme.middle.positive.default.foreground)
                             .shadow_md(),
                     )
                     .child(label),

crates/storybook2/src/themes/rose_pine_dawn.rs β†’ crates/storybook2/src/themes/rose_pine.rs πŸ”—

@@ -1,7 +1,7 @@
 use crate::theme::Theme;
 use gpui3::serde_json::{self, json};
 
-pub fn rose_pine_dawn() -> Theme {
+pub fn rose_pine() -> Theme {
     serde_json::from_value(json! {
         {
           "name": "RosΓ© Pine",
@@ -843,3 +843,844 @@ pub fn rose_pine_dawn() -> Theme {
     })
     .unwrap()
 }
+
+pub fn rose_pine_dawn() -> Theme {
+    serde_json::from_value(json!({
+      "name": "RosΓ© Pine Dawn",
+      "is_light": true,
+      "ramps": {},
+      "lowest": {
+        "base": {
+          "default": {
+            "background": "#dcd8d8",
+            "border": "#dcd6d5",
+            "foreground": "#575279"
+          },
+          "hovered": {
+            "background": "#dcd6d5",
+            "border": "#dcd6d5",
+            "foreground": "#575279"
+          },
+          "pressed": {
+            "background": "#efe6df",
+            "border": "#dcd6d5",
+            "foreground": "#575279"
+          },
+          "active": {
+            "background": "#c1bac1",
+            "border": "#a9a3b0",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#dcd8d8",
+            "border": "#d0cccf",
+            "foreground": "#938fa3"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#c7c0c5"
+          }
+        },
+        "variant": {
+          "default": {
+            "background": "#dcd8d8",
+            "border": "#dcd6d5",
+            "foreground": "#706c8c"
+          },
+          "hovered": {
+            "background": "#dcd6d5",
+            "border": "#dcd6d5",
+            "foreground": "#706c8c"
+          },
+          "pressed": {
+            "background": "#efe6df",
+            "border": "#dcd6d5",
+            "foreground": "#706c8c"
+          },
+          "active": {
+            "background": "#c1bac1",
+            "border": "#a9a3b0",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#dcd8d8",
+            "border": "#d0cccf",
+            "foreground": "#938fa3"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#c7c0c5"
+          }
+        },
+        "on": {
+          "default": {
+            "background": "#fef9f2",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "hovered": {
+            "background": "#e5e0df",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "pressed": {
+            "background": "#d4d0d2",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "active": {
+            "background": "#dbd5d4",
+            "border": "#dbd3d1",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#fef9f2",
+            "border": "#f6f1eb",
+            "foreground": "#b1abb5"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#d6d1d1"
+          }
+        },
+        "accent": {
+          "default": {
+            "background": "#dde9eb",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "hovered": {
+            "background": "#c3d7db",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "pressed": {
+            "background": "#b6cfd3",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "active": {
+            "background": "#a3c3c9",
+            "border": "#8db6bd",
+            "foreground": "#06090a"
+          },
+          "disabled": {
+            "background": "#dde9eb",
+            "border": "#d0e0e3",
+            "foreground": "#72a5ae"
+          },
+          "inverted": {
+            "background": "#06090a",
+            "border": "#ffffff",
+            "foreground": "#a8c7cd"
+          }
+        },
+        "positive": {
+          "default": {
+            "background": "#dbeee7",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "hovered": {
+            "background": "#bee0d5",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "pressed": {
+            "background": "#b0dacb",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "active": {
+            "background": "#9bd0bf",
+            "border": "#82c6b1",
+            "foreground": "#060a09"
+          },
+          "disabled": {
+            "background": "#dbeee7",
+            "border": "#cde7de",
+            "foreground": "#63b89f"
+          },
+          "inverted": {
+            "background": "#060a09",
+            "border": "#ffffff",
+            "foreground": "#a1d4c3"
+          }
+        },
+        "warning": {
+          "default": {
+            "background": "#ffebd6",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "hovered": {
+            "background": "#ffdab7",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "pressed": {
+            "background": "#fed2a6",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "active": {
+            "background": "#fbc891",
+            "border": "#f7bc77",
+            "foreground": "#330704"
+          },
+          "disabled": {
+            "background": "#ffebd6",
+            "border": "#ffe2c7",
+            "foreground": "#f1ac57"
+          },
+          "inverted": {
+            "background": "#330704",
+            "border": "#ffffff",
+            "foreground": "#fccb97"
+          }
+        },
+        "negative": {
+          "default": {
+            "background": "#f1dfe3",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "hovered": {
+            "background": "#e6c6cd",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "pressed": {
+            "background": "#e0bac2",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "active": {
+            "background": "#d8a8b3",
+            "border": "#ce94a3",
+            "foreground": "#0b0708"
+          },
+          "disabled": {
+            "background": "#f1dfe3",
+            "border": "#ecd2d8",
+            "foreground": "#c17b8e"
+          },
+          "inverted": {
+            "background": "#0b0708",
+            "border": "#ffffff",
+            "foreground": "#dbadb8"
+          }
+        }
+      },
+      "middle": {
+        "base": {
+          "default": {
+            "background": "#fef9f2",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "hovered": {
+            "background": "#e5e0df",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "pressed": {
+            "background": "#d4d0d2",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "active": {
+            "background": "#dbd5d4",
+            "border": "#dbd3d1",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#fef9f2",
+            "border": "#f6f1eb",
+            "foreground": "#b1abb5"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#d6d1d1"
+          }
+        },
+        "variant": {
+          "default": {
+            "background": "#fef9f2",
+            "border": "#e5e0df",
+            "foreground": "#706c8c"
+          },
+          "hovered": {
+            "background": "#e5e0df",
+            "border": "#e5e0df",
+            "foreground": "#706c8c"
+          },
+          "pressed": {
+            "background": "#d4d0d2",
+            "border": "#e5e0df",
+            "foreground": "#706c8c"
+          },
+          "active": {
+            "background": "#dbd5d4",
+            "border": "#dbd3d1",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#fef9f2",
+            "border": "#f6f1eb",
+            "foreground": "#b1abb5"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#d6d1d1"
+          }
+        },
+        "on": {
+          "default": {
+            "background": "#faf4ed",
+            "border": "#fdf8f1",
+            "foreground": "#575279"
+          },
+          "hovered": {
+            "background": "#fdf8f1",
+            "border": "#fdf8f1",
+            "foreground": "#575279"
+          },
+          "pressed": {
+            "background": "#fdf8f2",
+            "border": "#fdf8f1",
+            "foreground": "#575279"
+          },
+          "active": {
+            "background": "#e6e1e0",
+            "border": "#d0cccf",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#faf4ed",
+            "border": "#fcf6ef",
+            "foreground": "#efe6df"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#ede9e5"
+          }
+        },
+        "accent": {
+          "default": {
+            "background": "#dde9eb",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "hovered": {
+            "background": "#c3d7db",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "pressed": {
+            "background": "#b6cfd3",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "active": {
+            "background": "#a3c3c9",
+            "border": "#8db6bd",
+            "foreground": "#06090a"
+          },
+          "disabled": {
+            "background": "#dde9eb",
+            "border": "#d0e0e3",
+            "foreground": "#72a5ae"
+          },
+          "inverted": {
+            "background": "#06090a",
+            "border": "#ffffff",
+            "foreground": "#a8c7cd"
+          }
+        },
+        "positive": {
+          "default": {
+            "background": "#dbeee7",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "hovered": {
+            "background": "#bee0d5",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "pressed": {
+            "background": "#b0dacb",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "active": {
+            "background": "#9bd0bf",
+            "border": "#82c6b1",
+            "foreground": "#060a09"
+          },
+          "disabled": {
+            "background": "#dbeee7",
+            "border": "#cde7de",
+            "foreground": "#63b89f"
+          },
+          "inverted": {
+            "background": "#060a09",
+            "border": "#ffffff",
+            "foreground": "#a1d4c3"
+          }
+        },
+        "warning": {
+          "default": {
+            "background": "#ffebd6",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "hovered": {
+            "background": "#ffdab7",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "pressed": {
+            "background": "#fed2a6",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "active": {
+            "background": "#fbc891",
+            "border": "#f7bc77",
+            "foreground": "#330704"
+          },
+          "disabled": {
+            "background": "#ffebd6",
+            "border": "#ffe2c7",
+            "foreground": "#f1ac57"
+          },
+          "inverted": {
+            "background": "#330704",
+            "border": "#ffffff",
+            "foreground": "#fccb97"
+          }
+        },
+        "negative": {
+          "default": {
+            "background": "#f1dfe3",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "hovered": {
+            "background": "#e6c6cd",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "pressed": {
+            "background": "#e0bac2",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "active": {
+            "background": "#d8a8b3",
+            "border": "#ce94a3",
+            "foreground": "#0b0708"
+          },
+          "disabled": {
+            "background": "#f1dfe3",
+            "border": "#ecd2d8",
+            "foreground": "#c17b8e"
+          },
+          "inverted": {
+            "background": "#0b0708",
+            "border": "#ffffff",
+            "foreground": "#dbadb8"
+          }
+        }
+      },
+      "highest": {
+        "base": {
+          "default": {
+            "background": "#faf4ed",
+            "border": "#fdf8f1",
+            "foreground": "#575279"
+          },
+          "hovered": {
+            "background": "#fdf8f1",
+            "border": "#fdf8f1",
+            "foreground": "#575279"
+          },
+          "pressed": {
+            "background": "#fdf8f2",
+            "border": "#fdf8f1",
+            "foreground": "#575279"
+          },
+          "active": {
+            "background": "#e6e1e0",
+            "border": "#d0cccf",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#faf4ed",
+            "border": "#fcf6ef",
+            "foreground": "#efe6df"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#ede9e5"
+          }
+        },
+        "variant": {
+          "default": {
+            "background": "#faf4ed",
+            "border": "#fdf8f1",
+            "foreground": "#706c8c"
+          },
+          "hovered": {
+            "background": "#fdf8f1",
+            "border": "#fdf8f1",
+            "foreground": "#706c8c"
+          },
+          "pressed": {
+            "background": "#fdf8f2",
+            "border": "#fdf8f1",
+            "foreground": "#706c8c"
+          },
+          "active": {
+            "background": "#e6e1e0",
+            "border": "#d0cccf",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#faf4ed",
+            "border": "#fcf6ef",
+            "foreground": "#efe6df"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#ede9e5"
+          }
+        },
+        "on": {
+          "default": {
+            "background": "#fef9f2",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "hovered": {
+            "background": "#e5e0df",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "pressed": {
+            "background": "#d4d0d2",
+            "border": "#e5e0df",
+            "foreground": "#575279"
+          },
+          "active": {
+            "background": "#dbd5d4",
+            "border": "#dbd3d1",
+            "foreground": "#575279"
+          },
+          "disabled": {
+            "background": "#fef9f2",
+            "border": "#f6f1eb",
+            "foreground": "#b1abb5"
+          },
+          "inverted": {
+            "background": "#575279",
+            "border": "#faf4ed",
+            "foreground": "#d6d1d1"
+          }
+        },
+        "accent": {
+          "default": {
+            "background": "#dde9eb",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "hovered": {
+            "background": "#c3d7db",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "pressed": {
+            "background": "#b6cfd3",
+            "border": "#c3d7db",
+            "foreground": "#57949f"
+          },
+          "active": {
+            "background": "#a3c3c9",
+            "border": "#8db6bd",
+            "foreground": "#06090a"
+          },
+          "disabled": {
+            "background": "#dde9eb",
+            "border": "#d0e0e3",
+            "foreground": "#72a5ae"
+          },
+          "inverted": {
+            "background": "#06090a",
+            "border": "#ffffff",
+            "foreground": "#a8c7cd"
+          }
+        },
+        "positive": {
+          "default": {
+            "background": "#dbeee7",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "hovered": {
+            "background": "#bee0d5",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "pressed": {
+            "background": "#b0dacb",
+            "border": "#bee0d5",
+            "foreground": "#3eaa8e"
+          },
+          "active": {
+            "background": "#9bd0bf",
+            "border": "#82c6b1",
+            "foreground": "#060a09"
+          },
+          "disabled": {
+            "background": "#dbeee7",
+            "border": "#cde7de",
+            "foreground": "#63b89f"
+          },
+          "inverted": {
+            "background": "#060a09",
+            "border": "#ffffff",
+            "foreground": "#a1d4c3"
+          }
+        },
+        "warning": {
+          "default": {
+            "background": "#ffebd6",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "hovered": {
+            "background": "#ffdab7",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "pressed": {
+            "background": "#fed2a6",
+            "border": "#ffdab7",
+            "foreground": "#e99d35"
+          },
+          "active": {
+            "background": "#fbc891",
+            "border": "#f7bc77",
+            "foreground": "#330704"
+          },
+          "disabled": {
+            "background": "#ffebd6",
+            "border": "#ffe2c7",
+            "foreground": "#f1ac57"
+          },
+          "inverted": {
+            "background": "#330704",
+            "border": "#ffffff",
+            "foreground": "#fccb97"
+          }
+        },
+        "negative": {
+          "default": {
+            "background": "#f1dfe3",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "hovered": {
+            "background": "#e6c6cd",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "pressed": {
+            "background": "#e0bac2",
+            "border": "#e6c6cd",
+            "foreground": "#b4647a"
+          },
+          "active": {
+            "background": "#d8a8b3",
+            "border": "#ce94a3",
+            "foreground": "#0b0708"
+          },
+          "disabled": {
+            "background": "#f1dfe3",
+            "border": "#ecd2d8",
+            "foreground": "#c17b8e"
+          },
+          "inverted": {
+            "background": "#0b0708",
+            "border": "#ffffff",
+            "foreground": "#dbadb8"
+          }
+        }
+      },
+      "popover_shadow": {
+        "blur": 4,
+        "color": "#2c2a4d33",
+        "offset": [
+          1,
+          2
+        ]
+      },
+      "modal_shadow": {
+        "blur": 16,
+        "color": "#2c2a4d33",
+        "offset": [
+          0,
+          2
+        ]
+      },
+      "players": {
+        "0": {
+          "selection": "#57949f3d",
+          "cursor": "#57949f"
+        },
+        "1": {
+          "selection": "#3eaa8e3d",
+          "cursor": "#3eaa8e"
+        },
+        "2": {
+          "selection": "#7c697f3d",
+          "cursor": "#7c697f"
+        },
+        "3": {
+          "selection": "#907aa93d",
+          "cursor": "#907aa9"
+        },
+        "4": {
+          "selection": "#907aa93d",
+          "cursor": "#907aa9"
+        },
+        "5": {
+          "selection": "#2a69833d",
+          "cursor": "#2a6983"
+        },
+        "6": {
+          "selection": "#b4647a3d",
+          "cursor": "#b4647a"
+        },
+        "7": {
+          "selection": "#e99d353d",
+          "cursor": "#e99d35"
+        }
+      },
+      "syntax": {
+        "comment": {
+          "color": "#9893a5"
+        },
+        "operator": {
+          "color": "#286983"
+        },
+        "punctuation": {
+          "color": "#797593"
+        },
+        "variable": {
+          "color": "#575279"
+        },
+        "string": {
+          "color": "#ea9d34"
+        },
+        "type": {
+          "color": "#56949f"
+        },
+        "type.builtin": {
+          "color": "#56949f"
+        },
+        "boolean": {
+          "color": "#d7827e"
+        },
+        "function": {
+          "color": "#d7827e"
+        },
+        "keyword": {
+          "color": "#286983"
+        },
+        "tag": {
+          "color": "#56949f"
+        },
+        "function.method": {
+          "color": "#d7827e"
+        },
+        "title": {
+          "color": "#ea9d34"
+        },
+        "link_text": {
+          "color": "#56949f",
+          "italic": false
+        },
+        "link_uri": {
+          "color": "#d7827e"
+        }
+      },
+      "color_family": {
+        "neutral": {
+          "low": 39.80392156862745,
+          "high": 95.49019607843137,
+          "range": 55.686274509803916,
+          "scaling_value": 1.7957746478873242
+        },
+        "red": {
+          "low": 0,
+          "high": 100,
+          "range": 100,
+          "scaling_value": 1
+        },
+        "orange": {
+          "low": 0,
+          "high": 100,
+          "range": 100,
+          "scaling_value": 1
+        },
+        "yellow": {
+          "low": 8.823529411764707,
+          "high": 100,
+          "range": 91.17647058823529,
+          "scaling_value": 1.0967741935483872
+        },
+        "green": {
+          "low": 0,
+          "high": 100,
+          "range": 100,
+          "scaling_value": 1
+        },
+        "cyan": {
+          "low": 0,
+          "high": 100,
+          "range": 100,
+          "scaling_value": 1
+        },
+        "blue": {
+          "low": 0,
+          "high": 100,
+          "range": 100,
+          "scaling_value": 1
+        },
+        "violet": {
+          "low": 0,
+          "high": 100,
+          "range": 100,
+          "scaling_value": 1
+        },
+        "magenta": {
+          "low": 0,
+          "high": 100,
+          "range": 100,
+          "scaling_value": 1
+        }
+      }
+    }))
+    .unwrap()
+}

crates/storybook2/src/workspace.rs πŸ”—

@@ -26,8 +26,8 @@ impl Workspace {
     }
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<State = Self> {
-        let theme = rose_pine_dawn();
         themed(rose_pine_dawn(), cx, |cx| {
+            let theme = theme(cx);
             div()
                 .size_full()
                 .flex()