Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/src/elements/div.rs                  |   4 
crates/gpui3/src/platform.rs                      |  29 ---
crates/gpui3/src/platform/mac/text_system.rs      | 109 ++++++----------
crates/gpui3/src/style.rs                         |  55 ++-----
crates/gpui3/src/style_helpers.rs                 |  44 ++++-
crates/gpui3/src/text_system.rs                   | 102 +++++++-------
crates/gpui3/src/text_system/line_wrapper.rs      |  54 +++-----
crates/gpui3/src/text_system/text_layout_cache.rs |   4 
8 files changed, 171 insertions(+), 230 deletions(-)

Detailed changes

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

@@ -35,7 +35,7 @@ impl<S: 'static> Element for Div<S> {
         let style = self.computed_style();
         let pop_text_style = style
             .text_style(cx)
-            .map(|style| cx.push_text_style(style))
+            .map(|style| cx.push_text_style(style.clone()))
             .is_some();
 
         let children = self
@@ -63,7 +63,7 @@ impl<S: 'static> Element for Div<S> {
         let style = self.computed_style();
         let pop_text_style = style
             .text_style(cx)
-            .map(|style| cx.push_text_style(style))
+            .map(|style| cx.push_text_style(style.clone()))
             .is_some();
         style.paint_background(bounds, cx);
         // self.interaction_handlers().paint(order, bounds, cx);

crates/gpui3/src/platform.rs 🔗

@@ -6,8 +6,8 @@ mod mac;
 mod test;
 
 use crate::{
-    AnyWindowHandle, Bounds, FontFeatures, FontId, FontStyle, FontWeight, GlyphId, LineLayout,
-    Pixels, Point, Result, RunStyle, Scene, SharedString, Size,
+    AnyWindowHandle, Bounds, Font, FontId, FontMetrics, GlyphId, LineLayout, Pixels, Point, Result,
+    RunStyle, Scene, SharedString, Size,
 };
 use anyhow::anyhow;
 use async_task::Runnable;
