Checkpoint

Nathan Sobo created

Change summary

crates/gpui/src/app.rs               |  48 ----------
crates/gpui/src/app/window.rs        |  14 ++
crates/gpui/src/fonts.rs             |  85 +++++++++++++++--
crates/gpui2/src/elements/div.rs     |  14 +-
crates/gpui2/src/elements/text.rs    |   2 
crates/gpui2/src/gpui2.rs            |   2 
crates/gpui2/src/layout_context.rs   |  24 ----
crates/gpui2/src/style.rs            | 141 ++++++++++++++++++++++-------
crates/storybook/src/collab_panel.rs |   1 
crates/storybook/src/storybook.rs    |  16 +++
10 files changed, 217 insertions(+), 130 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -10,7 +10,6 @@ mod window_input_handler;
 use crate::{
     elements::{AnyElement, AnyRootElement, RootElement},
     executor::{self, Task},
-    fonts::TextStyle,
     json,
     keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
     platform::{
@@ -3345,10 +3344,6 @@ impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
         self.element_state::<Tag, T>(element_id, T::default())
     }
 
-    pub fn rem_pixels(&self) -> f32 {
-        16.
-    }
-
     pub fn default_element_state_dynamic<T: 'static + Default>(
         &mut self,
         tag: TypeTag,
@@ -3447,17 +3442,6 @@ impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
     }
 }
 
-/// Methods shared by both LayoutContext and PaintContext
-///
-/// It's that PaintContext should be implemented in terms of layout context and
-/// deref to it, in which case we wouldn't need this.
-pub trait RenderContext<'a, 'b, V> {
-    fn text_style(&self) -> TextStyle;
-    fn push_text_style(&mut self, style: TextStyle);
-    fn pop_text_style(&mut self);
-    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V>;
-}
-
 pub struct LayoutContext<'a, 'b, 'c, V> {
     // Nathan: Making this is public while I work on gpui2.
     pub view_context: &'c mut ViewContext<'a, 'b, V>,
@@ -3520,38 +3504,6 @@ impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> {
             .or_default()
             .push(self_view_id);
     }
