mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 18:41:56 +03:00
Feature/fallback fonts (#15306)
Supersedes https://github.com/zed-industries/zed/pull/12090 fixes #5180 fixes #5055 See original PR for an example of the feature at work. This PR changes the settings interface to be backwards compatible, and adds the `ui_font_fallbacks`, `buffer_font_fallbacks`, and `terminal.font_fallbacks` settings. Release Notes: - Added support for font fallbacks via three new settings: `ui_font_fallbacks`, `buffer_font_fallbacks`, and `terminal.font_fallbacks`.(#5180, #5055). --------- Co-authored-by: Junkui Zhang <364772080@qq.com>
This commit is contained in:
parent
3e31955b7f
commit
a1bd7a1297
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4866,6 +4866,7 @@ dependencies = [
|
||||
"cocoa",
|
||||
"collections",
|
||||
"core-foundation",
|
||||
"core-foundation-sys 0.8.6",
|
||||
"core-graphics",
|
||||
"core-text",
|
||||
"cosmic-text",
|
||||
|
@ -26,6 +26,9 @@
|
||||
},
|
||||
// The name of a font to use for rendering text in the editor
|
||||
"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": [],
|
||||
// The OpenType features to enable for text in the editor.
|
||||
"buffer_font_features": {
|
||||
// Disable ligatures:
|
||||
@ -47,8 +50,11 @@
|
||||
// },
|
||||
"buffer_line_height": "comfortable",
|
||||
// The name of a font to use for rendering text in the UI
|
||||
// (On macOS) You can set this to ".SystemUIFont" to use the system font
|
||||
// You can set this to ".SystemUIFont" to use the system font
|
||||
"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": [],
|
||||
// The OpenType features to enable for text in the UI
|
||||
"ui_font_features": {
|
||||
// Disable ligatures:
|
||||
@ -675,6 +681,10 @@
|
||||
// Set the terminal's font family. If this option is not included,
|
||||
// the terminal will default to matching the buffer's font family.
|
||||
// "font_family": "Zed Plex Mono",
|
||||
// Set the terminal's font fallbacks. If this option is not included,
|
||||
// the terminal will default to matching the buffer's font fallbacks.
|
||||
// This will be merged with the platform's default font fallbacks
|
||||
// "font_fallbacks": ["FiraCode Nerd Fonts"],
|
||||
// Sets the maximum number of lines in the terminal's scrollback buffer.
|
||||
// Default: 10_000, maximum: 100_000 (all bigger values set will be treated as 100_000), 0 disables the scrolling.
|
||||
// Existing terminals will not pick up this change until they are recreated.
|
||||
|
@ -1123,16 +1123,17 @@ impl Context {
|
||||
.timer(Duration::from_millis(200))
|
||||
.await;
|
||||
|
||||
let token_count = cx
|
||||
.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
|
||||
})?
|
||||
.await?;
|
||||
if let Some(token_count) = cx.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
|
||||
})? {
|
||||
let token_count = token_count.await?;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.token_count = Some(token_count);
|
||||
cx.notify()
|
||||
})?;
|
||||
}
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.token_count = Some(token_count);
|
||||
cx.notify()
|
||||
})?;
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.log_err()
|
||||
|
@ -1635,15 +1635,18 @@ impl PromptEditor {
|
||||
})?
|
||||
.await?;
|
||||
|
||||
let token_count = cx
|
||||
.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
|
||||
})?
|
||||
.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.token_count = Some(token_count);
|
||||
cx.notify();
|
||||
})
|
||||
if let Some(token_count) = cx.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
|
||||
})? {
|
||||
let token_count = token_count.await?;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.token_count = Some(token_count);
|
||||
cx.notify();
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1832,6 +1835,7 @@ impl PromptEditor {
|
||||
},
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
line_height: relative(1.3),
|
||||
|
@ -734,26 +734,29 @@ impl PromptLibrary {
|
||||
const DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
cx.background_executor().timer(DEBOUNCE_TIMEOUT).await;
|
||||
let token_count = cx
|
||||
.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(
|
||||
LanguageModelRequest {
|
||||
messages: vec![LanguageModelRequestMessage {
|
||||
role: Role::System,
|
||||
content: body.to_string(),
|
||||
}],
|
||||
stop: Vec::new(),
|
||||
temperature: 1.,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let prompt_editor = this.prompt_editors.get_mut(&prompt_id).unwrap();
|
||||
prompt_editor.token_count = Some(token_count);
|
||||
cx.notify();
|
||||
})
|
||||
if let Some(token_count) = cx.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(
|
||||
LanguageModelRequest {
|
||||
messages: vec![LanguageModelRequestMessage {
|
||||
role: Role::System,
|
||||
content: body.to_string(),
|
||||
}],
|
||||
stop: Vec::new(),
|
||||
temperature: 1.,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})? {
|
||||
let token_count = token_count.await?;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let prompt_editor = this.prompt_editors.get_mut(&prompt_id).unwrap();
|
||||
prompt_editor.token_count = Some(token_count);
|
||||
cx.notify();
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
.log_err()
|
||||
});
|
||||
|
@ -707,15 +707,18 @@ impl PromptEditor {
|
||||
inline_assistant.request_for_inline_assist(assist_id, cx)
|
||||
})??;
|
||||
|
||||
let token_count = cx
|
||||
.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
|
||||
})?
|
||||
.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.token_count = Some(token_count);
|
||||
cx.notify();
|
||||
})
|
||||
if let Some(token_count) = cx.update(|cx| {
|
||||
LanguageModelCompletionProvider::read_global(cx).count_tokens(request, cx)
|
||||
})? {
|
||||
let token_count = token_count.await?;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.token_count = Some(token_count);
|
||||
cx.notify();
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -906,6 +909,7 @@ impl PromptEditor {
|
||||
},
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
line_height: relative(1.3),
|
||||
|
@ -533,6 +533,7 @@ impl Render for MessageEditor {
|
||||
},
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: TextSize::Small.rems(cx).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
font_style: FontStyle::Normal,
|
||||
|
@ -2190,6 +2190,7 @@ impl CollabPanel {
|
||||
},
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
font_style: FontStyle::Normal,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
|
||||
use futures::{future::BoxFuture, stream::BoxStream, StreamExt};
|
||||
use gpui::{AppContext, Global, Model, ModelContext, Task};
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry,
|
||||
@ -143,11 +143,11 @@ impl LanguageModelCompletionProvider {
|
||||
&self,
|
||||
request: LanguageModelRequest,
|
||||
cx: &AppContext,
|
||||
) -> BoxFuture<'static, Result<usize>> {
|
||||
) -> Option<BoxFuture<'static, Result<usize>>> {
|
||||
if let Some(model) = self.active_model() {
|
||||
model.count_tokens(request, cx)
|
||||
Some(model.count_tokens(request, cx))
|
||||
} else {
|
||||
std::future::ready(Err(anyhow!("No active model set"))).boxed()
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12430,6 +12430,7 @@ impl Render for Editor {
|
||||
color: cx.theme().colors().editor_foreground,
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
line_height: relative(settings.buffer_line_height.value()),
|
||||
@ -12439,6 +12440,7 @@ impl Render for Editor {
|
||||
color: cx.theme().colors().editor_foreground,
|
||||
font_family: settings.buffer_font.family.clone(),
|
||||
font_features: settings.buffer_font.features.clone(),
|
||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||
font_size: settings.buffer_font_size(cx).into(),
|
||||
font_weight: settings.buffer_font.weight,
|
||||
line_height: relative(settings.buffer_line_height.value()),
|
||||
|
@ -27,6 +27,7 @@ pub fn marked_display_snapshot(
|
||||
let font = Font {
|
||||
family: "Zed Plex Mono".into(),
|
||||
features: FontFeatures::default(),
|
||||
fallbacks: None,
|
||||
weight: FontWeight::default(),
|
||||
style: FontStyle::default(),
|
||||
};
|
||||
|
@ -816,6 +816,7 @@ impl ExtensionsPage {
|
||||
},
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
line_height: relative(1.3),
|
||||
|
@ -93,6 +93,7 @@ cbindgen = { version = "0.26.0", default-features = false }
|
||||
block = "0.1"
|
||||
cocoa.workspace = true
|
||||
core-foundation.workspace = true
|
||||
core-foundation-sys = "0.8"
|
||||
core-graphics = "0.23"
|
||||
core-text = "20.1"
|
||||
foreign-types = "0.5"
|
||||
|
@ -1,10 +1,12 @@
|
||||
#![allow(unused, non_upper_case_globals)]
|
||||
|
||||
use crate::FontFeatures;
|
||||
use crate::{FontFallbacks, FontFeatures};
|
||||
use cocoa::appkit::CGFloat;
|
||||
use core_foundation::{
|
||||
array::{
|
||||
kCFTypeArrayCallBacks, CFArray, CFArrayAppendValue, CFArrayCreateMutable, CFMutableArrayRef,
|
||||
kCFTypeArrayCallBacks, CFArray, CFArrayAppendArray, CFArrayAppendValue,
|
||||
CFArrayCreateMutable, CFArrayGetCount, CFArrayGetValueAtIndex, CFArrayRef,
|
||||
CFMutableArrayRef,
|
||||
},
|
||||
base::{kCFAllocatorDefault, CFRelease, TCFType},
|
||||
dictionary::{
|
||||
@ -13,21 +15,88 @@ use core_foundation::{
|
||||
number::CFNumber,
|
||||
string::{CFString, CFStringRef},
|
||||
};
|
||||
use core_foundation_sys::locale::CFLocaleCopyPreferredLanguages;
|
||||
use core_graphics::{display::CFDictionary, geometry::CGAffineTransform};
|
||||
use core_text::{
|
||||
font::{CTFont, CTFontRef},
|
||||
font::{cascade_list_for_languages, CTFont, CTFontRef},
|
||||
font_descriptor::{
|
||||
kCTFontFeatureSettingsAttribute, CTFontDescriptor, CTFontDescriptorCopyAttributes,
|
||||
CTFontDescriptorCreateCopyWithFeature, CTFontDescriptorCreateWithAttributes,
|
||||
kCTFontCascadeListAttribute, kCTFontFeatureSettingsAttribute, CTFontDescriptor,
|
||||
CTFontDescriptorCopyAttributes, CTFontDescriptorCreateCopyWithFeature,
|
||||
CTFontDescriptorCreateWithAttributes, CTFontDescriptorCreateWithNameAndSize,
|
||||
CTFontDescriptorRef,
|
||||
},
|
||||
};
|
||||
use font_kit::font::Font;
|
||||
use font_kit::font::Font as FontKitFont;
|
||||
use std::ptr;
|
||||
|
||||
pub fn apply_features(font: &mut Font, features: &FontFeatures) {
|
||||
pub fn apply_features_and_fallbacks(
|
||||
font: &mut FontKitFont,
|
||||
features: &FontFeatures,
|
||||
fallbacks: Option<&FontFallbacks>,
|
||||
) -> anyhow::Result<()> {
|
||||
unsafe {
|
||||
let fallback_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
||||
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let preferred_languages: CFArray<CFString> =
|
||||
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<CTFontDescriptor> =
|
||||
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,
|
||||
&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);
|
||||
let new_font = CTFontCreateCopyWithAttributes(
|
||||
font.native_font().as_concrete_TypeRef(),
|
||||
0.0,
|
||||
std::ptr::null(),
|
||||
new_descriptor.as_concrete_TypeRef(),
|
||||
);
|
||||
let new_font = CTFont::wrap_under_create_rule(new_font);
|
||||
*font = font_kit::font::Font::from_native_font(&new_font);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_feature_array(features: &FontFeatures) -> CFMutableArrayRef {
|
||||
unsafe {
|
||||
let native_font = font.native_font();
|
||||
let mut feature_array =
|
||||
CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
||||
for (tag, value) in features.tag_value_list() {
|
||||
@ -48,26 +117,7 @@ pub fn apply_features(font: &mut Font, features: &FontFeatures) {
|
||||
CFArrayAppendValue(feature_array, dict as _);
|
||||
CFRelease(dict as _);
|
||||
}
|
||||
let attrs = CFDictionaryCreate(
|
||||
kCFAllocatorDefault,
|
||||
&kCTFontFeatureSettingsAttribute as *const _ as _,
|
||||
&feature_array as *const _ as _,
|
||||
1,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks,
|
||||
);
|
||||
CFRelease(feature_array as *const _ as _);
|
||||
let new_descriptor = CTFontDescriptorCreateWithAttributes(attrs);
|
||||
CFRelease(attrs as _);
|
||||
let new_descriptor = CTFontDescriptor::wrap_under_create_rule(new_descriptor);
|
||||
let new_font = CTFontCreateCopyWithAttributes(
|
||||
font.native_font().as_concrete_TypeRef(),
|
||||
0.0,
|
||||
ptr::null(),
|
||||
new_descriptor.as_concrete_TypeRef(),
|
||||
);
|
||||
let new_font = CTFont::wrap_under_create_rule(new_font);
|
||||
*font = Font::from_native_font(&new_font);
|
||||
feature_array
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,4 +132,8 @@ extern "C" {
|
||||
matrix: *const CGAffineTransform,
|
||||
attributes: CTFontDescriptorRef,
|
||||
) -> CTFontRef;
|
||||
fn CTFontCopyDefaultCascadeListForLanguages(
|
||||
font: CTFontRef,
|
||||
languagePrefList: CFArrayRef,
|
||||
) -> CFArrayRef;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontRun,
|
||||
FontStyle, FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point,
|
||||
point, px, size, Bounds, DevicePixels, Font, FontFallbacks, FontFeatures, FontId, FontMetrics,
|
||||
FontRun, FontStyle, FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point,
|
||||
RenderGlyphParams, Result, ShapedGlyph, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
@ -43,7 +43,7 @@ use pathfinder_geometry::{
|
||||
use smallvec::SmallVec;
|
||||
use std::{borrow::Cow, char, cmp, convert::TryFrom, sync::Arc};
|
||||
|
||||
use super::open_type;
|
||||
use super::open_type::apply_features_and_fallbacks;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const kCGImageAlphaOnly: u32 = 7;
|
||||
@ -54,6 +54,7 @@ pub(crate) struct MacTextSystem(RwLock<MacTextSystemState>);
|
||||
struct FontKey {
|
||||
font_family: SharedString,
|
||||
font_features: FontFeatures,
|
||||
font_fallbacks: Option<FontFallbacks>,
|
||||
}
|
||||
|
||||
struct MacTextSystemState {
|
||||
@ -123,11 +124,13 @@ impl PlatformTextSystem for MacTextSystem {
|
||||
let font_key = FontKey {
|
||||
font_family: font.family.clone(),
|
||||
font_features: font.features.clone(),
|
||||
font_fallbacks: font.fallbacks.clone(),
|
||||
};
|
||||
let candidates = if let Some(font_ids) = lock.font_ids_by_font_key.get(&font_key) {
|
||||
font_ids.as_slice()
|
||||
} else {
|
||||
let font_ids = lock.load_family(&font.family, &font.features)?;
|
||||
let font_ids =
|
||||
lock.load_family(&font.family, &font.features, font.fallbacks.as_ref())?;
|
||||
lock.font_ids_by_font_key.insert(font_key.clone(), font_ids);
|
||||
lock.font_ids_by_font_key[&font_key].as_ref()
|
||||
};
|
||||
@ -212,6 +215,7 @@ impl MacTextSystemState {
|
||||
&mut self,
|
||||
name: &str,
|
||||
features: &FontFeatures,
|
||||
fallbacks: Option<&FontFallbacks>,
|
||||
) -> Result<SmallVec<[FontId; 4]>> {
|
||||
let name = if name == ".SystemUIFont" {
|
||||
".AppleSystemUIFont"
|
||||
@ -227,8 +231,7 @@ impl MacTextSystemState {
|
||||
for font in family.fonts() {
|
||||
let mut font = font.load()?;
|
||||
|
||||
open_type::apply_features(&mut font, features);
|
||||
|
||||
apply_features_and_fallbacks(&mut font, features, fallbacks)?;
|
||||
// This block contains a precautionary fix to guard against loading fonts
|
||||
// that might cause panics due to `.unwrap()`s up the chain.
|
||||
{
|
||||
@ -457,6 +460,7 @@ impl MacTextSystemState {
|
||||
CFRange::init(utf16_start as isize, (utf16_end - utf16_start) as isize);
|
||||
|
||||
let font: &FontKitFont = &self.fonts[run.font_id.0];
|
||||
|
||||
unsafe {
|
||||
string.set_attribute(
|
||||
cf_range,
|
||||
@ -634,7 +638,7 @@ impl From<FontStyle> for FontkitStyle {
|
||||
}
|
||||
}
|
||||
|
||||
// Some fonts may have no attributest despite `core_text` requiring them (and panicking).
|
||||
// Some fonts may have no attributes despite `core_text` requiring them (and panicking).
|
||||
// This is the same version as `core_text` has without `expect` calls.
|
||||
mod lenient_font_attributes {
|
||||
use core_foundation::{
|
||||
|
@ -30,6 +30,7 @@ struct FontInfo {
|
||||
font_family: String,
|
||||
font_face: IDWriteFontFace3,
|
||||
features: IDWriteTypography,
|
||||
fallbacks: Option<IDWriteFontFallback>,
|
||||
is_system_font: bool,
|
||||
is_emoji: bool,
|
||||
}
|
||||
@ -287,6 +288,63 @@ impl DirectWriteState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_font_fallbacks(
|
||||
&self,
|
||||
fallbacks: Option<&FontFallbacks>,
|
||||
) -> Result<Option<IDWriteFontFallback>> {
|
||||
if fallbacks.is_some_and(|fallbacks| 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 mathcing font find 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)?;
|
||||
Ok(Some(builder.CreateFontFallback()?))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn generate_font_features(
|
||||
&self,
|
||||
font_features: &FontFeatures,
|
||||
@ -302,6 +360,7 @@ impl DirectWriteState {
|
||||
font_weight: FontWeight,
|
||||
font_style: FontStyle,
|
||||
font_features: &FontFeatures,
|
||||
font_fallbacks: Option<&FontFallbacks>,
|
||||
is_system_font: bool,
|
||||
) -> Option<FontId> {
|
||||
let collection = if is_system_font {
|
||||
@ -334,11 +393,16 @@ impl DirectWriteState {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let fallbacks = self
|
||||
.generate_font_fallbacks(font_fallbacks)
|
||||
.log_err()
|
||||
.unwrap_or_default();
|
||||
let font_info = FontInfo {
|
||||
font_family: family_name.to_owned(),
|
||||
font_face,
|
||||
is_system_font,
|
||||
features: direct_write_features,
|
||||
fallbacks,
|
||||
is_system_font,
|
||||
is_emoji,
|
||||
};
|
||||
let font_id = FontId(self.fonts.len());
|
||||
@ -371,6 +435,7 @@ impl DirectWriteState {
|
||||
target_font.weight,
|
||||
target_font.style,
|
||||
&target_font.features,
|
||||
target_font.fallbacks.as_ref(),
|
||||
)
|
||||
.unwrap()
|
||||
} else {
|
||||
@ -379,6 +444,7 @@ impl DirectWriteState {
|
||||
target_font.weight,
|
||||
target_font.style,
|
||||
&target_font.features,
|
||||
target_font.fallbacks.as_ref(),
|
||||
)
|
||||
.unwrap_or_else(|| {
|
||||
let family = self.system_ui_font_name.clone();
|
||||
@ -388,6 +454,7 @@ impl DirectWriteState {
|
||||
target_font.weight,
|
||||
target_font.style,
|
||||
&target_font.features,
|
||||
target_font.fallbacks.as_ref(),
|
||||
true,
|
||||
)
|
||||
.unwrap()
|
||||
@ -402,16 +469,38 @@ impl DirectWriteState {
|
||||
weight: FontWeight,
|
||||
style: FontStyle,
|
||||
features: &FontFeatures,
|
||||
fallbacks: Option<&FontFallbacks>,
|
||||
) -> Option<FontId> {
|
||||
// try to find target font in custom font collection first
|
||||
self.get_font_id_from_font_collection(family_name, weight, style, features, false)
|
||||
.or_else(|| {
|
||||
self.get_font_id_from_font_collection(family_name, weight, style, features, true)
|
||||
})
|
||||
.or_else(|| {
|
||||
self.update_system_font_collection();
|
||||
self.get_font_id_from_font_collection(family_name, weight, style, features, true)
|
||||
})
|
||||
self.get_font_id_from_font_collection(
|
||||
family_name,
|
||||
weight,
|
||||
style,
|
||||
features,
|
||||
fallbacks,
|
||||
false,
|
||||
)
|
||||
.or_else(|| {
|
||||
self.get_font_id_from_font_collection(
|
||||
family_name,
|
||||
weight,
|
||||
style,
|
||||
features,
|
||||
fallbacks,
|
||||
true,
|
||||
)
|
||||
})
|
||||
.or_else(|| {
|
||||
self.update_system_font_collection();
|
||||
self.get_font_id_from_font_collection(
|
||||
family_name,
|
||||
weight,
|
||||
style,
|
||||
features,
|
||||
fallbacks,
|
||||
true,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn layout_line(
|
||||
@ -440,15 +529,22 @@ impl DirectWriteState {
|
||||
} else {
|
||||
&self.custom_font_collection
|
||||
};
|
||||
let format = self.components.factory.CreateTextFormat(
|
||||
&HSTRING::from(&font_info.font_family),
|
||||
collection,
|
||||
font_info.font_face.GetWeight(),
|
||||
font_info.font_face.GetStyle(),
|
||||
DWRITE_FONT_STRETCH_NORMAL,
|
||||
font_size.0,
|
||||
&HSTRING::from(&self.components.locale),
|
||||
)?;
|
||||
let format: IDWriteTextFormat1 = self
|
||||
.components
|
||||
.factory
|
||||
.CreateTextFormat(
|
||||
&HSTRING::from(&font_info.font_family),
|
||||
collection,
|
||||
font_info.font_face.GetWeight(),
|
||||
font_info.font_face.GetStyle(),
|
||||
DWRITE_FONT_STRETCH_NORMAL,
|
||||
font_size.0,
|
||||
&HSTRING::from(&self.components.locale),
|
||||
)?
|
||||
.cast()?;
|
||||
if let Some(ref fallbacks) = font_info.fallbacks {
|
||||
format.SetFontFallback(fallbacks)?;
|
||||
}
|
||||
|
||||
let layout = self.components.factory.CreateTextLayout(
|
||||
&text_wide,
|
||||
@ -1183,6 +1279,7 @@ fn get_font_identifier_and_font_struct(
|
||||
features: FontFeatures::default(),
|
||||
weight: weight.into(),
|
||||
style: style.into(),
|
||||
fallbacks: None,
|
||||
};
|
||||
let is_emoji = unsafe { font_face.IsColorFont().as_bool() };
|
||||
Some((identifier, font_struct, is_emoji))
|
||||
|
@ -1,4 +1,5 @@
|
||||
use derive_more::{Deref, DerefMut};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{borrow::Borrow, sync::Arc};
|
||||
use util::arc_cow::ArcCow;
|
||||
|
@ -6,9 +6,9 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
|
||||
CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, FontStyle, FontWeight,
|
||||
Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size, SizeRefinement, Styled,
|
||||
TextRun, WindowContext,
|
||||
CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font, FontFallbacks, FontFeatures,
|
||||
FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size,
|
||||
SizeRefinement, Styled, TextRun, WindowContext,
|
||||
};
|
||||
use collections::HashSet;
|
||||
use refineable::Refineable;
|
||||
@ -180,6 +180,9 @@ pub struct TextStyle {
|
||||
/// The font features to use
|
||||
pub font_features: FontFeatures,
|
||||
|
||||
/// The fallback fonts to use
|
||||
pub font_fallbacks: Option<FontFallbacks>,
|
||||
|
||||
/// The font size to use, in pixels or rems.
|
||||
pub font_size: AbsoluteLength,
|
||||
|
||||
@ -218,6 +221,7 @@ impl Default for TextStyle {
|
||||
"Helvetica".into()
|
||||
},
|
||||
font_features: FontFeatures::default(),
|
||||
font_fallbacks: None,
|
||||
font_size: rems(1.).into(),
|
||||
line_height: phi(),
|
||||
font_weight: FontWeight::default(),
|
||||
@ -269,6 +273,7 @@ impl TextStyle {
|
||||
Font {
|
||||
family: self.font_family.clone(),
|
||||
features: self.font_features.clone(),
|
||||
fallbacks: self.font_fallbacks.clone(),
|
||||
weight: self.font_weight,
|
||||
style: self.font_style,
|
||||
}
|
||||
@ -286,6 +291,7 @@ impl TextStyle {
|
||||
font: Font {
|
||||
family: self.font_family.clone(),
|
||||
features: Default::default(),
|
||||
fallbacks: self.font_fallbacks.clone(),
|
||||
weight: self.font_weight,
|
||||
style: self.font_style,
|
||||
},
|
||||
|
@ -506,6 +506,7 @@ pub trait Styled: Sized {
|
||||
let Font {
|
||||
family,
|
||||
features,
|
||||
fallbacks,
|
||||
weight,
|
||||
style,
|
||||
} = font;
|
||||
@ -515,6 +516,7 @@ pub trait Styled: Sized {
|
||||
text_style.font_features = Some(features);
|
||||
text_style.font_weight = Some(weight);
|
||||
text_style.font_style = Some(style);
|
||||
text_style.font_fallbacks = fallbacks;
|
||||
|
||||
self
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
mod font_fallbacks;
|
||||
mod font_features;
|
||||
mod line;
|
||||
mod line_layout;
|
||||
mod line_wrapper;
|
||||
|
||||
pub use font_fallbacks::*;
|
||||
pub use font_features::*;
|
||||
pub use line::*;
|
||||
pub use line_layout::*;
|
||||
@ -62,8 +64,7 @@ impl TextSystem {
|
||||
wrapper_pool: Mutex::default(),
|
||||
font_runs_pool: Mutex::default(),
|
||||
fallback_font_stack: smallvec![
|
||||
// TODO: This is currently Zed-specific.
|
||||
// We should allow GPUI users to provide their own fallback font stack.
|
||||
// TODO: Remove this when Linux have implemented setting fallbacks.
|
||||
font("Zed Plex Mono"),
|
||||
font("Helvetica"),
|
||||
font("Segoe UI"), // Windows
|
||||
@ -683,6 +684,9 @@ pub struct Font {
|
||||
/// The font features to use.
|
||||
pub features: FontFeatures,
|
||||
|
||||
/// The fallbacks fonts to use.
|
||||
pub fallbacks: Option<FontFallbacks>,
|
||||
|
||||
/// The font weight.
|
||||
pub weight: FontWeight,
|
||||
|
||||
@ -697,6 +701,7 @@ pub fn font(family: impl Into<SharedString>) -> Font {
|
||||
features: FontFeatures::default(),
|
||||
weight: FontWeight::default(),
|
||||
style: FontStyle::default(),
|
||||
fallbacks: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
21
crates/gpui/src/text_system/font_fallbacks.rs
Normal file
21
crates/gpui/src/text_system/font_fallbacks.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// The fallback fonts that can be configured for a given font.
|
||||
/// Fallback fonts family names are stored here.
|
||||
#[derive(Default, Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize, JsonSchema)]
|
||||
pub struct FontFallbacks(pub Arc<Vec<String>>);
|
||||
|
||||
impl FontFallbacks {
|
||||
/// Get the fallback fonts family names
|
||||
pub fn fallback_list(&self) -> &[String] {
|
||||
&self.0.as_slice()
|
||||
}
|
||||
|
||||
/// Create a font fallback from a list of strings
|
||||
pub fn from_fonts(fonts: Vec<String>) -> Self {
|
||||
FontFallbacks(Arc::new(fonts))
|
||||
}
|
||||
}
|
@ -162,6 +162,7 @@ pub fn render_item<T>(
|
||||
color: cx.theme().colors().text,
|
||||
font_family: settings.buffer_font.family.clone(),
|
||||
font_features: settings.buffer_font.features.clone(),
|
||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||
font_size: settings.buffer_font_size(cx).into(),
|
||||
font_weight: settings.buffer_font.weight,
|
||||
line_height: relative(1.),
|
||||
|
@ -406,6 +406,7 @@ impl AuthenticationPrompt {
|
||||
color: cx.theme().colors().text,
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
font_style: FontStyle::Normal,
|
||||
|
@ -341,6 +341,7 @@ impl AuthenticationPrompt {
|
||||
color: cx.theme().colors().text,
|
||||
font_family: settings.ui_font.family.clone(),
|
||||
font_features: settings.ui_font.features.clone(),
|
||||
font_fallbacks: settings.ui_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.ui_font.weight,
|
||||
font_style: FontStyle::Normal,
|
||||
|
@ -114,6 +114,7 @@ impl BufferSearchBar {
|
||||
},
|
||||
font_family: settings.buffer_font.family.clone(),
|
||||
font_features: settings.buffer_font.features.clone(),
|
||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.buffer_font.weight,
|
||||
line_height: relative(1.3),
|
||||
|
@ -1338,6 +1338,7 @@ impl ProjectSearchBar {
|
||||
},
|
||||
font_family: settings.buffer_font.family.clone(),
|
||||
font_features: settings.buffer_font.features.clone(),
|
||||
font_fallbacks: settings.buffer_font.fallbacks.clone(),
|
||||
font_size: rems(0.875).into(),
|
||||
font_weight: settings.buffer_font.weight,
|
||||
line_height: relative(1.3),
|
||||
|
@ -18,9 +18,11 @@ pub fn test_settings() -> String {
|
||||
"ui_font_family": "Courier",
|
||||
"ui_font_features": {},
|
||||
"ui_font_size": 14,
|
||||
"ui_font_fallback": [],
|
||||
"buffer_font_family": "Courier",
|
||||
"buffer_font_features": {},
|
||||
"buffer_font_size": 14,
|
||||
"buffer_font_fallback": [],
|
||||
"theme": EMPTY_THEME_NAME,
|
||||
}),
|
||||
&mut value,
|
||||
|
@ -1,8 +1,10 @@
|
||||
use collections::HashMap;
|
||||
use gpui::{px, AbsoluteLength, AppContext, FontFeatures, FontWeight, Pixels};
|
||||
use gpui::{
|
||||
px, AbsoluteLength, AppContext, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString,
|
||||
};
|
||||
use schemars::{
|
||||
gen::SchemaGenerator,
|
||||
schema::{InstanceType, RootSchema, Schema, SchemaObject},
|
||||
schema::{ArrayValidation, InstanceType, RootSchema, Schema, SchemaObject},
|
||||
JsonSchema,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@ -24,15 +26,16 @@ pub struct Toolbar {
|
||||
pub title: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct TerminalSettings {
|
||||
pub shell: Shell,
|
||||
pub working_directory: WorkingDirectory,
|
||||
pub font_size: Option<Pixels>,
|
||||
pub font_family: Option<String>,
|
||||
pub line_height: TerminalLineHeight,
|
||||
pub font_family: Option<SharedString>,
|
||||
pub font_fallbacks: Option<FontFallbacks>,
|
||||
pub font_features: Option<FontFeatures>,
|
||||
pub font_weight: Option<FontWeight>,
|
||||
pub line_height: TerminalLineHeight,
|
||||
pub env: HashMap<String, String>,
|
||||
pub blinking: TerminalBlink,
|
||||
pub alternate_scroll: AlternateScroll,
|
||||
@ -111,6 +114,13 @@ pub struct TerminalSettingsContent {
|
||||
/// If this option is not included,
|
||||
/// the terminal will default to matching the buffer's font family.
|
||||
pub font_family: Option<String>,
|
||||
|
||||
/// Sets the terminal's font fallbacks.
|
||||
///
|
||||
/// If this option is not included,
|
||||
/// the terminal will default to matching the buffer's font fallbacks.
|
||||
pub font_fallbacks: Option<Vec<String>>,
|
||||
|
||||
/// Sets the terminal's line height.
|
||||
///
|
||||
/// Default: comfortable
|
||||
@ -192,30 +202,51 @@ impl settings::Settings for TerminalSettings {
|
||||
_: &AppContext,
|
||||
) -> RootSchema {
|
||||
let mut root_schema = generator.root_schema_for::<Self::FileContent>();
|
||||
let available_fonts = params
|
||||
let available_fonts: Vec<_> = params
|
||||
.font_names
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Value::String)
|
||||
.collect();
|
||||
let fonts_schema = SchemaObject {
|
||||
|
||||
let font_family_schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
enum_values: Some(available_fonts),
|
||||
..Default::default()
|
||||
};
|
||||
root_schema
|
||||
.definitions
|
||||
.extend([("FontFamilies".into(), fonts_schema.into())]);
|
||||
|
||||
let font_fallback_schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Array.into()),
|
||||
array: Some(Box::new(ArrayValidation {
|
||||
items: Some(schemars::schema::SingleOrVec::Single(Box::new(
|
||||
font_family_schema.clone().into(),
|
||||
))),
|
||||
unique_items: Some(true),
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
root_schema.definitions.extend([
|
||||
("FontFamilies".into(), font_family_schema.into()),
|
||||
("FontFallbacks".into(), font_fallback_schema.into()),
|
||||
]);
|
||||
root_schema
|
||||
.schema
|
||||
.object
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.properties
|
||||
.extend([(
|
||||
"font_family".to_owned(),
|
||||
Schema::new_ref("#/definitions/FontFamilies".into()),
|
||||
)]);
|
||||
.extend([
|
||||
(
|
||||
"font_family".to_owned(),
|
||||
Schema::new_ref("#/definitions/FontFamilies".into()),
|
||||
),
|
||||
(
|
||||
"font_fallbacks".to_owned(),
|
||||
Schema::new_ref("#/definitions/FontFallbacks".into()),
|
||||
),
|
||||
]);
|
||||
|
||||
root_schema
|
||||
}
|
||||
|
@ -614,16 +614,24 @@ impl Element for TerminalElement {
|
||||
let buffer_font_size = settings.buffer_font_size(cx);
|
||||
|
||||
let terminal_settings = TerminalSettings::get_global(cx);
|
||||
|
||||
let font_family = terminal_settings
|
||||
.font_family
|
||||
.as_ref()
|
||||
.map(|string| string.clone().into())
|
||||
.unwrap_or(settings.buffer_font.family);
|
||||
.unwrap_or(&settings.buffer_font.family)
|
||||
.clone();
|
||||
|
||||
let font_fallbacks = terminal_settings
|
||||
.font_fallbacks
|
||||
.as_ref()
|
||||
.or(settings.buffer_font.fallbacks.as_ref())
|
||||
.map(|fallbacks| fallbacks.clone());
|
||||
|
||||
let font_features = terminal_settings
|
||||
.font_features
|
||||
.clone()
|
||||
.unwrap_or(settings.buffer_font.features.clone());
|
||||
.as_ref()
|
||||
.unwrap_or(&settings.buffer_font.features)
|
||||
.clone();
|
||||
|
||||
let font_weight = terminal_settings.font_weight.unwrap_or_default();
|
||||
|
||||
@ -653,6 +661,7 @@ impl Element for TerminalElement {
|
||||
font_family,
|
||||
font_features,
|
||||
font_weight,
|
||||
font_fallbacks,
|
||||
font_size: font_size.into(),
|
||||
font_style: FontStyle::Normal,
|
||||
line_height: line_height.into(),
|
||||
|
@ -3,10 +3,11 @@ use crate::{Appearance, SyntaxTheme, Theme, ThemeRegistry, ThemeStyleContent};
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use gpui::{
|
||||
px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Global, Pixels, Subscription,
|
||||
ViewContext, WindowContext,
|
||||
px, AppContext, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels,
|
||||
Subscription, ViewContext, WindowContext,
|
||||
};
|
||||
use refineable::Refineable;
|
||||
use schemars::schema::ArrayValidation;
|
||||
use schemars::{
|
||||
gen::SchemaGenerator,
|
||||
schema::{InstanceType, Schema, SchemaObject},
|
||||
@ -244,6 +245,9 @@ pub struct ThemeSettingsContent {
|
||||
/// The name of a font to use for rendering in the UI.
|
||||
#[serde(default)]
|
||||
pub ui_font_family: Option<String>,
|
||||
/// The font fallbacks to use for rendering in the UI.
|
||||
#[serde(default)]
|
||||
pub ui_font_fallbacks: Option<Vec<String>>,
|
||||
/// The OpenType features to enable for text in the UI.
|
||||
#[serde(default)]
|
||||
pub ui_font_features: Option<FontFeatures>,
|
||||
@ -253,6 +257,9 @@ pub struct ThemeSettingsContent {
|
||||
/// The name of a font to use for rendering in text buffers.
|
||||
#[serde(default)]
|
||||
pub buffer_font_family: Option<String>,
|
||||
/// The font fallbacks to use for rendering in text buffers.
|
||||
#[serde(default)]
|
||||
pub buffer_font_fallbacks: Option<Vec<String>>,
|
||||
/// The default font size for rendering in text buffers.
|
||||
#[serde(default)]
|
||||
pub buffer_font_size: Option<f32>,
|
||||
@ -510,14 +517,22 @@ impl settings::Settings for ThemeSettings {
|
||||
let mut this = Self {
|
||||
ui_font_size: defaults.ui_font_size.unwrap().into(),
|
||||
ui_font: Font {
|
||||
family: defaults.ui_font_family.clone().unwrap().into(),
|
||||
family: defaults.ui_font_family.as_ref().unwrap().clone().into(),
|
||||
features: defaults.ui_font_features.clone().unwrap(),
|
||||
fallbacks: defaults
|
||||
.ui_font_fallbacks
|
||||
.as_ref()
|
||||
.map(|fallbacks| FontFallbacks::from_fonts(fallbacks.clone())),
|
||||
weight: defaults.ui_font_weight.map(FontWeight).unwrap(),
|
||||
style: Default::default(),
|
||||
},
|
||||
buffer_font: Font {
|
||||
family: defaults.buffer_font_family.clone().unwrap().into(),
|
||||
family: defaults.buffer_font_family.as_ref().unwrap().clone().into(),
|
||||
features: defaults.buffer_font_features.clone().unwrap(),
|
||||
fallbacks: defaults
|
||||
.buffer_font_fallbacks
|
||||
.as_ref()
|
||||
.map(|fallbacks| FontFallbacks::from_fonts(fallbacks.clone())),
|
||||
weight: defaults.buffer_font_weight.map(FontWeight).unwrap(),
|
||||
style: FontStyle::default(),
|
||||
},
|
||||
@ -543,7 +558,9 @@ impl settings::Settings for ThemeSettings {
|
||||
if let Some(value) = value.buffer_font_features.clone() {
|
||||
this.buffer_font.features = value;
|
||||
}
|
||||
|
||||
if let Some(value) = value.buffer_font_fallbacks.clone() {
|
||||
this.buffer_font.fallbacks = Some(FontFallbacks::from_fonts(value));
|
||||
}
|
||||
if let Some(value) = value.buffer_font_weight {
|
||||
this.buffer_font.weight = FontWeight(value);
|
||||
}
|
||||
@ -554,6 +571,9 @@ impl settings::Settings for ThemeSettings {
|
||||
if let Some(value) = value.ui_font_features.clone() {
|
||||
this.ui_font.features = value;
|
||||
}
|
||||
if let Some(value) = value.ui_font_fallbacks.clone() {
|
||||
this.ui_font.fallbacks = Some(FontFallbacks::from_fonts(value));
|
||||
}
|
||||
if let Some(value) = value.ui_font_weight {
|
||||
this.ui_font.weight = FontWeight(value);
|
||||
}
|
||||
@ -605,15 +625,28 @@ impl settings::Settings for ThemeSettings {
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Value::String)
|
||||
.collect();
|
||||
let fonts_schema = SchemaObject {
|
||||
.collect::<Vec<_>>();
|
||||
let font_family_schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
enum_values: Some(available_fonts),
|
||||
..Default::default()
|
||||
};
|
||||
let font_fallback_schema = SchemaObject {
|
||||
instance_type: Some(InstanceType::Array.into()),
|
||||
array: Some(Box::new(ArrayValidation {
|
||||
items: Some(schemars::schema::SingleOrVec::Single(Box::new(
|
||||
font_family_schema.clone().into(),
|
||||
))),
|
||||
unique_items: Some(true),
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
root_schema.definitions.extend([
|
||||
("ThemeName".into(), theme_name_schema.into()),
|
||||
("FontFamilies".into(), fonts_schema.into()),
|
||||
("FontFamilies".into(), font_family_schema.into()),
|
||||
("FontFallbacks".into(), font_fallback_schema.into()),
|
||||
]);
|
||||
|
||||
root_schema
|
||||
@ -627,10 +660,18 @@ impl settings::Settings for ThemeSettings {
|
||||
"buffer_font_family".to_owned(),
|
||||
Schema::new_ref("#/definitions/FontFamilies".into()),
|
||||
),
|
||||
(
|
||||
"buffer_font_fallbacks".to_owned(),
|
||||
Schema::new_ref("#/definitions/FontFallbacks".into()),
|
||||
),
|
||||
(
|
||||
"ui_font_family".to_owned(),
|
||||
Schema::new_ref("#/definitions/FontFamilies".into()),
|
||||
),
|
||||
(
|
||||
"ui_font_fallbacks".to_owned(),
|
||||
Schema::new_ref("#/definitions/FontFallbacks".into()),
|
||||
),
|
||||
]);
|
||||
|
||||
root_schema
|
||||
|
Loading…
Reference in New Issue
Block a user