diff --git a/assets/settings/default.json b/assets/settings/default.json index fc63b49690..9335f86c75 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -28,7 +28,7 @@ "buffer_font_family": "Zed Plex Mono", // Set the buffer text's font fallbacks, this will be merged with // the platform's default fallbacks. - "buffer_font_fallbacks": [], + "buffer_font_fallbacks": null, // The OpenType features to enable for text in the editor. "buffer_font_features": { // Disable ligatures: @@ -54,7 +54,7 @@ "ui_font_family": "Zed Plex Sans", // Set the UI's font fallbacks, this will be merged with the platform's // default font fallbacks. - "ui_font_fallbacks": [], + "ui_font_fallbacks": null, // The OpenType features to enable for text in the UI "ui_font_features": { // Disable ligatures: diff --git a/crates/gpui/src/platform/mac/open_type.rs b/crates/gpui/src/platform/mac/open_type.rs index 87e2543521..1e19fc22b8 100644 --- a/crates/gpui/src/platform/mac/open_type.rs +++ b/crates/gpui/src/platform/mac/open_type.rs @@ -35,50 +35,25 @@ pub fn apply_features_and_fallbacks( fallbacks: Option<&FontFallbacks>, ) -> anyhow::Result<()> { unsafe { - let fallback_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - + let mut keys = vec![kCTFontFeatureSettingsAttribute]; + let mut values = vec![generate_feature_array(features)]; if let Some(fallbacks) = fallbacks { - for user_fallback in fallbacks.fallback_list() { - let name = CFString::from(user_fallback.as_str()); - let fallback_desc = - CTFontDescriptorCreateWithNameAndSize(name.as_concrete_TypeRef(), 0.0); - CFArrayAppendValue(fallback_array, fallback_desc as _); - CFRelease(fallback_desc as _); + if !fallbacks.fallback_list().is_empty() { + keys.push(kCTFontCascadeListAttribute); + values.push(generate_fallback_array( + fallbacks, + font.native_font().as_concrete_TypeRef(), + )); } } - - { - let preferred_languages: CFArray = - CFArray::wrap_under_create_rule(CFLocaleCopyPreferredLanguages()); - - let default_fallbacks = CTFontCopyDefaultCascadeListForLanguages( - font.native_font().as_concrete_TypeRef(), - preferred_languages.as_concrete_TypeRef(), - ); - let default_fallbacks: CFArray = - CFArray::wrap_under_create_rule(default_fallbacks); - - default_fallbacks - .iter() - .filter(|desc| desc.font_path().is_some()) - .map(|desc| { - CFArrayAppendValue(fallback_array, desc.as_concrete_TypeRef() as _); - }); - } - - let feature_array = generate_feature_array(features); - let keys = [kCTFontFeatureSettingsAttribute, kCTFontCascadeListAttribute]; - let values = [feature_array, fallback_array]; let attrs = CFDictionaryCreate( kCFAllocatorDefault, keys.as_ptr() as _, values.as_ptr() as _, - 2, + keys.len() as isize, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks, ); - CFRelease(feature_array as *const _ as _); - CFRelease(fallback_array as *const _ as _); let new_descriptor = CTFontDescriptorCreateWithAttributes(attrs); CFRelease(attrs as _); let new_descriptor = CTFontDescriptor::wrap_under_create_rule(new_descriptor); @@ -97,8 +72,7 @@ pub fn apply_features_and_fallbacks( fn generate_feature_array(features: &FontFeatures) -> CFMutableArrayRef { unsafe { - let mut feature_array = - CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + let feature_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); for (tag, value) in features.tag_value_list() { let keys = [kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue]; let values = [ @@ -121,6 +95,42 @@ fn generate_feature_array(features: &FontFeatures) -> CFMutableArrayRef { } } +fn generate_fallback_array(fallbacks: &FontFallbacks, font_ref: CTFontRef) -> CFMutableArrayRef { + unsafe { + let fallback_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + for user_fallback in fallbacks.fallback_list() { + let name = CFString::from(user_fallback.as_str()); + let fallback_desc = + CTFontDescriptorCreateWithNameAndSize(name.as_concrete_TypeRef(), 0.0); + CFArrayAppendValue(fallback_array, fallback_desc as _); + CFRelease(fallback_desc as _); + } + append_system_fallbacks(fallback_array, font_ref); + fallback_array + } +} + +fn append_system_fallbacks(fallback_array: CFMutableArrayRef, font_ref: CTFontRef) { + unsafe { + let preferred_languages: CFArray = + CFArray::wrap_under_create_rule(CFLocaleCopyPreferredLanguages()); + + let default_fallbacks = CTFontCopyDefaultCascadeListForLanguages( + font_ref, + preferred_languages.as_concrete_TypeRef(), + ); + let default_fallbacks: CFArray = + CFArray::wrap_under_create_rule(default_fallbacks); + + default_fallbacks + .iter() + .filter(|desc| desc.font_path().is_some()) + .map(|desc| { + CFArrayAppendValue(fallback_array, desc.as_concrete_TypeRef() as _); + }); + } +} + #[link(name = "CoreText", kind = "framework")] extern "C" { static kCTFontOpenTypeFeatureTag: CFStringRef; diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 5c33393795..6253881f5a 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -275,54 +275,52 @@ impl DirectWriteState { fn generate_font_fallbacks( &self, - fallbacks: Option<&FontFallbacks>, + fallbacks: &FontFallbacks, ) -> Result> { - if fallbacks.is_some_and(|fallbacks| fallbacks.fallback_list().is_empty()) { + if fallbacks.fallback_list().is_empty() { return Ok(None); } unsafe { let builder = self.components.factory.CreateFontFallbackBuilder()?; let font_set = &self.system_font_collection.GetFontSet()?; - if let Some(fallbacks) = fallbacks { - for family_name in fallbacks.fallback_list() { - let Some(fonts) = font_set - .GetMatchingFonts( - &HSTRING::from(family_name), - DWRITE_FONT_WEIGHT_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, - DWRITE_FONT_STYLE_NORMAL, - ) - .log_err() - else { - continue; - }; - if fonts.GetFontCount() == 0 { - log::error!("No matching font found for {}", family_name); - continue; - } - let font = fonts.GetFontFaceReference(0)?.CreateFontFace()?; - let mut count = 0; - font.GetUnicodeRanges(None, &mut count).ok(); - if count == 0 { - continue; - } - let mut unicode_ranges = vec![DWRITE_UNICODE_RANGE::default(); count as usize]; - let Some(_) = font - .GetUnicodeRanges(Some(&mut unicode_ranges), &mut count) - .log_err() - else { - continue; - }; - let target_family_name = HSTRING::from(family_name); - builder.AddMapping( - &unicode_ranges, - &[target_family_name.as_ptr()], - None, - None, - None, - 1.0, - )?; + for family_name in fallbacks.fallback_list() { + let Some(fonts) = font_set + .GetMatchingFonts( + &HSTRING::from(family_name), + DWRITE_FONT_WEIGHT_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + DWRITE_FONT_STYLE_NORMAL, + ) + .log_err() + else { + continue; + }; + if fonts.GetFontCount() == 0 { + log::error!("No matching font found for {}", family_name); + continue; } + let font = fonts.GetFontFaceReference(0)?.CreateFontFace()?; + let mut count = 0; + font.GetUnicodeRanges(None, &mut count).ok(); + if count == 0 { + continue; + } + let mut unicode_ranges = vec![DWRITE_UNICODE_RANGE::default(); count as usize]; + let Some(_) = font + .GetUnicodeRanges(Some(&mut unicode_ranges), &mut count) + .log_err() + else { + continue; + }; + let target_family_name = HSTRING::from(family_name); + builder.AddMapping( + &unicode_ranges, + &[target_family_name.as_ptr()], + None, + None, + None, + 1.0, + )?; } let system_fallbacks = self.components.factory.GetSystemFontFallback()?; builder.AddMappings(&system_fallbacks)?; @@ -378,10 +376,8 @@ impl DirectWriteState { else { continue; }; - let fallbacks = self - .generate_font_fallbacks(font_fallbacks) - .log_err() - .unwrap_or_default(); + let fallbacks = font_fallbacks + .and_then(|fallbacks| self.generate_font_fallbacks(fallbacks).log_err().flatten()); let font_info = FontInfo { font_family: family_name.to_owned(), font_face,