-
-    pub fn with_text_style<F, T>(&mut self, style: TextStyle, f: F) -> T
-    where
-        F: FnOnce(&mut Self) -> T,
-    {
-        self.push_text_style(style);
-        let result = f(self);
-        self.pop_text_style();
-        result
-    }
-}
-
-impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, 'c, V> {
-    fn text_style(&self) -> TextStyle {
-        self.window
-            .text_style_stack
-            .last()
-            .cloned()
-            .unwrap_or(TextStyle::default(&self.font_cache))
-    }
-
-    fn push_text_style(&mut self, style: TextStyle) {
-        self.window.text_style_stack.push(style);
-    }
-
-    fn pop_text_style(&mut self) {
-        self.window.text_style_stack.pop();
-    }
-
-    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
-        &mut self.view_context
-    }
 }
 
 impl<'a, 'b, 'c, V> Deref for LayoutContext<'a, 'b, 'c, V> {

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

@@ -1,6 +1,6 @@
 use crate::{
     elements::AnyRootElement,
-    fonts::TextStyle,
+    fonts::{TextStyle, TextStyleRefinement},
     geometry::{rect::RectF, Size},
     json::ToJson,
     keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
@@ -235,6 +235,10 @@ impl<'a> WindowContext<'a> {
             .push_back(Effect::RepaintWindow { window });
     }
 
+    pub fn rem_size(&self) -> f32 {
+        16.
+    }
+
     pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
         self.window.layout_engines.last_mut()
     }
@@ -1284,8 +1288,14 @@ impl<'a> WindowContext<'a> {
             .unwrap_or(TextStyle::default(&self.font_cache))
     }
 
-    pub fn push_text_style(&mut self, style: TextStyle) {
+    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(())
     }
 
     pub fn pop_text_style(&mut self) {

crates/gpui/src/fonts.rs 🔗

@@ -60,7 +60,7 @@ pub struct Features {
     pub zero: Option<bool>,
 }
 
-#[derive(Clone, Debug, JsonSchema, Refineable)]
+#[derive(Clone, Debug, JsonSchema)]
 pub struct TextStyle {
     pub color: Color,
     pub font_family_name: Arc<str>,
@@ -80,19 +80,78 @@ impl TextStyle {
             ..Default::default()
         }
     }
+}
+
+impl TextStyle {
+    pub fn refine(
+        &mut self,
+        refinement: &TextStyleRefinement,
+        font_cache: &FontCache,
+    ) -> Result<()> {
+        if let Some(font_size) = refinement.font_size {
+            self.font_size = font_size;
+        }
+        if let Some(color) = refinement.color {
+            self.color = color;
+        }
+        if let Some(underline) = refinement.underline {
+            self.underline = underline;
+        }
+
+        let mut update_font_id = false;
+        if let Some(font_family) = refinement.font_family.clone() {
+            self.font_family_id = font_cache.load_family(&[&font_family], &Default::default())?;
+            self.font_family_name = font_family;
+            update_font_id = true;
+        }
+        if let Some(font_weight) = refinement.font_weight {
+            self.font_properties.weight = font_weight;
+            update_font_id = true;
+        }
+        if let Some(font_style) = refinement.font_style {
+            self.font_properties.style = font_style;
+            update_font_id = true;
+        }
+
+        if update_font_id {
+            self.font_id = font_cache.select_font(self.font_family_id, &self.font_properties)?;
+        }
 
-    pub fn refine(self, refinement: TextStyleRefinement) -> TextStyle {
-        TextStyle {
-            color: refinement.color.unwrap_or(self.color),
-            font_family_name: refinement
-                .font_family_name
-                .unwrap_or_else(|| self.font_family_name.clone()),
-            font_family_id: refinement.font_family_id.unwrap_or(self.font_family_id),
-            font_id: refinement.font_id.unwrap_or(self.font_id),
-            font_size: refinement.font_size.unwrap_or(self.font_size),
-            font_properties: refinement.font_properties.unwrap_or(self.font_properties),
-            underline: refinement.underline.unwrap_or(self.underline),
-            soft_wrap: refinement.soft_wrap.unwrap_or(self.soft_wrap),
+        Ok(())
+    }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct TextStyleRefinement {
+    pub color: Option<Color>,
+    pub font_family: Option<Arc<str>>,
+    pub font_size: Option<f32>,
+    pub font_weight: Option<Weight>,
+    pub font_style: Option<Style>,
+    pub underline: Option<Underline>,
+}
+
+impl Refineable for TextStyleRefinement {
+    type Refinement = Self;
+
+    fn refine(&mut self, refinement: &Self::Refinement) {
+        if refinement.color.is_some() {
+            self.color = refinement.color;
+        }
+        if refinement.font_family.is_some() {
+            self.font_family = refinement.font_family.clone();
+        }
+        if refinement.font_size.is_some() {
+            self.font_size = refinement.font_size;
+        }
+        if refinement.font_weight.is_some() {
+            self.font_weight = refinement.font_weight;
+        }
+        if refinement.font_style.is_some() {
+            self.font_style = refinement.font_style;
+        }
+        if refinement.underline.is_some() {
+            self.underline = refinement.underline;
         }
     }
 }

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

@@ -6,9 +6,10 @@ use crate::{
     InteractionHandlers, Interactive,
 };
 use anyhow::Result;
-use gpui::{LayoutId, RenderContext};
+use gpui::LayoutId;
 use refineable::{Refineable, RefinementCascade};
 use smallvec::SmallVec;
+use util::ResultExt;
 
 pub struct Div<V: 'static> {
     styles: RefinementCascade<Style>,
@@ -36,9 +37,8 @@ impl<V: 'static> Element<V> for Div<V> {
         Self: Sized,
     {
         let style = self.computed_style();
-        let pop_text_style = style.text_style().map_or(false, |style| {
-            cx.push_text_style(cx.text_style().clone().refined(&style));
-            true
+        let pop_text_style = style.text_style(cx).map_or(false, |style| {
+            cx.push_text_style(&style).log_err().is_some()
         });
 
         let children = self
@@ -64,10 +64,8 @@ impl<V: 'static> Element<V> for Div<V> {
         Self: Sized,
     {
         let style = &self.computed_style();
-        let pop_text_style = style.text_style().map_or(false, |style| {
-            let style = cx.text_style().clone().refined(&style);
-            cx.push_text_style(style);
-            true
+        let pop_text_style = style.text_style(cx).map_or(false, |style| {
+            cx.push_text_style(&style).log_err().is_some()
         });
         style.paint_background(layout.bounds, cx);
         self.interaction_handlers()

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

@@ -4,7 +4,7 @@ use crate::{
     paint_context::PaintContext,
 };
 use anyhow::Result;
-use gpui::{geometry::Size, text_layout::LineLayout, LayoutId, RenderContext};
+use gpui::{geometry::Size, text_layout::LineLayout, LayoutId};
 use parking_lot::Mutex;
 use std::sync::Arc;
 

crates/gpui2/src/gpui2.rs 🔗

@@ -18,5 +18,5 @@ pub use gpui::*;
 pub use gpui2_macros::{Element, *};
 pub use interactive::*;
 pub use layout_context::LayoutContext;
-pub use platform::{WindowBounds, WindowOptions};
+pub use platform::{Platform, WindowBounds, WindowOptions};
 pub use view::*;

crates/gpui2/src/layout_context.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{element::LayoutId, style::Style};
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
-use gpui::{geometry::Size, MeasureParams, RenderContext, ViewContext};
+use gpui::{geometry::Size, MeasureParams};
 pub use gpui::{taffy::tree::NodeId, LayoutContext as LegacyLayoutContext};
 
 #[derive(Deref, DerefMut)]
@@ -11,24 +11,6 @@ pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
     pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
 }
 
-impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
-    fn text_style(&self) -> gpui::fonts::TextStyle {
-        self.legacy_cx.text_style()
-    }
-
-    fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
-        self.legacy_cx.push_text_style(style)
-    }
-
-    fn pop_text_style(&mut self) {
-        self.legacy_cx.pop_text_style()
-    }
-
-    fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
-        &mut self.view_context
-    }
-}
-
 impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
     pub fn new(legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>) -> Self {
         Self { legacy_cx }
@@ -39,7 +21,7 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
         style: Style,
         children: impl IntoIterator<Item = NodeId>,
     ) -> Result<LayoutId> {
-        let rem_size = self.rem_pixels();
+        let rem_size = self.rem_size();
         let id = self
             .legacy_cx
             .layout_engine()
@@ -53,7 +35,7 @@ impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
     where
         F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
     {
-        let rem_size = self.rem_pixels();
+        let rem_size = self.rem_size();
         let layout_id = self
             .layout_engine()
             .ok_or_else(|| anyhow!("no layout engine"))?

crates/gpui2/src/style.rs 🔗

@@ -4,20 +4,23 @@ use crate::{
     elements::pressable::{pressable, Pressable},
     paint_context::PaintContext,
 };
+pub use fonts::Style as FontStyle;
+pub use fonts::Weight as FontWeight;
 pub use gpui::taffy::style::{
     AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
     Overflow, Position,
 };
 use gpui::{
-    fonts::TextStyleRefinement,
+    fonts::{self, TextStyleRefinement},
     geometry::{
         rect::RectF, relative, AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length,
         Point, PointRefinement, Size, SizeRefinement,
     },
-    taffy,
+    taffy, WindowContext,
 };
 use gpui2_macros::styleable_helpers;
 use refineable::{Refineable, RefinementCascade};
+use std::sync::Arc;
 
 #[derive(Clone, Refineable)]
 pub struct Style {
@@ -92,11 +95,41 @@ pub struct Style {
     /// The radius of the corners of this element
     #[refineable]
     pub corner_radii: CornerRadii,
+
     /// The color of text within this element. Cascades to children unless overridden.
     pub text_color: Option<Hsla>,
+
+    /// The font size in rems.
+    pub font_size: Option<f32>,
+
+    pub font_family: Option<Arc<str>>,
+
+    pub font_weight: Option<FontWeight>,
+
+    pub font_style: Option<FontStyle>,
 }
 
 impl Style {
+    pub fn text_style(&self, cx: &WindowContext) -> Option<TextStyleRefinement> {
+        if self.text_color.is_none()
+            && self.font_size.is_none()
+            && self.font_family.is_none()
+            && self.font_weight.is_none()
+            && self.font_style.is_none()
+        {
+            return None;
+        }
+
+        Some(TextStyleRefinement {
+            color: self.text_color.map(Into::into),
+            font_family: self.font_family.clone(),
+            font_size: self.font_size.map(|size| size * cx.rem_size()),
+            font_weight: self.font_weight,
+            font_style: self.font_style,
+            underline: None,
+        })
+    }
+
     pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
         taffy::style::Style {
             display: self.display,
@@ -128,7 +161,7 @@ impl Style {
     /// Paints the background of an element styled with this style.
     /// Return the bounds in which to paint the content.
     pub fn paint_background<V: 'static>(&self, bounds: RectF, cx: &mut PaintContext<V>) {
-        let rem_size = cx.rem_pixels();
+        let rem_size = cx.rem_size();
         if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
             cx.scene.push_quad(gpui::Quad {
                 bounds,
@@ -138,17 +171,6 @@ impl Style {
             });
         }
     }
-
-    pub fn text_style(&self) -> Option<TextStyleRefinement> {
-        if let Some(color) = self.text_color {
-            Some(TextStyleRefinement {
-                color: Some(color.into()),
-                ..Default::default()
-            })
-        } else {
-            None
-        }
-    }
 }
 
 impl Default for Style {
@@ -182,29 +204,12 @@ impl Default for Style {
             flex_shrink: 1.0,
             flex_basis: Length::Auto,
             fill: None,
-            text_color: None,
             corner_radii: CornerRadii::default(),
-        }
-    }
-}
-
-impl StyleRefinement {
-    pub fn text_style(&self) -> Option<TextStyleRefinement> {
-        self.text_color.map(|color| TextStyleRefinement {
-            color: Some(color.into()),
-            ..Default::default()
-        })
-    }
-}
-
-pub struct OptionalTextStyle {
-    color: Option<Hsla>,
-}
-
-impl OptionalTextStyle {
-    pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
-        if let Some(color) = self.color {
-            style.color = color.into();
+            text_color: None,
+            font_size: Some(1.),
+            font_family: None,
+            font_weight: None,
+            font_style: None,
         }
     }
 }
@@ -416,4 +421,68 @@ pub trait StyleHelpers: Styleable<Style = Style> {
         self.declared_style().text_color = Some(color.into());
         self
     }
+
+    fn text_xs(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(0.75);
+        self
+    }
+
+    fn text_sm(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(0.875);
+        self
+    }
+
+    fn text_base(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(1.0);
+        self
+    }
+
+    fn text_lg(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(1.125);
+        self
+    }
+
+    fn text_xl(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(1.25);
+        self
+    }
+
+    fn text_2xl(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(1.5);
+        self
+    }
+
+    fn text_3xl(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_size = Some(1.875);
+        self
+    }
+
+    fn font(mut self, family_name: impl Into<Arc<str>>) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().font_family = Some(family_name.into());
+        self
+    }
 }

crates/storybook/src/collab_panel.rs 🔗

@@ -19,6 +19,7 @@ impl<V: 'static> CollabPanelElement<V> {
 
         div()
             .full()
+            .font("Zed Mono")
             .text_color(theme.middle.variant.default.foreground)
             .fill(theme.middle.base.default.background)
             .py_2()

crates/storybook/src/storybook.rs 🔗

@@ -1,4 +1,5 @@
 #![allow(dead_code, unused_variables)]
+
 use crate::theme::Theme;
 use ::theme as legacy_theme;
 use collab_panel::collab_panel;
@@ -25,6 +26,7 @@ fn main() {
             .unwrap();
         cx.set_global(store);
         legacy_theme::init(Assets, cx);
+        // load_embedded_fonts(cx.platform().as_ref());
 
         cx.add_window(
             gpui2::WindowOptions {
@@ -66,6 +68,7 @@ use rust_embed::RustEmbed;
 #[derive(RustEmbed)]
 #[folder = "../../assets"]
 #[include = "themes/**/*"]
+#[include = "fonts/**/*"]
 #[exclude = "*.DS_Store"]
 pub struct Assets;
 
@@ -80,3 +83,16 @@ impl AssetSource for Assets {
         Self::iter().filter(|p| p.starts_with(path)).collect()
     }
 }
+
+// fn load_embedded_fonts(platform: &dyn gpui2::Platform) {
+//     let font_paths = Assets.list("fonts");
+//     let mut embedded_fonts = Vec::new();
+//     for font_path in &font_paths {
+//         if font_path.ends_with(".ttf") {
+//             let font_path = &*font_path;
+//             let font_bytes = Assets.load(font_path).unwrap().to_vec();
+//             embedded_fonts.push(Arc::from(font_bytes));
+//         }
+//     }
+//     platform.fonts().add_fonts(&embedded_fonts).unwrap();
+// }