font fallback guifont extension

macos-click-through
keith 5 years ago
parent 2c196f84f1
commit e8263f1f19

@ -33,8 +33,7 @@ pub struct Editor {
pub grid: CharacterGrid,
pub title: String,
pub mouse_enabled: bool,
pub font_name: Option<String>,
pub font_size: Option<f32>,
pub guifont: Option<String>,
pub cursor: Cursor,
pub default_style: Arc<Style>,
pub defined_styles: HashMap<u64, Arc<Style>>,
@ -49,8 +48,7 @@ impl Editor {
grid: CharacterGrid::new(window_geometry_or_default()),
title: "Neovide".to_string(),
mouse_enabled: true,
font_name: None,
font_size: None,
guifont: None,
cursor: Cursor::new(),
default_style: Arc::new(Style::new(Colors::new(
Some(colors::WHITE),
@ -307,14 +305,8 @@ impl Editor {
fn set_option(&mut self, gui_option: GuiOption) {
trace!("Option set {:?}", &gui_option);
match gui_option {
GuiOption::GuiFont(font_description) => {
let parts: Vec<&str> = font_description.split(':').collect();
self.font_name = Some(parts[0].to_string());
for part in parts.iter().skip(1) {
if part.starts_with('h') && part.len() > 1 {
self.font_size = part[1..].parse::<f32>().ok();
}
}
GuiOption::GuiFont(guifont) => {
self.guifont = Some(guifont);
}
_ => {}
}

@ -10,7 +10,11 @@ use log::{trace, warn};
use lru::LruCache;
use skribo::{FontCollection, FontFamily, FontRef as SkriboFont, LayoutSession, TextStyle};
use skulpin::skia_safe::{Data, Font as SkiaFont, TextBlob, TextBlobBuilder, Typeface};
use std::collections::HashMap;
use std::iter;
use super::font_options::FontOptions;
const STANDARD_CHARACTER_STRING: &str =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
@ -158,29 +162,27 @@ struct ShapeKey {
pub fn build_collection_by_font_name(
loader: &mut FontLoader,
font_name: Option<&str>,
fallback_list: &Vec<String>,
bold: bool,
italic: bool,
) -> FontCollection {
let mut collection = FontCollection::new();
if let Some(font_name) = font_name {
let weight = if bold { Weight::BOLD } else { Weight::NORMAL };
let style = if italic { Style::Italic } else { Style::Normal };
let properties = Properties {
weight,
style,
stretch: Stretch::NORMAL,
};
let gui_fonts = &[font_name, SYSTEM_DEFAULT_FONT];
for font_name in gui_fonts {
if let Some(family) = loader.get_or_load(font_name, false) {
if let Some(font) = family.get(properties) {
collection.add_family(FontFamily::new_from_font(font.clone()));
break;
}
let weight = if bold { Weight::BOLD } else { Weight::NORMAL };
let style = if italic { Style::Italic } else { Style::Normal };
let properties = Properties {
weight,
style,
stretch: Stretch::NORMAL,
};
let gui_fonts = fallback_list.iter().map(|fallback_item| fallback_item.as_ref()).chain(iter::once(SYSTEM_DEFAULT_FONT));
for font_name in gui_fonts {
if let Some(family) = loader.get_or_load(font_name, false) {
if let Some(font) = family.get(properties) {
collection.add_family(FontFamily::new_from_font(font.clone()));
break;
}
}
}
@ -207,11 +209,11 @@ struct FontSet {
}
impl FontSet {
fn new(font_name: Option<&str>, mut loader: &mut FontLoader) -> FontSet {
fn new(fallback_list: &Vec<String>, mut loader: &mut FontLoader) -> FontSet {
FontSet {
normal: build_collection_by_font_name(&mut loader, font_name, false, false),
bold: build_collection_by_font_name(&mut loader, font_name, true, false),
italic: build_collection_by_font_name(&mut loader, font_name, false, true),
normal: build_collection_by_font_name(&mut loader, fallback_list, false, false),
bold: build_collection_by_font_name(&mut loader, fallback_list, true, false),
italic: build_collection_by_font_name(&mut loader, fallback_list, false, true),
}
}
@ -225,8 +227,7 @@ impl FontSet {
}
pub struct CachingShaper {
pub font_name: Option<String>,
pub base_size: f32,
pub options: FontOptions,
font_set: FontSet,
font_loader: FontLoader,
font_cache: LruCache<String, SkiaFont>,
@ -243,12 +244,14 @@ fn build_skia_font_from_skribo_font(skribo_font: &SkriboFont, base_size: f32) ->
impl CachingShaper {
pub fn new() -> CachingShaper {
let options = FontOptions::new(String::from(SYSTEM_DEFAULT_FONT), DEFAULT_FONT_SIZE);
let mut loader = FontLoader::new();
let font_set = FontSet::new(&options.fallback_list, &mut loader);
CachingShaper {
font_name: Some(String::from(SYSTEM_DEFAULT_FONT)),
base_size: DEFAULT_FONT_SIZE,
font_set: FontSet::new(Some(SYSTEM_DEFAULT_FONT), &mut loader),
options,
font_set,
font_loader: loader,
font_cache: LruCache::new(10),
blob_cache: LruCache::new(10000),
@ -259,7 +262,7 @@ impl CachingShaper {
let font_name = skribo_font.font.postscript_name()?;
if !self.font_cache.contains(&font_name) {
let font = build_skia_font_from_skribo_font(skribo_font, self.base_size)?;
let font = build_skia_font_from_skribo_font(skribo_font, self.options.size)?;
self.font_cache.put(font_name.clone(), font);
}
@ -279,12 +282,12 @@ impl CachingShaper {
pub fn shape(&mut self, text: &str, bold: bool, italic: bool) -> Vec<TextBlob> {
let style = TextStyle {
size: self.base_size,
size: self.options.size,
};
let session = LayoutSession::create(text, &style, &self.font_set.get(bold, italic));
let metrics = self.metrics();
let ascent = metrics.ascent * self.base_size / metrics.units_per_em as f32;
let ascent = metrics.ascent * self.options.size / metrics.units_per_em as f32;
let mut blobs = Vec::new();
for layout_run in session.iter_all() {
@ -321,21 +324,23 @@ impl CachingShaper {
self.blob_cache.get(&key).unwrap()
}
pub fn change_font(&mut self, font_name: Option<&str>, base_size: Option<f32>) {
trace!("Font changed {:?} {:?}", &font_name, &base_size);
self.font_name = font_name.map(|name| name.to_string());
self.base_size = base_size.unwrap_or(DEFAULT_FONT_SIZE);
self.font_set = FontSet::new(font_name, &mut self.font_loader);
self.font_cache.clear();
self.blob_cache.clear();
pub fn update_font(&mut self, guifont_setting: &str) -> bool {
let updated = self.options.update(guifont_setting);
if updated {
trace!("Font changed: {:?}", self.options);
self.font_set = FontSet::new(&self.options.fallback_list, &mut self.font_loader);
self.font_cache.clear();
self.blob_cache.clear();
}
updated
}
pub fn font_base_dimensions(&mut self) -> (f32, f32) {
let metrics = self.metrics();
let font_height =
(metrics.ascent - metrics.descent) * self.base_size / metrics.units_per_em as f32;
(metrics.ascent - metrics.descent) * self.options.size / metrics.units_per_em as f32;
let style = TextStyle {
size: self.base_size,
size: self.options.size,
};
let session =
LayoutSession::create(STANDARD_CHARACTER_STRING, &style, &self.font_set.normal);
@ -363,6 +368,6 @@ impl CachingShaper {
pub fn underline_position(&mut self) -> f32 {
let metrics = self.metrics();
-metrics.underline_position * self.base_size / metrics.units_per_em as f32
-metrics.underline_position * self.options.size / metrics.units_per_em as f32
}
}

@ -0,0 +1,55 @@
#[derive(Clone, PartialEq, Debug)]
pub struct FontOptions {
previous_guifont_setting: Option<String>,
pub fallback_list: Vec<String>,
pub size: f32
}
impl FontOptions {
pub fn new(name: String, size: f32) -> FontOptions {
FontOptions {
previous_guifont_setting: None,
fallback_list: vec!(name),
size
}
}
pub fn update(self: &mut FontOptions, guifont_setting: &str) -> bool {
if self.previous_guifont_setting.is_some() &&
guifont_setting == self.previous_guifont_setting.as_ref().unwrap() {
return false;
}
self.previous_guifont_setting = Some(guifont_setting.to_string());
let mut parts = guifont_setting
.split(':')
.filter(|part| !part.is_empty());
let mut updated = false;
if let Some(parts) = parts.next() {
let parsed_fallback_list: Vec<String> = parts
.split(',')
.filter(|fallback| !fallback.is_empty())
.map(|fallback| fallback.to_string())
.collect();
if parsed_fallback_list.len() > 0 && self.fallback_list != parsed_fallback_list {
self.fallback_list = parsed_fallback_list;
updated = true;
}
}
for part in parts {
if part.starts_with('h') && part.len() > 1 {
if let Some(size) = part[1..].parse::<f32>().ok() {
if (self.size - size).abs() > std::f32::EPSILON {
self.size = size;
updated = true;
}
}
}
}
updated
}
}

@ -6,8 +6,10 @@ use skulpin::skia_safe::{colors, dash_path_effect, Budgeted, Canvas, Paint, Rect
use skulpin::CoordinateSystemHelper;
mod caching_shaper;
pub mod font_options;
pub mod cursor_renderer;
pub use font_options::*;
pub use caching_shaper::CachingShaper;
use crate::editor::{Style, EDITOR};
@ -44,11 +46,14 @@ impl Renderer {
}
}
fn set_font(&mut self, name: Option<&str>, size: Option<f32>) {
self.shaper.change_font(name, size);
let (font_width, font_height) = self.shaper.font_base_dimensions();
self.font_width = font_width;
self.font_height = font_height;
fn update_font(&mut self, guifont_setting: &str) -> bool {
let updated = self.shaper.update_font(guifont_setting);
if updated {
let (font_width, font_height) = self.shaper.font_base_dimensions();
self.font_width = font_width;
self.font_height = font_height;
}
updated
}
fn compute_text_region(&self, grid_pos: (u64, u64), cell_width: u64) -> Rect {
@ -100,7 +105,7 @@ impl Renderer {
if style.underline || style.undercurl {
let line_position = self.shaper.underline_position();
let stroke_width = self.shaper.base_size / 10.0;
let stroke_width = self.shaper.options.size / 10.0;
self.paint
.set_color(style.special(&default_style.colors).to_color());
self.paint.set_stroke_width(stroke_width);
@ -152,32 +157,19 @@ impl Renderer {
) -> bool {
trace!("Rendering");
let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = {
let ((draw_commands, should_clear), default_style, cursor, guifont_setting) = {
let mut editor = EDITOR.lock();
(
editor.build_draw_commands(),
editor.default_style.clone(),
editor.cursor.clone(),
editor.font_name.clone(),
editor.font_size,
editor.guifont.clone()
)
};
let current_font = Some(self.shaper.font_name.clone().unwrap_or(String::from("")));
let editor_font = if font_name.clone().unwrap_or_default().is_empty() {
&current_font
} else {
&font_name
};
let font_changed = current_font != *editor_font
|| font_size
.map(|new_size| (new_size - self.shaper.base_size).abs() > std::f32::EPSILON)
.unwrap_or(false);
if font_changed {
self.set_font(font_name.as_deref(), font_size);
}
let font_changed = guifont_setting
.map(|guifont| self.update_font(&guifont))
.unwrap_or(false);
if should_clear {
self.surface = None;

Loading…
Cancel
Save