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_options::*;
const DEFAULT_FONT_SIZE: f32 = 14.0;
#[derive(new, Clone, Hash, PartialEq, Eq, Debug)]
struct ShapeKey {
pub cells: Vec<String>,
@ -22,7 +20,7 @@ struct ShapeKey {
}
pub struct CachingShaper {
pub options: Option<FontOptions>,
options: FontOptions,
font_loader: FontLoader,
blob_cache: LruCache<ShapeKey, Vec<TextBlob>>,
shape_context: ShapeContext,
@ -30,11 +28,12 @@ pub struct CachingShaper {
}
impl CachingShaper {
pub fn new(device_scale_factor: f32) -> CachingShaper {
let scale_factor = points_to_pixels(device_scale_factor);
pub fn new(scale_factor: f32) -> CachingShaper {
let options = FontOptions::default();
let font_size = options.size * scale_factor;
CachingShaper {
options: None,
font_loader: FontLoader::new(DEFAULT_FONT_SIZE * scale_factor),
options,
font_loader: FontLoader::new(font_size),
blob_cache: LruCache::new(10000),
shape_context: ShapeContext::new(),
scale_factor,
@ -42,21 +41,8 @@ impl CachingShaper {
}
fn current_font_pair(&mut self) -> Arc<FontPair> {
let default_key = FontKey {
italic: false,
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());
let default_key = FontKey::default();
let font_key = FontKey::from(&self.options);
if let Some(font_pair) = self.font_loader.get_or_load(&font_key) {
return font_pair;
@ -68,26 +54,28 @@ impl CachingShaper {
}
pub fn current_size(&self) -> f32 {
self.options
.as_ref()
.map(|options| options.size)
.unwrap_or(DEFAULT_FONT_SIZE)
* self.scale_factor
self.options.size * self.scale_factor
}
pub fn update_font(&mut self, guifont_setting: &str) -> bool {
let new_options = FontOptions::parse(guifont_setting, DEFAULT_FONT_SIZE);
pub fn update_scale_factor(&mut self, scale_factor: f32) {
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() {
self.font_loader =
FontLoader::new(new_options.as_ref().unwrap().size * self.scale_factor);
self.blob_cache.clear();
self.options = new_options;
pub fn update_font(&mut self, guifont_setting: &str) {
trace!("Updating font: {}", guifont_setting);
true
} else {
false
self.options = FontOptions::parse(guifont_setting);
self.reset_font_loader();
}
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 {
@ -156,34 +144,17 @@ impl CachingShaper {
// Create font fallback list
let mut font_fallback_keys = Vec::new();
// Add guifont fallback list
if let Some(options) = &self.options {
font_fallback_keys.extend(options.fallback_list.iter().map(|font_name| FontKey {
italic: options.italic || italic,
bold: options.bold || bold,
// Add parsed fonts from guifont
font_fallback_keys.extend(self.options.font_list.iter().map(|font_name| FontKey {
italic: self.options.italic || italic,
bold: self.options.bold || bold,
font_selection: font_name.into(),
}));
// Add default font
font_fallback_keys.push(FontKey {
italic: options.italic || italic,
bold: options.bold || bold,
font_selection: FontSelection::Default,
});
// Add skia fallback
font_fallback_keys.push(FontKey {
italic: options.italic || italic,
bold: options.bold || bold,
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,
italic: self.options.italic || italic,
bold: self.options.bold || bold,
font_selection: FontSelection::Default,
});
@ -193,7 +164,6 @@ impl CachingShaper {
bold,
font_selection: cluster.chars()[0].ch.into(),
});
}
// Add last resort
font_fallback_keys.push(FontKey {
@ -312,25 +282,3 @@ impl CachingShaper {
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 skia_safe::{font::Edging, Data, Font, FontHinting, FontMgr, FontStyle, Typeface};
use super::font_options::FontOptions;
use super::swash_font::SwashFont;
#[derive(RustEmbed)]
@ -54,6 +55,26 @@ pub struct FontKey {
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)]
pub enum FontSelection {
Name(String),

@ -1,30 +1,33 @@
use super::font_loader::FontSelection;
const DEFAULT_FONT_SIZE: f32 = 14.0;
#[derive(Clone, Debug)]
pub struct FontOptions {
guifont_setting: Option<String>,
pub fallback_list: Vec<String>,
pub font_list: Vec<String>,
pub size: f32,
pub bold: bool,
pub italic: bool,
}
impl FontOptions {
pub fn parse(guifont_setting: &str, default_size: f32) -> Option<FontOptions> {
let mut fallback_list = None;
let mut size = default_size;
pub fn parse(guifont_setting: &str) -> FontOptions {
let mut font_list = Vec::new();
let mut size = DEFAULT_FONT_SIZE;
let mut bold = false;
let mut italic = false;
let mut parts = guifont_setting.split(':').filter(|part| !part.is_empty());
if let Some(parts) = parts.next() {
let parsed_fallback_list: Vec<String> = parts
let parsed_font_list: Vec<String> = parts
.split(',')
.filter(|fallback| !fallback.is_empty())
.map(|fallback| fallback.to_string())
.collect();
if !parsed_fallback_list.is_empty() {
fallback_list = Some(parsed_fallback_list);
if !parsed_font_list.is_empty() {
font_list = parsed_font_list;
}
}
@ -40,25 +43,60 @@ impl FontOptions {
}
}
fallback_list.map(|fallback_list| FontOptions {
guifont_setting: Some(guifont_setting.to_string()),
fallback_list,
FontOptions {
font_list,
bold,
italic,
size,
})
size: points_to_pixels(size),
}
}
impl PartialEq for FontOptions {
fn eq(&self, other: &Self) -> bool {
if self.guifont_setting.is_some() && self.guifont_setting == other.guifont_setting {
return true;
pub fn primary_font(&self) -> FontSelection {
self.font_list
.first()
.map(|f| FontSelection::from(f))
.unwrap_or(FontSelection::Default)
}
}
self.fallback_list == other.fallback_list
impl Default for FontOptions {
fn default() -> Self {
FontOptions {
font_list: Vec::new(),
bold: false,
italic: false,
size: points_to_pixels(DEFAULT_FONT_SIZE),
}
}
}
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.bold == other.bold
&& 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::Arc;
use log::error;
use log::{error, trace};
use skia_safe::{colors, dash_path_effect, BlendMode, Canvas, Color, Paint, Rect};
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) {
if self.shaper.update_font(guifont_setting) {
self.shaper.update_font(guifont_setting);
self.update_font_dimensions();
}
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 {

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

Loading…
Cancel
Save