diff --git a/Cargo.lock b/Cargo.lock index 84731880fe..d7b49e5798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5127,6 +5127,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "parity-tokio-ipc" version = "0.9.0" @@ -9985,6 +9994,7 @@ dependencies = [ "node_runtime", "num_cpus", "outline", + "owning_ref", "parking_lot 0.11.2", "plugin_runtime", "postage", diff --git a/crates/gpui/src/text_layout.rs b/crates/gpui/src/text_layout.rs index 97f4b7a12d..1f0efe269d 100644 --- a/crates/gpui/src/text_layout.rs +++ b/crates/gpui/src/text_layout.rs @@ -22,8 +22,8 @@ use std::{ }; pub struct TextLayoutCache { - prev_frame: Mutex>>, - curr_frame: RwLock>>, + prev_frame: Mutex>>, + curr_frame: RwLock>>, fonts: Arc, } @@ -56,7 +56,7 @@ impl TextLayoutCache { font_size: f32, runs: &'a [(usize, RunStyle)], ) -> Line { - let key = &CacheKeyRef { + let key = &BorrowedCacheKey { text, font_size: OrderedFloat(font_size), runs, @@ -72,7 +72,7 @@ impl TextLayoutCache { Line::new(layout, runs) } else { let layout = Arc::new(self.fonts.layout_line(text, font_size, runs)); - let key = CacheKeyValue { + let key = OwnedCacheKey { text: text.into(), font_size: OrderedFloat(font_size), runs: SmallVec::from(runs), @@ -84,7 +84,7 @@ impl TextLayoutCache { } trait CacheKey { - fn key(&self) -> CacheKeyRef; + fn key(&self) -> BorrowedCacheKey; } impl<'a> PartialEq for (dyn CacheKey + 'a) { @@ -102,15 +102,15 @@ impl<'a> Hash for (dyn CacheKey + 'a) { } #[derive(Eq)] -struct CacheKeyValue { +struct OwnedCacheKey { text: String, font_size: OrderedFloat, runs: SmallVec<[(usize, RunStyle); 1]>, } -impl CacheKey for CacheKeyValue { - fn key(&self) -> CacheKeyRef { - CacheKeyRef { +impl CacheKey for OwnedCacheKey { + fn key(&self) -> BorrowedCacheKey { + BorrowedCacheKey { text: self.text.as_str(), font_size: self.font_size, runs: self.runs.as_slice(), @@ -118,38 +118,38 @@ impl CacheKey for CacheKeyValue { } } -impl PartialEq for CacheKeyValue { +impl PartialEq for OwnedCacheKey { fn eq(&self, other: &Self) -> bool { self.key().eq(&other.key()) } } -impl Hash for CacheKeyValue { +impl Hash for OwnedCacheKey { fn hash(&self, state: &mut H) { self.key().hash(state); } } -impl<'a> Borrow for CacheKeyValue { +impl<'a> Borrow for OwnedCacheKey { fn borrow(&self) -> &(dyn CacheKey + 'a) { self as &dyn CacheKey } } #[derive(Copy, Clone)] -struct CacheKeyRef<'a> { +struct BorrowedCacheKey<'a> { text: &'a str, font_size: OrderedFloat, runs: &'a [(usize, RunStyle)], } -impl<'a> CacheKey for CacheKeyRef<'a> { - fn key(&self) -> CacheKeyRef { +impl<'a> CacheKey for BorrowedCacheKey<'a> { + fn key(&self) -> BorrowedCacheKey { *self } } -impl<'a> PartialEq for CacheKeyRef<'a> { +impl<'a> PartialEq for BorrowedCacheKey<'a> { fn eq(&self, other: &Self) -> bool { self.text == other.text && self.font_size == other.font_size @@ -162,7 +162,7 @@ impl<'a> PartialEq for CacheKeyRef<'a> { } } -impl<'a> Hash for CacheKeyRef<'a> { +impl<'a> Hash for BorrowedCacheKey<'a> { fn hash(&self, state: &mut H) { self.text.hash(state); self.font_size.hash(state); diff --git a/crates/gpui3/Cargo.toml b/crates/gpui3/Cargo.toml index 965ee5371d..9960e222c4 100644 --- a/crates/gpui3/Cargo.toml +++ b/crates/gpui3/Cargo.toml @@ -2,7 +2,7 @@ name = "gpui3" version = "0.1.0" edition = "2021" -authors = ["Nathan Sobo "] +authors = ["Nathan Sobo "] description = "The next version of Zed's GPU-accelerated UI framework" publish = false diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 20a291a222..0dc2bb5921 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -4,9 +4,7 @@ use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign}; use refineable::Refineable; use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign}; -#[derive( - Refineable, Default, Add, AddAssign, Sub, SubAssign, Mul, Copy, Debug, PartialEq, Eq, Hash, -)] +#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)] #[refineable(debug)] #[repr(C)] pub struct Point { @@ -31,6 +29,17 @@ impl Point { } } +impl, S: Clone> Mul for Point { + type Output = Self; + + fn mul(self, rhs: S) -> Self::Output { + Self { + x: self.x.clone() * rhs.clone(), + y: self.y.clone() * rhs, + } + } +} + impl, S: Clone> MulAssign for Point { fn mul_assign(&mut self, rhs: S) { self.x = self.x.clone() * rhs.clone(); @@ -116,6 +125,17 @@ impl Size { } } +impl, S: Clone> Mul for Size { + type Output = Self; + + fn mul(self, rhs: S) -> Self::Output { + Self { + width: self.width.clone() * rhs.clone(), + height: self.height.clone() * rhs, + } + } +} + impl, S: Clone> MulAssign for Size { fn mul_assign(&mut self, rhs: S) { self.width = self.width.clone() * rhs.clone(); @@ -170,6 +190,20 @@ pub struct Bounds { unsafe impl Zeroable for Bounds {} unsafe impl Pod for Bounds {} +impl, S: Clone> Mul for Bounds +where + T: Mul, +{ + type Output = Self; + + fn mul(self, rhs: S) -> Self::Output { + Self { + origin: self.origin * rhs.clone(), + size: self.size * rhs, + } + } +} + impl, S: Clone> MulAssign for Bounds { fn mul_assign(&mut self, rhs: S) { self.origin *= rhs.clone(); @@ -235,6 +269,19 @@ pub struct Edges { pub left: T, } +impl> Mul for Edges { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self { + top: self.top.clone() * rhs.top, + right: self.right.clone() * rhs.right, + bottom: self.bottom.clone() * rhs.bottom, + left: self.left.clone() * rhs.left, + } + } +} + impl, S: Clone> MulAssign for Edges { fn mul_assign(&mut self, rhs: S) { self.top = self.top.clone() * rhs.clone(); @@ -317,6 +364,19 @@ pub struct Corners { pub bottom_left: T, } +impl> Mul for Corners { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self { + top_left: self.top_left.clone() * rhs.top_left, + top_right: self.top_right.clone() * rhs.top_right, + bottom_right: self.bottom_right.clone() * rhs.bottom_right, + bottom_left: self.bottom_left.clone() * rhs.bottom_left, + } + } +} + impl, S: Clone> MulAssign for Corners { fn mul_assign(&mut self, rhs: S) { self.top_left = self.top_left.clone() * rhs.clone(); diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 3782d259ab..fbf880bbc9 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -156,8 +156,8 @@ pub trait PlatformDispatcher: Send + Sync { pub trait PlatformTextSystem: Send + Sync { fn add_fonts(&self, fonts: &[Arc>]) -> Result<()>; fn all_font_families(&self) -> Vec; - fn select_font(&self, descriptor: Font) -> Result; - fn font_metrics(&self, font_id: FontId) -> Arc; + fn font_id(&self, descriptor: &Font) -> Result; + fn font_metrics(&self, font_id: FontId) -> FontMetrics; fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index 6c3cd7bf27..8a468b170b 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -47,7 +47,7 @@ struct TextSystemState { system_source: SystemSource, fonts: Vec, font_selections: HashMap, - font_metrics: HashMap>, + font_metrics: HashMap, font_ids_by_postscript_name: HashMap, font_ids_by_family_name: HashMap>, postscript_names_by_font_id: HashMap, @@ -87,9 +87,9 @@ impl PlatformTextSystem for MacTextSystem { .expect("core text should never return an error") } - fn select_font(&self, font: Font) -> Result { + fn font_id(&self, font: &Font) -> Result { let lock = self.0.upgradable_read(); - if let Some(font_id) = lock.font_selections.get(&font) { + if let Some(font_id) = lock.font_selections.get(font) { Ok(*font_id) } else { let mut lock = parking_lot::RwLockUpgradableReadGuard::upgrade(lock); @@ -121,20 +121,14 @@ impl PlatformTextSystem for MacTextSystem { } } - fn font_metrics(&self, font_id: FontId) -> Arc { - 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 = Arc::new(lock.fonts[font_id.0].metrics().into()); - lock.font_metrics.insert(font_id, metrics.clone()); - metrics - } + fn font_metrics(&self, font_id: FontId) -> FontMetrics { + self.0.read().fonts[font_id.0].metrics().into() } fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - self.0.read().typographic_bounds(font_id, glyph_id) + Ok(self.0.read().fonts[font_id.0] + .typographic_bounds(glyph_id.into())? + .into()) } fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { @@ -214,12 +208,6 @@ impl TextSystemState { Ok(font_ids) } - fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - Ok(self.fonts[font_id.0] - .typographic_bounds(glyph_id.into())? - .into()) - } - fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { Ok(self.fonts[font_id.0].advance(glyph_id.into())?.into()) } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index f8b806fd4d..bf6b528a42 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -2,18 +2,18 @@ mod font_features; mod line_wrapper; mod text_layout_cache; +use anyhow::anyhow; pub use font_features::*; use line_wrapper::*; pub use text_layout_cache::*; use crate::{ - px, Bounds, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size, UnderlineStyle, + px, Bounds, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, UnderlineStyle, }; use collections::HashMap; use core::fmt; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use std::{ - borrow::BorrowMut, fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, ops::{Deref, DerefMut}, @@ -29,33 +29,65 @@ pub struct FontFamilyId(pub usize); pub struct TextSystem { text_layout_cache: Arc, platform_text_system: Arc, - wrapper_pool: Mutex>>, + font_ids_by_font: RwLock>, + fonts_by_font_id: RwLock>, + font_metrics: RwLock>, + wrapper_pool: Mutex>>, } impl TextSystem { pub fn new(platform_text_system: Arc) -> Self { TextSystem { text_layout_cache: Arc::new(TextLayoutCache::new(platform_text_system.clone())), - wrapper_pool: Mutex::new(HashMap::default()), platform_text_system, + font_metrics: RwLock::new(HashMap::default()), + font_ids_by_font: RwLock::new(HashMap::default()), + fonts_by_font_id: RwLock::new(HashMap::default()), + wrapper_pool: Mutex::new(HashMap::default()), } } - pub fn select_font(&self, descriptor: impl Into) -> Result { - self.platform_text_system.select_font(descriptor.into()) + pub fn font_id(&self, font: &Font) -> Result { + if let Some(font_id) = self.font_ids_by_font.read().get(font) { + Ok(*font_id) + } else { + let font_id = self.platform_text_system.font_id(font)?; + self.font_ids_by_font.write().insert(font.clone(), font_id); + self.fonts_by_font_id.write().insert(font_id, font.clone()); + + Ok(font_id) + } } - pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Size { - let metrics = self.platform_text_system.font_metrics(font_id); - metrics.bounding_box(font_size); - - todo!() - // self.font_cache.bounding_box(font_id, font_size) + pub fn with_font(&self, font_id: FontId, f: impl FnOnce(&Self, &Font) -> T) -> Result { + self.fonts_by_font_id + .read() + .get(&font_id) + .ok_or_else(|| anyhow!("font not found")) + .map(|font| f(self, font)) } - pub fn em_width(&self, font_id: FontId, font_size: Pixels) -> Pixels { - todo!() - // self.font_cache.em_width(font_id, font_size) + pub fn bounding_box(&self, font: &Font, font_size: Pixels) -> Result> { + self.read_metrics(&font, |metrics| metrics.bounding_box(font_size)) + } + + pub fn typographic_bounds( + &self, + font: &Font, + font_size: Pixels, + character: char, + ) -> Result> { + let font_id = self.font_id(font)?; + let glyph_id = self + .platform_text_system + .glyph_for_char(font_id, character) + .ok_or_else(|| anyhow!("glyph not found for character '{}'", character))?; + let bounds = self + .platform_text_system + .typographic_bounds(font_id, glyph_id)?; + self.read_metrics(font, |metrics| { + (bounds / metrics.units_per_em as f32 * font_size.0).map(px) + }) } pub fn em_advance(&self, font_id: FontId, font_size: Pixels) -> Pixels { @@ -68,34 +100,41 @@ impl TextSystem { // self.font_cache.line_height(font_size) } - pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { - todo!() - // self.font_cache.cap_height(font_id, font_size) + pub fn cap_height(&self, font: &Font, font_size: Pixels) -> Result { + self.read_metrics(font, |metrics| metrics.cap_height(font_size)) } - pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { - todo!() - // self.font_cache.x_height(font_id, font_size) + pub fn x_height(&self, font: &Font, font_size: Pixels) -> Result { + self.read_metrics(font, |metrics| metrics.x_height(font_size)) } - pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Pixels { - todo!() - // self.font_cache.ascent(font_id, font_size) + pub fn ascent(&self, font: &Font, font_size: Pixels) -> Result { + self.read_metrics(font, |metrics| metrics.ascent(font_size)) } - pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Pixels { - todo!() - // self.font_cache.descent(font_id, font_size) + pub fn descent(&self, font: &Font, font_size: Pixels) -> Result { + self.read_metrics(font, |metrics| metrics.descent(font_size)) } - pub fn em_size(&self, font_id: FontId, font_size: Pixels) -> Pixels { - todo!() - // self.font_cache.em_size(font_id, font_size) + pub fn baseline_offset(&self, font: &Font, font_size: Pixels) -> Result { + let line_height = self.line_height(font_size); + let ascent = self.ascent(font, font_size)?; + let descent = self.descent(font, font_size)?; + let padding_top = (line_height - ascent - descent) / 2.; + Ok(padding_top + ascent) } - pub fn baseline_offset(&self, font_id: FontId, font_size: Pixels) -> Pixels { - todo!() - // self.font_cache.baseline_offset(font_id, font_size) + fn read_metrics(&self, font: &Font, read: impl FnOnce(&FontMetrics) -> T) -> Result { + if let Some(metrics) = self.font_metrics.read().get(font) { + Ok(read(metrics)) + } else { + let font_id = self.platform_text_system.font_id(&font)?; + let mut lock = self.font_metrics.write(); + let metrics = lock + .entry(font.clone()) + .or_insert_with(|| self.platform_text_system.font_metrics(font_id)); + Ok(read(metrics)) + } } pub fn layout_str<'a>( @@ -113,7 +152,12 @@ impl TextSystem { pub fn line_wrapper(self: &Arc, font: Font, font_size: Pixels) -> LineWrapperHandle { let lock = &mut self.wrapper_pool.lock(); - let wrappers = lock.entry((font.clone(), font_size)).or_default(); + let wrappers = lock + .entry(FontWithSize { + font: font.clone(), + font_size, + }) + .or_default(); let wrapper = wrappers.pop().unwrap_or_else(|| { LineWrapper::new(font, font_size, self.platform_text_system.clone()) }); @@ -125,6 +169,12 @@ impl TextSystem { } } +#[derive(Hash, Eq, PartialEq)] +struct FontWithSize { + font: Font, + font_size: Pixels, +} + pub struct LineWrapperHandle { wrapper: Option, text_system: Arc, @@ -135,7 +185,10 @@ impl Drop for LineWrapperHandle { let mut state = self.text_system.wrapper_pool.lock(); let wrapper = self.wrapper.take().unwrap(); state - .get_mut(&(wrapper.font.clone(), wrapper.font_size)) + .get_mut(&FontWithSize { + font: wrapper.font.clone(), + font_size: wrapper.font_size, + }) .unwrap() .push(wrapper); } @@ -329,49 +382,43 @@ pub struct FontMetrics { } impl FontMetrics { - /// Returns the number of pixels that make up the "em square", - /// a scalable grid for determining the size of a typeface. - pub fn units_per_em(&self, font_size: Pixels) -> Pixels { - Pixels((self.units_per_em as f32 / font_size.0).ceil()) - } - /// Returns the vertical distance from the baseline of the font to the top of the glyph covers in pixels. pub fn ascent(&self, font_size: Pixels) -> Pixels { - Pixels((self.ascent / font_size.0).ceil() as f32) + Pixels((self.ascent / self.units_per_em as f32) * font_size.0) } /// Returns the vertical distance from the baseline of the font to the bottom of the glyph covers in pixels. pub fn descent(&self, font_size: Pixels) -> Pixels { - Pixels((self.descent / font_size.0).ceil() as f32) + Pixels((self.descent / self.units_per_em as f32) * font_size.0) } /// Returns the recommended additional space to add between lines of type in pixels. pub fn line_gap(&self, font_size: Pixels) -> Pixels { - Pixels((self.line_gap / font_size.0).ceil() as f32) + Pixels((self.line_gap / self.units_per_em as f32) * font_size.0) } /// Returns the suggested position of the underline in pixels. pub fn underline_position(&self, font_size: Pixels) -> Pixels { - Pixels((self.underline_position / font_size.0).ceil() as f32) + Pixels((self.underline_position / self.units_per_em as f32) * font_size.0) } /// Returns the suggested thickness of the underline in pixels. pub fn underline_thickness(&self, font_size: Pixels) -> Pixels { - Pixels((self.underline_thickness / font_size.0).ceil() as f32) + Pixels((self.underline_thickness / self.units_per_em as f32) * font_size.0) } /// Returns the height of a capital letter measured from the baseline of the font in pixels. pub fn cap_height(&self, font_size: Pixels) -> Pixels { - Pixels((self.cap_height / font_size.0).ceil() as f32) + Pixels((self.cap_height / self.units_per_em as f32) * font_size.0) } /// Returns the height of a lowercase x in pixels. pub fn x_height(&self, font_size: Pixels) -> Pixels { - Pixels((self.x_height / font_size.0).ceil() as f32) + Pixels((self.x_height / self.units_per_em as f32) * font_size.0) } /// Returns the outer limits of the area that the font covers in pixels. pub fn bounding_box(&self, font_size: Pixels) -> Bounds { - (self.bounding_box / font_size.0).map(px) + (self.bounding_box / self.units_per_em as f32 * font_size.0).map(px) } } diff --git a/crates/gpui3/src/text_system/font_cache.rs b/crates/gpui3/src/text_system/font_cache.rs deleted file mode 100644 index 127b4cc94e..0000000000 --- a/crates/gpui3/src/text_system/font_cache.rs +++ /dev/null @@ -1,302 +0,0 @@ -use crate::{ - px, Bounds, FontFeatures, FontStyle, FontWeight, Pixels, PlatformTextSystem, Result, Size, -}; -use anyhow::anyhow; -use parking_lot::{RwLock, RwLockUpgradableReadGuard}; -use std::{collections::HashMap, sync::Arc}; - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct FontFamilyId(usize); - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct FontId(pub usize); - -pub(crate) struct FontCache(RwLock); - -pub(crate) struct FontCacheState { - platform_text_system: Arc, - families: Vec, - default_family: Option, - font_selections: HashMap>, - metrics: HashMap, -} - -unsafe impl Send for FontCache {} - -impl FontCache { - pub fn new(fonts: Arc) -> Self { - Self(RwLock::new(FontCacheState { - platform_text_system: fonts, - families: Default::default(), - default_family: None, - font_selections: Default::default(), - metrics: Default::default(), - })) - } - - pub fn family_name(&self, family_id: FontFamilyId) -> Result> { - self.0 - .read() - .families - .get(family_id.0) - .ok_or_else(|| anyhow!("invalid family id")) - .map(|family| family.name.clone()) - } - - pub fn load_family(&self, names: &[&str], features: &FontFeatures) -> Result { - for name in names { - let state = self.0.upgradable_read(); - - if let Some(ix) = state - .families - .iter() - .position(|f| f.name.as_ref() == *name && f.font_features == *features) - { - return Ok(FontFamilyId(ix)); - } - - let mut state = RwLockUpgradableReadGuard::upgrade(state); - - if let Ok(font_ids) = state.platform_text_system.load_family(name, features) { - if font_ids.is_empty() { - continue; - } - - let family_id = FontFamilyId(state.families.len()); - for font_id in &font_ids { - if state - .platform_text_system - .glyph_for_char(*font_id, 'm') - .is_none() - { - return Err(anyhow!("font must contain a glyph for the 'm' character")); - } - } - - state.families.push(Family { - name: Arc::from(*name), - font_features: features.clone(), - font_ids, - }); - return Ok(family_id); - } - } - - Err(anyhow!( - "could not find a non-empty font family matching one of the given names" - )) - } - - /// Returns an arbitrary font family that is available on the system. - pub fn known_existing_family(&self) -> FontFamilyId { - if let Some(family_id) = self.0.read().default_family { - return family_id; - } - - let default_family = self - .load_family( - &["Courier", "Helvetica", "Arial", "Verdana"], - &Default::default(), - ) - .unwrap_or_else(|_| { - let all_family_names = self.0.read().platform_text_system.all_families(); - let all_family_names: Vec<_> = all_family_names - .iter() - .map(|string| string.as_str()) - .collect(); - self.load_family(&all_family_names, &Default::default()) - .expect("could not load any default font family") - }); - - self.0.write().default_family = Some(default_family); - default_family - } - - pub fn default_font(&self, family_id: FontFamilyId) -> FontId { - self.select_font(family_id, Default::default(), Default::default()) - .unwrap() - } - - pub fn select_font( - &self, - family_id: FontFamilyId, - weight: FontWeight, - style: FontStyle, - ) -> Result { - let inner = self.0.upgradable_read(); - if let Some(font_id) = inner - .font_selections - .get(&family_id) - .and_then(|fonts| fonts.get(&(weight, style))) - { - Ok(*font_id) - } else { - let mut inner = RwLockUpgradableReadGuard::upgrade(inner); - let family = &inner.families[family_id.0]; - let font_id = inner - .platform_text_system - .select_font(&family.font_ids, weight, style) - .unwrap_or(family.font_ids[0]); - inner - .font_selections - .entry(family_id) - .or_default() - .insert((weight, style), font_id); - Ok(font_id) - } - } - - pub fn read_metric(&self, font_id: FontId, f: F) -> T - where - F: FnOnce(&FontMetrics) -> T, - T: 'static, - { - let state = self.0.upgradable_read(); - if let Some(metrics) = state.metrics.get(&font_id) { - f(metrics) - } else { - let metrics = state.platform_text_system.font_metrics(font_id); - let metric = f(&metrics); - let mut state = RwLockUpgradableReadGuard::upgrade(state); - state.metrics.insert(font_id, metrics); - metric - } - } - - pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Size { - let bounding_box = self.read_metric(font_id, |m| m.bounding_box); - - let width = px(bounding_box.size.width) * self.em_size(font_id, font_size); - let height = px(bounding_box.size.height) * self.em_size(font_id, font_size); - Size { width, height } - } - - pub fn em_width(&self, font_id: FontId, font_size: Pixels) -> Pixels { - let glyph_id; - let bounds; - { - let state = self.0.read(); - glyph_id = state - .platform_text_system - .glyph_for_char(font_id, 'm') - .unwrap(); - bounds = state - .platform_text_system - .typographic_bounds(font_id, glyph_id) - .unwrap(); - } - self.em_size(font_id, font_size) * bounds.size.width - } - - pub fn em_advance(&self, font_id: FontId, font_size: Pixels) -> Pixels { - let glyph_id; - let advance; - { - let state = self.0.read(); - glyph_id = state - .platform_text_system - .glyph_for_char(font_id, 'm') - .unwrap(); - advance = state - .platform_text_system - .advance(font_id, glyph_id) - .unwrap(); - } - self.em_size(font_id, font_size) * advance.width - } - - pub fn line_height(&self, font_size: Pixels) -> Pixels { - (font_size * 1.618).round() - } - - pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.em_size(font_id, font_size) * self.read_metric(font_id, |m| m.cap_height) - } - - pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.em_size(font_id, font_size) * self.read_metric(font_id, |m| m.x_height) - } - - pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.em_size(font_id, font_size) * self.read_metric(font_id, |m| m.ascent) - } - - pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.em_size(font_id, font_size) * self.read_metric(font_id, |m| -m.descent) - } - - pub fn em_size(&self, font_id: FontId, font_size: Pixels) -> Pixels { - font_size / self.read_metric(font_id, |m| m.units_per_em as f32) - } - - pub fn baseline_offset(&self, font_id: FontId, font_size: Pixels) -> Pixels { - let line_height = self.line_height(font_size); - let ascent = self.ascent(font_id, font_size); - let descent = self.descent(font_id, font_size); - let padding_top = (line_height - ascent - descent) / 2.; - padding_top + ascent - } -} - -#[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, -} - -struct Family { - name: Arc, - font_features: FontFeatures, - font_ids: Vec, -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{FontStyle, FontWeight, Platform, TestPlatform}; - - #[test] - fn test_select_font() { - let platform = TestPlatform::new(); - let fonts = FontCache::new(platform.text_system()); - let arial = fonts - .load_family( - &["Arial"], - &FontFeatures { - calt: Some(false), - ..Default::default() - }, - ) - .unwrap(); - let arial_regular = fonts - .select_font(arial, FontWeight::default(), FontStyle::default()) - .unwrap(); - let arial_italic = fonts - .select_font(arial, FontWeight::default(), FontStyle::Italic) - .unwrap(); - let arial_bold = fonts - .select_font(arial, FontWeight::BOLD, FontStyle::default()) - .unwrap(); - assert_ne!(arial_regular, arial_italic); - assert_ne!(arial_regular, arial_bold); - assert_ne!(arial_italic, arial_bold); - - let arial_with_calt = fonts - .load_family( - &["Arial"], - &FontFeatures { - calt: Some(true), - ..Default::default() - }, - ) - .unwrap(); - assert_ne!(arial_with_calt, arial); - } -} diff --git a/crates/gpui3/src/text_system/text_layout_cache.rs b/crates/gpui3/src/text_system/text_layout_cache.rs index 8c3d0c1aa6..6956b5a53d 100644 --- a/crates/gpui3/src/text_system/text_layout_cache.rs +++ b/crates/gpui3/src/text_system/text_layout_cache.rs @@ -2,6 +2,7 @@ use crate::{ black, point, px, Bounds, FontId, Glyph, Hsla, LineLayout, Pixels, PlatformTextSystem, Point, Run, RunStyle, UnderlineStyle, WindowContext, }; +use anyhow::Result; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use smallvec::SmallVec; use std::{ @@ -262,79 +263,86 @@ impl Line { let mut underline = None; for run in &self.layout.runs { - let max_glyph_width = cx - .text_system() - .bounding_box(run.font_id, self.layout.font_size) - .width; + cx.text_system().with_font(run.font_id, |system, font| { + let max_glyph_width = cx + .text_system() + .bounding_box(font, self.layout.font_size)? + .size + .width; - for glyph in &run.glyphs { - let glyph_origin = origin + baseline_offset + glyph.position; - if glyph_origin.x > visible_bounds.upper_right().x { - break; - } - - let mut finished_underline: Option<(Point, UnderlineStyle)> = None; - if glyph.index >= run_end { - if let Some(style_run) = style_runs.next() { - if let Some((_, underline_style)) = &mut underline { - if style_run.underline != *underline_style { - finished_underline = underline.take(); - } - } - if style_run.underline.thickness > px(0.) { - underline.get_or_insert(( - point( - glyph_origin.x, - origin.y + baseline_offset.y + (self.layout.descent * 0.618), - ), - UnderlineStyle { - color: style_run.underline.color, - thickness: style_run.underline.thickness, - squiggly: style_run.underline.squiggly, - }, - )); - } - - run_end += style_run.len as usize; - color = style_run.color; - } else { - run_end = self.layout.len; - finished_underline = underline.take(); + for glyph in &run.glyphs { + let glyph_origin = origin + baseline_offset + glyph.position; + if glyph_origin.x > visible_bounds.upper_right().x { + break; } + + let mut finished_underline: Option<(Point, UnderlineStyle)> = None; + if glyph.index >= run_end { + if let Some(style_run) = style_runs.next() { + if let Some((_, underline_style)) = &mut underline { + if style_run.underline != *underline_style { + finished_underline = underline.take(); + } + } + if style_run.underline.thickness > px(0.) { + underline.get_or_insert(( + point( + glyph_origin.x, + origin.y + + baseline_offset.y + + (self.layout.descent * 0.618), + ), + UnderlineStyle { + color: style_run.underline.color, + thickness: style_run.underline.thickness, + squiggly: style_run.underline.squiggly, + }, + )); + } + + run_end += style_run.len as usize; + color = style_run.color; + } else { + run_end = self.layout.len; + finished_underline = underline.take(); + } + } + + if glyph_origin.x + max_glyph_width < visible_bounds.origin.x { + continue; + } + + if let Some((_underline_origin, _underline_style)) = finished_underline { + // cx.scene().insert(Underline { + // origin: underline_origin, + // width: glyph_origin.x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color.unwrap(), + // squiggly: underline_style.squiggly, + // }); + } + + // todo!() + // if glyph.is_emoji { + // cx.scene().push_image_glyph(scene::ImageGlyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_origin, + // }); + // } else { + // cx.scene().push_glyph(scene::Glyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_origin, + // color, + // }); + // } } - if glyph_origin.x + max_glyph_width < visible_bounds.origin.x { - continue; - } - - if let Some((_underline_origin, _underline_style)) = finished_underline { - // cx.scene().insert(Underline { - // origin: underline_origin, - // width: glyph_origin.x - underline_origin.x, - // thickness: underline_style.thickness.into(), - // color: underline_style.color.unwrap(), - // squiggly: underline_style.squiggly, - // }); - } - - // todo!() - // if glyph.is_emoji { - // cx.scene().push_image_glyph(scene::ImageGlyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_origin, - // }); - // } else { - // cx.scene().push_glyph(scene::Glyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_origin, - // color, - // }); - // } - } + anyhow::Ok(()) + }); } if let Some((_underline_start, _underline_style)) = underline.take() { @@ -356,7 +364,7 @@ impl Line { line_height: Pixels, boundaries: &[ShapedBoundary], cx: &mut WindowContext, - ) { + ) -> Result<()> { let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; let baseline_offset = point(px(0.), padding_top + self.layout.ascent); @@ -434,31 +442,32 @@ impl Line { // }); } - let _glyph_bounds = Bounds { - origin: glyph_origin, - size: cx - .text_system() - .bounding_box(run.font_id, self.layout.font_size), - }; - // todo!() - // if glyph_bounds.intersects(visible_bounds) { - // if glyph.is_emoji { - // cx.scene().push_image_glyph(scene::ImageGlyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_bounds.origin() + baseline_offset, - // }); - // } else { - // cx.scene().push_glyph(scene::Glyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_bounds.origin() + baseline_offset, - // color, - // }); - // } - // } + cx.text_system().with_font(run.font_id, |system, font| { + let _glyph_bounds = Bounds { + origin: glyph_origin, + size: system.bounding_box(font, self.layout.font_size)?.size, + }; + // todo!() + // if glyph_bounds.intersects(visible_bounds) { + // if glyph.is_emoji { + // cx.scene().push_image_glyph(scene::ImageGlyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_bounds.origin() + baseline_offset, + // }); + // } else { + // cx.scene().push_glyph(scene::Glyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_bounds.origin() + baseline_offset, + // color, + // }); + // } + // } + anyhow::Ok(()) + })?; } } @@ -472,6 +481,8 @@ impl Line { // squiggly: underline_style.squiggly, // }); } + + Ok(()) } } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 1d014197e1..0804ed7878 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -137,6 +137,7 @@ tree-sitter-nu.workspace = true url = "2.2" urlencoding = "2.1.2" uuid = { version = "1.1.2", features = ["v4"] } +owning_ref = "0.4.1" [dev-dependencies] call = { path = "../call", features = ["test-support"] }