@@ -156,8 +156,8 @@ pub trait PlatformDispatcher: Send + Sync {
 pub trait PlatformTextSystem: Send + Sync {
     fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()>;
     fn all_font_families(&self) -> Vec<String>;
-    fn select_font(&self, descriptor: FontDescriptor) -> Result<FontId>;
-    fn font_metrics(&self, font_id: FontId) -> FontMetrics;
+    fn select_font(&self, descriptor: Font) -> Result<FontId>;
+    fn font_metrics(&self, font_id: FontId) -> Arc<FontMetrics>;
     fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
     fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
     fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
@@ -397,24 +397,3 @@ impl ClipboardItem {
         hasher.finish()
     }
 }
-
-#[derive(Clone, Debug, Eq, PartialEq, Hash)]
-pub struct FontDescriptor {
-    family: SharedString,
-    features: FontFeatures,
-    weight: FontWeight,
-    style: FontStyle,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct FontMetrics {
-    pub units_per_em: u32,
-    pub ascent: f32,
-    pub descent: f32,
-    pub line_gap: f32,
-    pub underline_position: f32,
-    pub underline_thickness: f32,
-    pub cap_height: f32,
-    pub x_height: f32,
-    pub bounding_box: Bounds<f32>,
-}

crates/gpui3/src/platform/mac/text_system.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
-    platform::FontDescriptor, point, px, size, Bounds, FontFeatures, FontId, FontMetrics,
-    FontStyle, FontWeight, Glyph, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point,
-    RasterizationOptions, Result, Run, RunStyle, SharedString, Size,
+    point, px, size, Bounds, Font, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, Glyph,
+    GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RasterizationOptions, Result, Run,
+    RunStyle, SharedString, Size,
 };
 use cocoa::appkit::{CGFloat, CGPoint};
 use collections::HashMap;
@@ -18,6 +18,7 @@ use core_graphics::{
 };
 use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName};
 use font_kit::{
+    font::Font as FontKitFont,
     handle::Handle,
     hinting::HintingOptions,
     metrics::Metrics,
@@ -44,8 +45,9 @@ pub struct MacTextSystem(RwLock<TextSystemState>);
 struct TextSystemState {
     memory_source: MemSource,
     system_source: SystemSource,
-    fonts: Vec<font_kit::font::Font>,
-    font_selections: HashMap<FontDescriptor, FontId>,
+    fonts: Vec<FontKitFont>,
+    font_selections: HashMap<Font, FontId>,
+    font_metrics: HashMap<FontId, Arc<FontMetrics>>,
     font_ids_by_postscript_name: HashMap<String, FontId>,
     font_ids_by_family_name: HashMap<SharedString, SmallVec<[FontId; 4]>>,
     postscript_names_by_font_id: HashMap<FontId, String>,
@@ -58,6 +60,7 @@ impl MacTextSystem {
             system_source: SystemSource::new(),
             fonts: Vec::new(),
             font_selections: HashMap::default(),
+            font_metrics: HashMap::default(),
             font_ids_by_postscript_name: HashMap::default(),
             font_ids_by_family_name: HashMap::default(),
             postscript_names_by_font_id: HashMap::default(),
@@ -84,21 +87,21 @@ impl PlatformTextSystem for MacTextSystem {
             .expect("core text should never return an error")
     }
 
-    fn select_font(&self, descriptor: FontDescriptor) -> Result<FontId> {
+    fn select_font(&self, font: Font) -> Result<FontId> {
         let lock = self.0.upgradable_read();
-        if let Some(font_id) = lock.font_selections.get(&descriptor) {
+        if let Some(font_id) = lock.font_selections.get(&font) {
             Ok(*font_id)
         } else {
             let mut lock = parking_lot::RwLockUpgradableReadGuard::upgrade(lock);
-            let candidates =
-                if let Some(font_ids) = lock.font_ids_by_family_name.get(&descriptor.family) {
-                    font_ids.as_slice()
-                } else {
-                    let font_ids = lock.load_family(&descriptor.family, descriptor.features)?;
-                    lock.font_ids_by_family_name
-                        .insert(descriptor.family.clone(), font_ids);
-                    lock.font_ids_by_family_name[&descriptor.family].as_ref()
-                };
+            let candidates = if let Some(font_ids) = lock.font_ids_by_family_name.get(&font.family)
+            {
+                font_ids.as_slice()
+            } else {
+                let font_ids = lock.load_family(&font.family, font.features)?;
+                lock.font_ids_by_family_name
+                    .insert(font.family.clone(), font_ids);
+                lock.font_ids_by_family_name[&font.family].as_ref()
+            };
 
             let candidate_properties = candidates
                 .iter()
@@ -108,8 +111,8 @@ impl PlatformTextSystem for MacTextSystem {
             let ix = font_kit::matching::find_best_match(
                 &candidate_properties,
                 &font_kit::properties::Properties {
-                    style: descriptor.style.into(),
-                    weight: descriptor.weight.into(),
+                    style: font.style.into(),
+                    weight: font.weight.into(),
                     stretch: Default::default(),
                 },
             )?;
@@ -118,8 +121,16 @@ impl PlatformTextSystem for MacTextSystem {
         }
     }
 
-    fn font_metrics(&self, font_id: FontId) -> FontMetrics {
-        self.0.read().font_metrics(font_id)
+    fn font_metrics(&self, font_id: FontId) -> Arc<FontMetrics> {
+        let lock = self.0.upgradable_read();
+        if let Some(metrics) = lock.font_metrics.get(&font_id) {
+            metrics.clone()
+        } else {
+            let mut lock = parking_lot::RwLockUpgradableReadGuard::upgrade(lock);
+            let metrics: Arc<FontMetrics> = Arc::new(lock.fonts[font_id.0].metrics().into());
+            lock.font_metrics.insert(font_id, metrics.clone());
+            metrics
+        }
     }
 
     fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
@@ -203,46 +214,6 @@ impl TextSystemState {
         Ok(font_ids)
     }
 
-    // fn select_font(
-    //     &mut self,
-    //     family: &SharedString,
-    //     weight: FontWeight,
-    //     style: FontStyle,
-    //     features: FontFeatures,
-    // ) -> Result<FontId> {
-    //     let candidates = if let Some(font_ids) = self.font_ids_by_family_name.get(family) {
-    //         font_ids
-    //     } else {
-    //         let font_ids = if let Some(font_ids) = self.font_ids_by_family_name.get(family) {
-    //             font_ids.as_slice()
-    //         } else {
-    //             self.font_ids_by_family_name
-    //                 .insert(family.clone())
-    //                 .or_insert(font_ids).as_slice()
-    //         };
-
-    //     };
-
-    //     let font_properties = candidates
-    //         .iter()
-    //         .map(|font_id| self.fonts[font_id.0].properties())
-    //         .collect::<SmallVec<[_; 4]>>();
-
-    //     // let idx = font_kit::matching::find_best_match(
-    //     //     &candidates,
-    //     //     &font_kit::properties::Properties {
-    //     //         style: style.into(),
-    //     //         weight: weight.into(),
-    //     //         stretch: Default::default(),
-    //     //     },
-    //     // )?;
-    //     // Ok(font_ids[idx])
-    // }
-
-    fn font_metrics(&self, font_id: FontId) -> FontMetrics {
-        self.fonts[font_id.0].metrics().into()
-    }
-
     fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
         Ok(self.fonts[font_id.0]
             .typographic_bounds(glyph_id.into())?
@@ -393,30 +364,30 @@ impl TextSystemState {
             string.replace_str(&CFString::new(text), CFRange::init(0, 0));
             let utf16_line_len = string.char_len() as usize;
 
-            let last_run: RefCell<Option<(usize, FontId)>> = Default::default();
+            let last_run: RefCell<Option<(usize, Font)>> = Default::default();
             let font_runs = runs
                 .iter()
                 .filter_map(|(len, style)| {
                     let mut last_run = last_run.borrow_mut();
-                    if let Some((last_len, last_font_id)) = last_run.as_mut() {
-                        if style.font_id == *last_font_id {
+                    if let Some((last_len, last_font)) = last_run.as_mut() {
+                        if style.font == *last_font {
                             *last_len += *len;
                             None
                         } else {
-                            let result = (*last_len, *last_font_id);
+                            let result = (*last_len, last_font.clone());
                             *last_len = *len;
-                            *last_font_id = style.font_id;
+                            *last_font = style.font.clone();
                             Some(result)
                         }
                     } else {
-                        *last_run = Some((*len, style.font_id));
+                        *last_run = Some((*len, style.font.clone()));
                         None
                     }
                 })
                 .chain(std::iter::from_fn(|| last_run.borrow_mut().take()));
 
             let mut ix_converter = StringIndexConverter::new(text);
-            for (run_len, font_id) in font_runs {
+            for (run_len, font_descriptor) in font_runs {
                 let utf8_end = ix_converter.utf8_ix + run_len;
                 let utf16_start = ix_converter.utf16_ix;
 
@@ -429,7 +400,9 @@ impl TextSystemState {
 
                 let cf_range =
                     CFRange::init(utf16_start as isize, (utf16_end - utf16_start) as isize);
-                let font = &self.fonts[font_id.0];
+
+                let font_id = self.font_selections[&font_descriptor];
+                let font: &FontKitFont = &self.fonts[font_id.0];
                 unsafe {
                     string.set_attribute(
                         cf_range,

crates/gpui3/src/style.rs 🔗

@@ -1,7 +1,8 @@
 use crate::{
     rems, AbsoluteLength, Bounds, Corners, CornersRefinement, DefiniteLength, Edges,
-    EdgesRefinement, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems,
-    Result, RunStyle, SharedString, Size, SizeRefinement, ViewContext, WindowContext,
+    EdgesRefinement, Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point,
+    PointRefinement, Rems, Result, RunStyle, SharedString, Size, SizeRefinement, ViewContext,
+    WindowContext,
 };
 use refineable::Refineable;
 pub use taffy::style::{
@@ -88,17 +89,8 @@ pub struct Style {
     #[refineable]
     pub corner_radii: Corners<AbsoluteLength>,
 
-    /// 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<Rems>,
-
-    pub font_family: Option<SharedString>,
-
-    pub font_weight: Option<FontWeight>,
-
-    pub font_style: Option<FontStyle>,
+    /// TEXT
+    pub text: TextStyleRefinement,
 }
 
 #[derive(Refineable, Clone, Debug)]
@@ -106,6 +98,7 @@ pub struct Style {
 pub struct TextStyle {
     pub color: Hsla,
     pub font_family: SharedString,
+    pub font_features: FontFeatures,
     pub font_size: Rems,
     pub font_weight: FontWeight,
     pub font_style: FontStyle,
@@ -117,6 +110,7 @@ impl Default for TextStyle {
         TextStyle {
             color: Hsla::default(),
             font_family: SharedString::default(),
+            font_features: FontFeatures::default(),
             font_size: rems(1.),
             font_weight: FontWeight::default(),
             font_style: FontStyle::default(),
@@ -151,7 +145,12 @@ impl TextStyle {
 
     pub fn to_run(&self) -> RunStyle {
         RunStyle {
-            font_id: todo!(),
+            font: Font {
+                family: self.font_family.clone(),
+                features: Default::default(),
+                weight: self.font_weight,
+                style: self.font_style,
+            },
             color: self.color,
             underline: self.underline.clone(),
         }
@@ -170,24 +169,12 @@ pub struct HighlightStyle {
 impl Eq for HighlightStyle {}
 
 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;
+    pub fn text_style(&self, _cx: &WindowContext) -> Option<&TextStyleRefinement> {
+        if self.text.is_some() {
+            Some(&self.text)
+        } else {
+            None
         }
-
-        Some(TextStyleRefinement {
-            color: self.text_color,
-            font_family: self.font_family.clone(),
-            font_size: self.font_size,
-            font_weight: self.font_weight,
-            font_style: self.font_style,
-            underline: None,
-        })
     }
 
     /// Paints the background of an element styled with this style.
@@ -244,11 +231,7 @@ impl Default for Style {
             fill: None,
             border_color: None,
             corner_radii: Corners::default(),
-            text_color: None,
-            font_size: Some(rems(1.)),
-            font_family: None,
-            font_weight: None,
-            font_style: None,
+            text: TextStyleRefinement::default(),
         }
     }
 }

crates/gpui3/src/style_helpers.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     self as gpui3, relative, rems, AlignItems, Display, Fill, FlexDirection, Hsla, JustifyContent,
-    Length, Position, SharedString, Style, Styled,
+    Length, Position, SharedString, Style, StyleRefinement, Styled, TextStyleRefinement,
 };
 
 pub trait StyleHelpers: Styled<Style = Style> {
@@ -213,12 +213,16 @@ pub trait StyleHelpers: Styled<Style = Style> {
         self
     }
 
-    fn text_color<C>(mut self, color: C) -> Self
+    fn text_style(&mut self) -> &mut Option<TextStyleRefinement> {
+        let style: &mut StyleRefinement = self.declared_style();
+        &mut style.text
+    }
+
+    fn text_color(mut self, color: impl Into<Hsla>) -> Self
     where
-        C: Into<Hsla>,
         Self: Sized,
     {
-        self.declared_style().text_color = Some(color.into());
+        self.text_style().get_or_insert_with(Default::default).color = Some(color.into());
         self
     }
 
@@ -226,7 +230,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_size = Some(rems(0.75));
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(rems(0.75));
         self
     }
 
@@ -234,7 +240,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_size = Some(rems(0.875));
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(rems(0.875));
         self
     }
 
@@ -242,7 +250,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_size = Some(rems(1.0));
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(rems(1.0));
         self
     }
 
@@ -250,7 +260,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_size = Some(rems(1.125));
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(rems(1.125));
         self
     }
 
@@ -258,7 +270,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_size = Some(rems(1.25));
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(rems(1.25));
         self
     }
 
@@ -266,7 +280,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_size = Some(rems(1.5));
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(rems(1.5));
         self
     }
 
@@ -274,7 +290,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_size = Some(rems(1.875));
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_size = Some(rems(1.875));
         self
     }
 
@@ -282,7 +300,9 @@ pub trait StyleHelpers: Styled<Style = Style> {
     where
         Self: Sized,
     {
-        self.declared_style().font_family = Some(family_name.into());
+        self.text_style()
+            .get_or_insert_with(Default::default)
+            .font_family = Some(family_name.into());
         self
     }
 }

crates/gpui3/src/text_system.rs 🔗

@@ -6,7 +6,9 @@ pub use font_features::*;
 use line_wrapper::*;
 pub use text_layout_cache::*;
 
-use crate::{Hsla, Pixels, PlatformTextSystem, Point, Result, Size, UnderlineStyle};
+use crate::{
+    Bounds, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size, UnderlineStyle,
+};
 use collections::HashMap;
 use core::fmt;
 use parking_lot::Mutex;
@@ -26,64 +28,25 @@ pub struct FontFamilyId(pub usize);
 pub struct TextSystem {
     text_layout_cache: Arc<TextLayoutCache>,
     platform_text_system: Arc<dyn PlatformTextSystem>,
-    wrapper_pool: Mutex<HashMap<(FontId, Pixels), Vec<LineWrapper>>>,
+    wrapper_pool: Mutex<HashMap<(Font, Pixels), Vec<LineWrapper>>>,
 }
 
 impl TextSystem {
     pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
         TextSystem {
-            // font_cache: Arc::new(FontCache::new(platform_text_system.clone())),
             text_layout_cache: Arc::new(TextLayoutCache::new(platform_text_system.clone())),
-            platform_text_system,
             wrapper_pool: Mutex::new(HashMap::default()),
+            platform_text_system,
         }
     }
 
-    pub fn font_family_name(&self, family_id: FontFamilyId) -> Result<Arc<str>> {
-        todo!()
-        // self.font_cache.family_name(family_id)
-    }
-
-    pub fn load_font_family(
-        &self,
-        names: &[&str],
-        features: &FontFeatures,
-    ) -> Result<FontFamilyId> {
-        todo!()
-        // self.font_cache.load_family(names, features)
+    pub fn select_font(&self, descriptor: impl Into<Font>) -> Result<FontId> {
+        self.platform_text_system.select_font(descriptor.into())
     }
 
-    /// Returns an arbitrary font family that is available on the system.
-    pub fn known_existing_font_family(&self) -> FontFamilyId {
-        todo!()
-        // self.font_cache.known_existing_family()
-    }
-
-    pub fn default_font(&self, family_id: FontFamilyId) -> FontId {
-        todo!()
-        // self.font_cache.default_font(family_id)
-    }
-
-    pub fn select_font(
-        &self,
-        family_id: FontFamilyId,
-        weight: FontWeight,
-        style: FontStyle,
-    ) -> Result<FontId> {
-        todo!()
-        // self.font_cache.select_font(family_id, weight, style)
-    }
-
-    // pub fn read_font_metric<F, T>(&self, font_id: FontId, f: F) -> T
-    // where
-    //     F: FnOnce(&FontMetrics) -> T,
-    //     T: 'static,
-    // {
-    //     todo!()
-    //     // self.font_cache.read_metric(font_id, f)
-    // }
-
     pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Size<Pixels> {
+        let metrics = self.platform_text_system.font_metrics(font_id);
+
         todo!()
         // self.font_cache.bounding_box(font_id, font_size)
     }
@@ -146,11 +109,11 @@ impl TextSystem {
         self.text_layout_cache.finish_frame()
     }
 
-    pub fn line_wrapper(self: &Arc<Self>, font_id: FontId, font_size: Pixels) -> LineWrapperHandle {
+    pub fn line_wrapper(self: &Arc<Self>, font: Font, font_size: Pixels) -> LineWrapperHandle {
         let lock = &mut self.wrapper_pool.lock();
-        let wrappers = lock.entry((font_id, font_size)).or_default();
+        let wrappers = lock.entry((font.clone(), font_size)).or_default();
         let wrapper = wrappers.pop().unwrap_or_else(|| {
-            LineWrapper::new(font_id, font_size, self.platform_text_system.clone())
+            LineWrapper::new(font, font_size, self.platform_text_system.clone())
         });
 
         LineWrapperHandle {
@@ -170,7 +133,7 @@ impl Drop for LineWrapperHandle {
         let mut state = self.text_system.wrapper_pool.lock();
         let wrapper = self.wrapper.take().unwrap();
         state
-            .get_mut(&(wrapper.font_id, wrapper.font_size))
+            .get_mut(&(wrapper.font.clone(), wrapper.font_size))
             .unwrap()
             .push(wrapper);
     }
@@ -256,8 +219,8 @@ impl Display for FontStyle {
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct RunStyle {
+    pub font: Font,
     pub color: Hsla,
-    pub font_id: FontId,
     pub underline: Option<UnderlineStyle>,
 }
 
@@ -305,3 +268,40 @@ pub struct Run {
     pub font_id: FontId,
     pub glyphs: Vec<Glyph>,
 }
+
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct Font {
+    pub family: SharedString,
+    pub features: FontFeatures,
+    pub weight: FontWeight,
+    pub style: FontStyle,
+}
+
+pub fn font(family: impl Into<SharedString>) -> Font {
+    Font {
+        family: family.into(),
+        features: FontFeatures::default(),
+        weight: FontWeight::default(),
+        style: FontStyle::default(),
+    }
+}
+
+impl Font {
+    pub fn bold(mut self) -> Self {
+        self.weight = FontWeight::BOLD;
+        self
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct FontMetrics {
+    pub units_per_em: u32,
+    pub ascent: f32,
+    pub descent: f32,
+    pub line_gap: f32,
+    pub underline_position: f32,
+    pub underline_thickness: f32,
+    pub cap_height: f32,
+    pub x_height: f32,
+    pub bounding_box: Bounds<f32>,
+}

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

@@ -1,11 +1,10 @@
-use super::FontId;
-use crate::{px, Line, Pixels, PlatformTextSystem, RunStyle, ShapedBoundary};
+use crate::{px, Font, Line, Pixels, PlatformTextSystem, RunStyle, ShapedBoundary};
 use collections::HashMap;
 use std::{iter, sync::Arc};
 
 pub struct LineWrapper {
     text_system: Arc<dyn PlatformTextSystem>,
-    pub(crate) font_id: FontId,
+    pub(crate) font: Font,
     pub(crate) font_size: Pixels,
     cached_ascii_char_widths: [Option<Pixels>; 128],
     cached_other_char_widths: HashMap<char, Pixels>,
@@ -14,14 +13,10 @@ pub struct LineWrapper {
 impl LineWrapper {
     pub const MAX_INDENT: u32 = 256;
 
-    pub fn new(
-        font_id: FontId,
-        font_size: Pixels,
-        text_system: Arc<dyn PlatformTextSystem>,
-    ) -> Self {
+    pub fn new(font: Font, font_size: Pixels, text_system: Arc<dyn PlatformTextSystem>) -> Self {
         Self {
             text_system,
-            font_id,
+            font,
             font_size,
             cached_ascii_char_widths: [None; 128],
             cached_other_char_widths: HashMap::default(),
@@ -190,7 +185,7 @@ impl LineWrapper {
                 &[(
                     1,
                     RunStyle {
-                        font_id: self.font_id,
+                        font: self.font.clone(),
                         color: Default::default(),
                         underline: Default::default(),
                     },
@@ -215,21 +210,17 @@ impl Boundary {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{App, FontFeatures, FontWeight};
+    use crate::{font, App};
 
     #[test]
     fn test_wrap_line() {
         App::test().run(|cx| {
             let text_system = cx.text_system().clone();
-            let family = text_system
-                .load_font_family(&["Courier"], &Default::default())
-                .unwrap();
-            let font_id = text_system
-                .select_font(family, Default::default(), Default::default())
-                .unwrap();
-
-            let mut wrapper =
-                LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
+            let mut wrapper = LineWrapper::new(
+                font("Courier"),
+                px(16.),
+                text_system.platform_text_system.clone(),
+            );
             assert_eq!(
                 wrapper
                     .wrap_line("aa bbb cccc ddddd eeee", px(72.))
@@ -290,21 +281,13 @@ mod tests {
         App::test().run(|cx| {
             let text_system = cx.text_system().clone();
 
-            let family = text_system
-                .load_font_family(&["Helvetica"], &FontFeatures::default())
-                .unwrap();
-            let font_id = text_system
-                .select_font(family, Default::default(), Default::default())
-                .unwrap();
             let normal = RunStyle {
-                font_id,
+                font: font("Helvetica"),
                 color: Default::default(),
                 underline: Default::default(),
             };
             let bold = RunStyle {
-                font_id: text_system
-                    .select_font(family, FontWeight::BOLD, Default::default())
-                    .unwrap(),
+                font: font("Helvetica").bold(),
                 color: Default::default(),
                 underline: Default::default(),
             };
@@ -317,13 +300,16 @@ mod tests {
                     (4, normal.clone()),
                     (5, bold.clone()),
                     (6, normal.clone()),
-                    (1, bold),
-                    (7, normal),
+                    (1, bold.clone()),
+                    (7, normal.clone()),
                 ],
             );
 
-            let mut wrapper =
-                LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
+            let mut wrapper = LineWrapper::new(
+                normal.font,
+                px(16.),
+                text_system.platform_text_system.clone(),
+            );
             assert_eq!(
                 wrapper
                     .wrap_shaped_line(text, &line, px(72.))

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

@@ -139,7 +139,7 @@ impl<'a> PartialEq for CacheKeyRef<'a> {
             && self.runs.len() == other.runs.len()
             && self.runs.iter().zip(other.runs.iter()).all(
                 |((len_a, style_a), (len_b, style_b))| {
-                    len_a == len_b && style_a.font_id == style_b.font_id
+                    len_a == len_b && style_a.font == style_b.font
                 },
             )
     }
@@ -151,7 +151,7 @@ impl<'a> Hash for CacheKeyRef<'a> {
         self.font_size.hash(state);
         for (len, style_id) in self.runs {
             len.hash(state);
-            style_id.font_id.hash(state);
+            style_id.font.hash(state);
         }
     }
 }