diff --git a/Cargo.toml b/Cargo.toml index f9c34be..8f95b1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ build = "build.rs" [dependencies] cairo-rs = "0.4" -pango = "0.4" pango-sys = "0.6" pangocairo = "0.5" pangocairo-sys = "0.7" @@ -51,6 +50,10 @@ atty = "0.2" [build-dependencies] phf_codegen = "0.7" +[dependencies.pango] +features = ["v1_38"] +version = "0.4" + [dependencies.gtk] version = "0.4" features = ["v3_22"] diff --git a/runtime/plugin/nvim_gui_shim.vim b/runtime/plugin/nvim_gui_shim.vim index 2815e55..1732a97 100644 --- a/runtime/plugin/nvim_gui_shim.vim +++ b/runtime/plugin/nvim_gui_shim.vim @@ -52,8 +52,10 @@ function s:GuiFontCommand(fname, bang) abort call GuiFont(a:fname, a:bang ==# '!') endif endfunction -command! -nargs=? -bang Guifont call s:GuiFontCommand("", "") -command! -nargs=? -bang GuiFont call s:GuiFontCommand("", "") +command! -nargs=1 -bang Guifont call s:GuiFontCommand("", "") +command! -nargs=1 -bang GuiFont call s:GuiFontCommand("", "") + +command! -nargs=? GuiFontFeatures call rpcnotify(1, 'Gui', 'FontFeatures', ) command! NGToggleSidebar call rpcnotify(1, 'Gui', 'Command', 'ToggleSidebar') diff --git a/src/nvim/redraw_handler.rs b/src/nvim/redraw_handler.rs index 874dff5..245a579 100644 --- a/src/nvim/redraw_handler.rs +++ b/src/nvim/redraw_handler.rs @@ -86,6 +86,7 @@ pub fn call_gui_event( ) -> result::Result<(), String> { match method { "Font" => call!(ui->set_font(args: str)), + "FontFeatures" => call!(ui->set_font_features(args: str)), "Clipboard" => match try_str!(args[0]) { "Set" => match try_str!(args[1]) { "*" => ui.clipboard_primary_set(try_str!(args[2])), diff --git a/src/render/context.rs b/src/render/context.rs index 3262151..2c24a65 100644 --- a/src/render/context.rs +++ b/src/render/context.rs @@ -8,18 +8,24 @@ use ui_model::StyledLine; use super::itemize::ItemizeIterator; pub struct Context { - state: ContextState, + font_metrics: FontMetrix, + font_features: FontFeatures, } impl Context { pub fn new(pango_context: pango::Context) -> Self { Context { - state: ContextState::new(pango_context), + font_metrics: FontMetrix::new(pango_context), + font_features: FontFeatures::new(), } } pub fn update(&mut self, pango_context: pango::Context) { - self.state = ContextState::new(pango_context); + self.font_metrics = FontMetrix::new(pango_context); + } + + pub fn update_font_features(&mut self, font_features: FontFeatures) { + self.font_features = font_features; } pub fn itemize(&self, line: &StyledLine) -> Vec { @@ -28,7 +34,7 @@ impl Context { ItemizeIterator::new(&line.line_str) .flat_map(|(offset, len)| { sys_pango::pango_itemize( - &self.state.pango_context, + &self.font_metrics.pango_context, &line.line_str, offset, len, @@ -40,32 +46,34 @@ impl Context { } pub fn create_layout(&self) -> pango::Layout { - pango::Layout::new(&self.state.pango_context) + pango::Layout::new(&self.font_metrics.pango_context) } - #[inline] pub fn font_description(&self) -> &pango::FontDescription { - &self.state.font_desc + &self.font_metrics.font_desc } - #[inline] pub fn cell_metrics(&self) -> &CellMetrics { - &self.state.cell_metrics + &self.font_metrics.cell_metrics + } + + pub fn font_features(&self) -> &FontFeatures { + &self.font_features } } -struct ContextState { +struct FontMetrix { pango_context: pango::Context, cell_metrics: CellMetrics, font_desc: pango::FontDescription, } -impl ContextState { +impl FontMetrix { pub fn new(pango_context: pango::Context) -> Self { let font_metrics = pango_context.get_metrics(None, None).unwrap(); let font_desc = pango_context.get_font_description().unwrap(); - ContextState { + FontMetrix { pango_context, cell_metrics: CellMetrics::new(&font_metrics), font_desc, @@ -115,3 +123,34 @@ impl CellMetrics { } } } + +pub struct FontFeatures { + features: Option, +} + +impl FontFeatures { + pub fn new() -> Self { + FontFeatures { + features: None, + } + } + + pub fn from(font_features: String) -> Self { + if font_features.trim().is_empty() { + return Self::new(); + } + + FontFeatures { + features: Some(font_features) + } + } + + pub fn insert_attr(&self, attr_list: &pango::AttrList, end_idx: usize) { + if let Some(ref features) = self.features { + let mut attr = sys_pango::attribute::new_features(features).unwrap(); + attr.set_start_index(0); + attr.set_end_index(end_idx as u32); + attr_list.insert(attr); + } + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs index 19fecbb..dc7ccb9 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -2,7 +2,7 @@ mod context; mod itemize; mod model_clip_iterator; -pub use self::context::Context; +pub use self::context::{Context, FontFeatures}; pub use self::context::CellMetrics; use self::model_clip_iterator::{ModelClipIteratorFactory, RowView}; @@ -210,7 +210,7 @@ pub fn shape_dirty( ) { for line in ui_model.model_mut() { if line.dirty_line { - let styled_line = ui_model::StyledLine::from(line, color_model); + let styled_line = ui_model::StyledLine::from(line, color_model, ctx.font_features()); let items = ctx.itemize(&styled_line); line.merge(&styled_line, &items); diff --git a/src/shell.rs b/src/shell.rs index 7101d95..99c12dc 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -276,6 +276,17 @@ impl State { self.on_redraw(&RepaintMode::All); } + pub fn set_font_features(&mut self, font_features: String) { + let font_features = render::FontFeatures::from(font_features); + + self.render_state + .borrow_mut() + .font_ctx + .update_font_features(font_features); + self.model.clear_glyphs(); + self.on_redraw(&RepaintMode::All); + } + pub fn open_file(&self, path: &str) { if let Some(mut nvim) = self.nvim() { nvim.command_async(&format!("e {}", path)) diff --git a/src/sys/pango/attribute.rs b/src/sys/pango/attribute.rs new file mode 100644 index 0000000..98501f4 --- /dev/null +++ b/src/sys/pango/attribute.rs @@ -0,0 +1,12 @@ +use pango_sys; +use pango; + +use glib::translate::*; + +pub fn new_features(features: &str) -> Option { + unsafe { + from_glib_full(pango_sys::pango_attr_font_features_new( + features.to_glib_none().0, + )) + } +} diff --git a/src/sys/pango/mod.rs b/src/sys/pango/mod.rs index c0a008d..c261e5a 100644 --- a/src/sys/pango/mod.rs +++ b/src/sys/pango/mod.rs @@ -2,6 +2,8 @@ mod item; mod analysis; mod attr_iterator; +pub mod attribute; + pub use self::item::Item; pub use self::analysis::Analysis; pub use self::attr_iterator::{AttrIterator, AttrIteratorFactory}; diff --git a/src/ui_model/line.rs b/src/ui_model/line.rs index 9b026ab..fbf8e95 100644 --- a/src/ui_model/line.rs +++ b/src/ui_model/line.rs @@ -1,10 +1,12 @@ use std::ops::{Index, IndexMut}; +use sys::pango as sys_pango; +use pango; + +use render; use color; use super::cell::Cell; use super::item::Item; -use sys::pango as sys_pango; -use pango; pub struct Line { pub line: Box<[Cell]>, @@ -242,8 +244,7 @@ pub struct StyledLine { } impl StyledLine { - pub fn from(line: &Line, color_model: &color::ColorModel) -> Self { - + pub fn from(line: &Line, color_model: &color::ColorModel, font_features: &render::FontFeatures) -> Self { let average_capacity = line.line.len() * 4 * 2; // code bytes * grapheme cluster let mut line_str = String::with_capacity(average_capacity); @@ -278,6 +279,7 @@ impl StyledLine { } style_attr.insert(&attr_list); + font_features.insert_attr(&attr_list, line_str.len()); StyledLine { line_str, @@ -410,7 +412,7 @@ mod tests { line[1].ch = "b".to_owned(); line[2].ch = "c".to_owned(); - let styled_line = StyledLine::from(&line, &color::ColorModel::new()); + let styled_line = StyledLine::from(&line, &color::ColorModel::new(), &render::FontFeatures::new()); assert_eq!("abc", styled_line.line_str); assert_eq!(3, styled_line.cell_to_byte.len()); assert_eq!(0, styled_line.cell_to_byte[0]);