WIP: Icons not yet rendering

Nathan Sobo created

Change summary

crates/gpui/src/app/window.rs        |  3 -
crates/gpui2/src/element.rs          |  2 
crates/gpui2/src/elements.rs         |  2 
crates/gpui2/src/elements/svg.rs     | 81 +++++++++++++++++++++++++++++
crates/gpui2/src/elements/text.rs    |  8 ++
crates/storybook/src/collab_panel.rs | 53 +++++++++++--------
crates/storybook/src/storybook.rs    |  1 
crates/storybook/src/theme.rs        |  5 +
8 files changed, 126 insertions(+), 29 deletions(-)

Detailed changes

crates/gpui/src/app/window.rs 🔗

@@ -1291,9 +1291,6 @@ impl<'a> WindowContext<'a> {
     pub fn push_text_style(&mut self, refinement: &TextStyleRefinement) -> Result<()> {
         let mut style = self.text_style();
         style.refine(refinement, self.font_cache())?;
-
-        dbg!(&style);
-
         self.window.text_style_stack.push(style);
         Ok(())
     }

crates/gpui2/src/element.rs 🔗

@@ -5,7 +5,7 @@ use gpui::geometry::vector::Vector2F;
 pub use gpui::{Layout, LayoutId};
 use smallvec::SmallVec;
 
-pub trait Element<V: 'static>: 'static {
+pub trait Element<V: 'static>: 'static + IntoElement<V> {
     type PaintState;
 
     fn layout(

crates/gpui2/src/elements.rs 🔗

@@ -1,6 +1,8 @@
 pub mod div;
 pub mod hoverable;
 pub mod pressable;
+pub mod svg;
 pub mod text;
 
 pub use div::div;
+pub use svg::svg;

crates/gpui2/src/elements/svg.rs 🔗

@@ -0,0 +1,81 @@
+use crate::{
+    self as gpui2, scene,
+    style::{Style, StyleHelpers, Styleable},
+    Element, IntoElement, Layout, LayoutId, Rgba,
+};
+use refineable::RefinementCascade;
+use std::borrow::Cow;
+
+#[derive(IntoElement)]
+pub struct Svg {
+    path: Option<Cow<'static, str>>,
+    style: RefinementCascade<Style>,
+}
+
+pub fn svg() -> Svg {
+    Svg {
+        path: None,
+        style: RefinementCascade::<Style>::default(),
+    }
+}
+
+impl Svg {
+    pub fn path(mut self, path: impl Into<Cow<'static, str>>) -> Self {
+        self.path = Some(path.into());
+        self
+    }
+}
+
+impl<V: 'static> Element<V> for Svg {
+    type PaintState = ();
+
+    fn layout(
+        &mut self,
+        _: &mut V,
+        cx: &mut crate::LayoutContext<V>,
+    ) -> anyhow::Result<(LayoutId, Self::PaintState)>
+    where
+        Self: Sized,
+    {
+        let style = self.computed_style();
+        Ok((cx.add_layout_node(style, [])?, ()))
+    }
+
+    fn paint(
+        &mut self,
+        _: &mut V,
+        layout: &Layout,
+        _: &mut Self::PaintState,
+        cx: &mut crate::paint_context::PaintContext<V>,
+    ) where
+        Self: Sized,
+    {
+        let fill_color = self.computed_style().fill.and_then(|fill| fill.color());
+        if let Some((path, fill_color)) = self.path.as_ref().zip(fill_color) {
+            if let Ok(svg_tree) = cx.asset_cache.svg(path) {
+                let icon = scene::Icon {
+                    bounds: layout.bounds,
+                    svg: svg_tree,
+                    path: path.clone(),
+                    color: Rgba::from(fill_color).into(),
+                };
+
+                cx.scene.push_icon(icon);
+            }
+        }
+    }
+}
+
+impl Styleable for Svg {
+    type Style = Style;
+
+    fn style_cascade(&mut self) -> &mut refineable::RefinementCascade<Self::Style> {
+        &mut self.style
+    }
+
+    fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
+        self.style.base()
+    }
+}
+
+impl StyleHelpers for Svg {}

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

@@ -90,6 +90,14 @@ impl<V: 'static> Element<V> for Text {
     }
 }
 
