Dynamic scale factor updates (#817)

* wip: dynamic scale factor updates

* refactor font options

* update font opts

* refacor scale factor updates

* cargo fmt
macos-click-through
partizan 3 years ago committed by GitHub
parent f58b9470a6
commit e8354e1b23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,8 +12,6 @@ use unicode_segmentation::UnicodeSegmentation;
use super::font_loader::*; use super::font_loader::*;
use super::font_options::*; use super::font_options::*;
const DEFAULT_FONT_SIZE: f32 = 14.0;
#[derive(new, Clone, Hash, PartialEq, Eq, Debug)] #[derive(new, Clone, Hash, PartialEq, Eq, Debug)]
struct ShapeKey { struct ShapeKey {
pub cells: Vec<String>, pub cells: Vec<String>,
@ -22,7 +20,7 @@ struct ShapeKey {
} }
pub struct CachingShaper { pub struct CachingShaper {
pub options: Option<FontOptions>, options: FontOptions,
font_loader: FontLoader, font_loader: FontLoader,
blob_cache: LruCache<ShapeKey, Vec<TextBlob>>, blob_cache: LruCache<ShapeKey, Vec<TextBlob>>,
shape_context: ShapeContext, shape_context: ShapeContext,
@ -30,11 +28,12 @@ pub struct CachingShaper {
} }
impl CachingShaper { impl CachingShaper {
pub fn new(device_scale_factor: f32) -> CachingShaper { pub fn new(scale_factor: f32) -> CachingShaper {
let scale_factor = points_to_pixels(device_scale_factor); let options = FontOptions::default();
let font_size = options.size * scale_factor;
CachingShaper { CachingShaper {
options: None, options,
font_loader: FontLoader::new(DEFAULT_FONT_SIZE * scale_factor), font_loader: FontLoader::new(font_size),
blob_cache: LruCache::new(10000), blob_cache: LruCache::new(10000),
shape_context: ShapeContext::new(), shape_context: ShapeContext::new(),
scale_factor, scale_factor,
@ -42,21 +41,8 @@ impl CachingShaper {
} }
fn current_font_pair(&mut self) -> Arc<FontPair> { fn current_font_pair(&mut self) -> Arc<FontPair> {
let default_key = FontKey { let default_key = FontKey::default();
italic: false, let font_key = FontKey::from(&self.options);
bold: false,
font_selection: FontSelection::Default,
};
let font_key = self
.options
.as_ref()
.map(|options| FontKey {
italic: options.italic,
bold: options.bold,
font_selection: options.fallback_list.first().unwrap().clone().into(),
})
.unwrap_or_else(|| default_key.clone());
if let Some(font_pair) = self.font_loader.get_or_load(&font_key) { if let Some(font_pair) = self.font_loader.get_or_load(&font_key) {
return font_pair; return font_pair;
@ -68,26 +54,28 @@ impl CachingShaper {
} }
pub fn current_size(&self) -> f32 { pub fn current_size(&self) -> f32 {
self.options self.options.size * self.scale_factor
.as_ref()
.map(|options| options.size)
.unwrap_or(DEFAULT_FONT_SIZE)
* self.scale_factor
} }
pub fn update_font(&mut self, guifont_setting: &str) -> bool { pub fn update_scale_factor(&mut self, scale_factor: f32) {
let new_options = FontOptions::parse(guifont_setting, DEFAULT_FONT_SIZE); trace!("scale_factor changed: {:.2}", scale_factor);
self.scale_factor = scale_factor;
self.reset_font_loader();
}
if new_options != self.options && new_options.is_some() { pub fn update_font(&mut self, guifont_setting: &str) {
self.font_loader = trace!("Updating font: {}", guifont_setting);
FontLoader::new(new_options.as_ref().unwrap().size * self.scale_factor);
self.blob_cache.clear();
self.options = new_options;
true self.options = FontOptions::parse(guifont_setting);
} else { self.reset_font_loader();
false }
}
fn reset_font_loader(&mut self) {
let font_size = self.options.size * self.scale_factor;
trace!("Using font_size: {:.2}px", font_size);
self.font_loader = FontLoader::new(font_size);
self.blob_cache.clear();
} }
fn metrics(&mut self) -> Metrics { fn metrics(&mut self) -> Metrics {
@ -156,44 +144,26 @@ impl CachingShaper {
// Create font fallback list // Create font fallback list
let mut font_fallback_keys = Vec::new(); let mut font_fallback_keys = Vec::new();
// Add guifont fallback list // Add parsed fonts from guifont
if let Some(options) = &self.options { font_fallback_keys.extend(self.options.font_list.iter().map(|font_name| FontKey {
font_fallback_keys.extend(options.fallback_list.iter().map(|font_name| FontKey { italic: self.options.italic || italic,
italic: options.italic || italic, bold: self.options.bold || bold,
bold: options.bold || bold, font_selection: font_name.into(),
font_selection: font_name.into(), }));
}));
// Add default font
// Add default font font_fallback_keys.push(FontKey {
font_fallback_keys.push(FontKey { italic: self.options.italic || italic,
italic: options.italic || italic, bold: self.options.bold || bold,
bold: options.bold || bold, font_selection: FontSelection::Default,
font_selection: FontSelection::Default, });
});
// Add skia fallback
// Add skia fallback font_fallback_keys.push(FontKey {
font_fallback_keys.push(FontKey { italic,
italic: options.italic || italic, bold,
bold: options.bold || bold, font_selection: cluster.chars()[0].ch.into(),
font_selection: cluster.chars()[0].ch.into(), });
});
} else {
// No confgured option. Default to not italic and not bold versions
// Add default font
font_fallback_keys.push(FontKey {
italic,
bold,
font_selection: FontSelection::Default,
});
// Add skia fallback
font_fallback_keys.push(FontKey {
italic,
bold,
font_selection: cluster.chars()[0].ch.into(),
});
}
// Add last resort // Add last resort
font_fallback_keys.push(FontKey { font_fallback_keys.push(FontKey {
@ -312,25 +282,3 @@ impl CachingShaper {
self.blob_cache.get(&key).unwrap() self.blob_cache.get(&key).unwrap()
} }
} }
fn points_to_pixels(value: f32) -> f32 {
// Fonts in neovim are using points, not pixels.
//
// Skia docs is incorrectly stating it uses points, but uses pixels:
// https://api.skia.org/classSkFont.html#a7e28a156a517d01bc608c14c761346bf
// https://github.com/mono/SkiaSharp/issues/1147#issuecomment-587421201
//
// So, we need to convert points to pixels.
//
// In reality, this depends on DPI/PPI of monitor, but here we only care about converting
// from points to pixels, so this is standard constant values.
let pixels_per_inch = 96.0;
let points_per_inch = 72.0;
// On macos points == pixels
#[cfg(target_os = "macos")]
let points_per_inch = 96.0;
let pixels_per_point = pixels_per_inch / points_per_inch;
value * pixels_per_point
}

@ -3,6 +3,7 @@ use std::sync::Arc;
use lru::LruCache; use lru::LruCache;
use skia_safe::{font::Edging, Data, Font, FontHinting, FontMgr, FontStyle, Typeface}; use skia_safe::{font::Edging, Data, Font, FontHinting, FontMgr, FontStyle, Typeface};
use super::font_options::FontOptions;
use super::swash_font::SwashFont; use super::swash_font::SwashFont;
#[derive(RustEmbed)] #[derive(RustEmbed)]
@ -54,6 +55,26 @@ pub struct FontKey {
pub font_selection: FontSelection, pub font_selection: FontSelection,
} }
impl Default for FontKey {
fn default() -> Self {
FontKey {
italic: false,
bold: false,
font_selection: FontSelection::Default,
}
}
}
impl From<&FontOptions> for FontKey {
fn from(options: &FontOptions) -> FontKey {
FontKey {
italic: options.italic,
bold: options.bold,
font_selection: options.primary_font(),
}
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)] #[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum FontSelection { pub enum FontSelection {
Name(String), Name(String),

@ -1,30 +1,33 @@
use super::font_loader::FontSelection;
const DEFAULT_FONT_SIZE: f32 = 14.0;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FontOptions { pub struct FontOptions {
guifont_setting: Option<String>, pub font_list: Vec<String>,
pub fallback_list: Vec<String>,
pub size: f32, pub size: f32,
pub bold: bool, pub bold: bool,
pub italic: bool, pub italic: bool,
} }
impl FontOptions { impl FontOptions {
pub fn parse(guifont_setting: &str, default_size: f32) -> Option<FontOptions> { pub fn parse(guifont_setting: &str) -> FontOptions {
let mut fallback_list = None; let mut font_list = Vec::new();
let mut size = default_size; let mut size = DEFAULT_FONT_SIZE;
let mut bold = false; let mut bold = false;
let mut italic = false; let mut italic = false;
let mut parts = guifont_setting.split(':').filter(|part| !part.is_empty()); let mut parts = guifont_setting.split(':').filter(|part| !part.is_empty());
if let Some(parts) = parts.next() { if let Some(parts) = parts.next() {
let parsed_fallback_list: Vec<String> = parts let parsed_font_list: Vec<String> = parts
.split(',') .split(',')
.filter(|fallback| !fallback.is_empty()) .filter(|fallback| !fallback.is_empty())
.map(|fallback| fallback.to_string()) .map(|fallback| fallback.to_string())
.collect(); .collect();
if !parsed_fallback_list.is_empty() { if !parsed_font_list.is_empty() {
fallback_list = Some(parsed_fallback_list); font_list = parsed_font_list;
} }
} }
@ -40,25 +43,60 @@ impl FontOptions {
} }
} }
fallback_list.map(|fallback_list| FontOptions { FontOptions {
guifont_setting: Some(guifont_setting.to_string()), font_list,
fallback_list,
bold, bold,
italic, italic,
size, size: points_to_pixels(size),
}) }
}
pub fn primary_font(&self) -> FontSelection {
self.font_list
.first()
.map(|f| FontSelection::from(f))
.unwrap_or(FontSelection::Default)
} }
} }
impl PartialEq for FontOptions { impl Default for FontOptions {
fn eq(&self, other: &Self) -> bool { fn default() -> Self {
if self.guifont_setting.is_some() && self.guifont_setting == other.guifont_setting { FontOptions {
return true; font_list: Vec::new(),
bold: false,
italic: false,
size: points_to_pixels(DEFAULT_FONT_SIZE),
} }
}
}
self.fallback_list == other.fallback_list impl PartialEq for FontOptions {
fn eq(&self, other: &Self) -> bool {
self.font_list == other.font_list
&& (self.size - other.size).abs() < std::f32::EPSILON && (self.size - other.size).abs() < std::f32::EPSILON
&& self.bold == other.bold && self.bold == other.bold
&& self.italic == other.italic && self.italic == other.italic
} }
} }
fn points_to_pixels(value: f32) -> f32 {
// Fonts in neovim are using points, not pixels.
//
// Skia docs is incorrectly stating it uses points, but uses pixels:
// https://api.skia.org/classSkFont.html#a7e28a156a517d01bc608c14c761346bf
// https://github.com/mono/SkiaSharp/issues/1147#issuecomment-587421201
//
// So, we need to convert points to pixels.
//
// In reality, this depends on DPI/PPI of monitor, but here we only care about converting
// from points to pixels, so this is standard constant values.
let pixels_per_inch = 96.0;
let points_per_inch = 72.0;
// On macos points == pixels
#[cfg(target_os = "macos")]
let points_per_inch = 96.0;
let pixels_per_point = pixels_per_inch / points_per_inch;
value * pixels_per_point
}

@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use std::sync::Arc; use std::sync::Arc;
use log::error; use log::{error, trace};
use skia_safe::{colors, dash_path_effect, BlendMode, Canvas, Color, Paint, Rect}; use skia_safe::{colors, dash_path_effect, BlendMode, Canvas, Color, Paint, Rect};
pub mod animation_utils; pub mod animation_utils;
@ -87,12 +87,25 @@ impl Renderer {
} }
} }
pub fn handle_scale_factor_update(&mut self, scale_factor: f64) {
self.shaper.update_scale_factor(scale_factor as f32);
self.update_font_dimensions();
}
fn update_font(&mut self, guifont_setting: &str) { fn update_font(&mut self, guifont_setting: &str) {
if self.shaper.update_font(guifont_setting) { self.shaper.update_font(guifont_setting);
let (font_width, font_height) = self.shaper.font_base_dimensions(); self.update_font_dimensions();
self.font_width = font_width; }
self.font_height = font_height;
} fn update_font_dimensions(&mut self) {
let (font_width, font_height) = self.shaper.font_base_dimensions();
self.font_width = font_width;
self.font_height = font_height;
trace!(
"Updating font dimensions: {}x{}",
self.font_height,
self.font_width,
);
} }
fn compute_text_region(&self, grid_pos: (u64, u64), cell_width: u64) -> Rect { fn compute_text_region(&self, grid_pos: (u64, u64), cell_width: u64) -> Rect {

@ -19,6 +19,7 @@ use glutin::{
window::{self, Fullscreen, Icon}, window::{self, Fullscreen, Icon},
ContextBuilder, GlProfile, WindowedContext, ContextBuilder, GlProfile, WindowedContext,
}; };
use log::trace;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use glutin::platform::unix::WindowBuilderExtUnix; use glutin::platform::unix::WindowBuilderExtUnix;
@ -128,6 +129,12 @@ impl GlutinWindowWrapper {
} => { } => {
self.handle_quit(running); self.handle_quit(running);
} }
Event::WindowEvent {
event: WindowEvent::ScaleFactorChanged { scale_factor, .. },
..
} => {
self.handle_scale_factor_update(scale_factor);
}
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::DroppedFile(path), event: WindowEvent::DroppedFile(path),
.. ..
@ -159,6 +166,7 @@ impl GlutinWindowWrapper {
let previous_size = self.saved_inner_size; let previous_size = self.saved_inner_size;
if previous_size != current_size { if previous_size != current_size {
trace!("Updating grid size: {:#?}", current_size);
self.saved_inner_size = current_size; self.saved_inner_size = current_size;
handle_new_grid_size(current_size, &self.renderer, &self.ui_command_sender); handle_new_grid_size(current_size, &self.renderer, &self.ui_command_sender);
self.skia_renderer.resize(&self.windowed_context); self.skia_renderer.resize(&self.windowed_context);
@ -181,6 +189,10 @@ impl GlutinWindowWrapper {
self.windowed_context.swap_buffers().unwrap(); self.windowed_context.swap_buffers().unwrap();
} }
} }
fn handle_scale_factor_update(&mut self, scale_factor: f64) {
self.renderer.handle_scale_factor_update(scale_factor);
}
} }
pub fn start_loop( pub fn start_loop(

Loading…
Cancel
Save