diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 61782fbe50e26a089eefe3c11e70a0016909f6b3..b3812bb7cb5747ff40bd6d05a39b9ee7bebbdda1 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -144,7 +144,7 @@ windows = { version = "0.61", features = ["Win32_Foundation"] } backtrace.workspace = true collections = { workspace = true, features = ["test-support"] } env_logger.workspace = true -gpui_platform.workspace = true +gpui_platform = { workspace = true, features = ["font-kit"] } lyon = { version = "1.0", features = ["extra"] } rand.workspace = true scheduler = { workspace = true, features = ["test-support"] } diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index acaf4fe83a49726e0a3c641ca577bf75c54e224d..50fdef9beaec03b5c6d6f8ed710ff992f6918018 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -1,6 +1,7 @@ #![cfg_attr(target_family = "wasm", no_main)] use std::{ + borrow::Cow, ops::{Deref, DerefMut}, sync::Arc, }; @@ -204,7 +205,7 @@ impl RenderOnce for CharacterGrid { "❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎", ]; - let columns = 11; + let columns = 20; let rows = characters.len().div_ceil(columns); let grid_rows = (0..rows).map(|row_idx| { @@ -238,6 +239,7 @@ impl RenderOnce for CharacterGrid { struct TextExample { next_id: usize, + font_family: SharedString, } impl TextExample { @@ -245,8 +247,33 @@ impl TextExample { self.next_id += 1; self.next_id } + + fn button( + text: &str, + cx: &mut Context, + on_click: impl Fn(&mut Self, &mut Context) + 'static, + ) -> impl IntoElement { + div() + .id(text.to_string()) + .flex_none() + .child(text.to_string()) + .bg(gpui::black()) + .text_color(gpui::white()) + .active(|this| this.opacity(0.8)) + .px_3() + .py_1() + .on_click(cx.listener(move |this, _, _, cx| on_click(this, cx))) + } } +const FONT_FAMILIES: [&str; 5] = [ + ".ZedMono", + ".SystemUIFont", + "Menlo", + "Monaco", + "Courier New", +]; + impl Render for TextExample { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let tcx = cx.text_context(); @@ -265,7 +292,26 @@ impl Render for TextExample { let step_up_6 = step_up_5 * type_scale; div() + .font_family(self.font_family.clone()) .size_full() + .child( + div() + .bg(gpui::white()) + .border_b_1() + .border_color(gpui::black()) + .p_3() + .flex() + .child(Self::button(&self.font_family, cx, |this, cx| { + let new_family = FONT_FAMILIES + .iter() + .position(|f| *f == this.font_family.as_str()) + .map(|idx| FONT_FAMILIES[(idx + 1) % FONT_FAMILIES.len()]) + .unwrap_or(FONT_FAMILIES[0]); + + this.font_family = SharedString::new(new_family); + cx.notify(); + })), + ) .child( div() .id("text-example") @@ -307,6 +353,15 @@ fn run_example() { items: vec![], }]); + let fonts = [include_bytes!( + "../../../assets/fonts/lilex/Lilex-Regular.ttf" + )] + .iter() + .map(|b| Cow::Borrowed(&b[..])) + .collect(); + + _ = cx.text_system().add_fonts(fonts); + cx.init_colors(); cx.set_global(GlobalTextContext(Arc::new(TextContext::default()))); @@ -323,7 +378,12 @@ fn run_example() { ))), ..Default::default() }, - |_window, cx| cx.new(|_cx| TextExample { next_id: 0 }), + |_window, cx| { + cx.new(|_cx| TextExample { + next_id: 0, + font_family: ".ZedMono".into(), + }) + }, ) .unwrap(); diff --git a/crates/gpui_macos/src/text_system.rs b/crates/gpui_macos/src/text_system.rs index e0f8a010eadf422ce588d8a7d30b3db6f9a4dcee..d4ffd2514e3ed1a7616cce9bb44cea0b06ab56f3 100644 --- a/crates/gpui_macos/src/text_system.rs +++ b/crates/gpui_macos/src/text_system.rs @@ -361,13 +361,22 @@ impl MacTextSystemState { fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { let font = &self.fonts[params.font_id.0]; let scale = Transform2F::from_scale(params.scale_factor); - Ok(bounds_from_rect_i(font.raster_bounds( + let mut bounds: Bounds = bounds_from_rect_i(font.raster_bounds( params.glyph_id.0, params.font_size.into(), scale, HintingOptions::None, font_kit::canvas::RasterizationOptions::GrayscaleAa, - )?)) + )?); + + // Add 3% of font size as padding, clamped between 1 and 5 pixels + // to avoid clipping of anti-aliased edges. + let pad = + ((params.font_size.as_f32() * 0.03 * params.scale_factor).ceil() as i32).clamp(1, 5); + bounds.origin.x -= DevicePixels(pad); + bounds.size.width += DevicePixels(pad); + + Ok(bounds) } fn rasterize_glyph(