+impl<V: 'static> IntoElement<V> for Text {
+    type Element = Self;
+
+    fn into_element(self) -> Self::Element {
+        self
+    }
+}
+
 pub struct TextLayout {
     line_layout: Arc<LineLayout>,
     line_height: f32,

crates/storybook/src/collab_panel.rs 🔗

@@ -1,5 +1,9 @@
-use crate::theme::theme;
-use gpui2::{elements::div, style::StyleHelpers, Element, IntoElement, ParentElement, ViewContext};
+use crate::theme::{theme, Theme};
+use gpui2::{
+    elements::{div, svg},
+    style::StyleHelpers,
+    Element, IntoElement, ParentElement, ViewContext,
+};
 use std::marker::PhantomData;
 
 #[derive(Element)]
@@ -112,10 +116,11 @@ impl<V: 'static> CollabPanelElement<V> {
                             )
                             .child(
                                 div().flex().h_full().gap_1().items_center().child(
-                                    div()
-                                        .w_3p5()
-                                        .h_3p5()
-                                        .fill(theme.middle.positive.default.foreground),
+                                    svg()
+                                        .path("icons/radix/caret-down.svg")
+                                        .h_3()
+                                        .w_3()
+                                        .fill(theme.middle.variant.default.foreground),
                                 ),
                             ),
                     )
@@ -159,23 +164,7 @@ impl<V: 'static> CollabPanelElement<V> {
                     .flex_col()
                     .gap_y_1()
                     // List Section Header
-                    .child(
-                        div()
-                            .h_7()
-                            .px_2()
-                            .flex()
-                            .justify_between()
-                            .items_center()
-                            .child(div().flex().gap_1().text_sm().child("CHANNELS"))
-                            .child(
-                                div().flex().h_full().gap_1().items_center().child(
-                                    div()
-                                        .w_3p5()
-                                        .h_3p5()
-                                        .fill(theme.middle.positive.default.foreground),
-                                ),
-                            ),
-                    ),
+                    .child(self.list_section_header(theme)),
             )
             // Large List Item
             .child(
@@ -196,4 +185,22 @@ impl<V: 'static> CollabPanelElement<V> {
                     ),
             )
     }
+
+    fn list_section_header(&self, theme: &Theme) -> impl Element<V> {
+        div()
+            .h_7()
+            .px_2()
+            .flex()
+            .justify_between()
+            .items_center()
+            .child(div().flex().gap_1().text_sm().child("CHANNELS"))
+            .child(
+                div().flex().h_full().gap_1().items_center().child(
+                    div()
+                        .w_3p5()
+                        .h_3p5()
+                        .fill(theme.middle.positive.default.foreground),
+                ),
+            )
+    }
 }

crates/storybook/src/storybook.rs 🔗

@@ -69,6 +69,7 @@ use rust_embed::RustEmbed;
 #[folder = "../../assets"]
 #[include = "themes/**/*"]
 #[include = "fonts/**/*"]
+#[include = "icons/**/*"]
 #[exclude = "*.DS_Store"]
 pub struct Assets;
 

crates/storybook/src/theme.rs 🔗

@@ -2,7 +2,7 @@ use gpui2::{
     color::Hsla,
     element::{Element, PaintContext},
     layout_context::LayoutContext,
-    serde_json, AppContext, WindowContext,
+    serde_json, AppContext, IntoElement, WindowContext,
 };
 use serde::{de::Visitor, Deserialize, Deserializer};
 use std::{collections::HashMap, fmt, marker::PhantomData};
@@ -133,7 +133,8 @@ where
     deserializer.deserialize_map(SyntaxVisitor)
 }
 
-pub struct Themed<V: 'static, E> {
+#[derive(IntoElement)]
+pub struct Themed<V: 'static, E: Element<V>> {
     pub(crate) theme: Theme,
     pub(crate) child: E,
     pub(crate) view_type: PhantomData<V>,