From d6f6b04ca3c9b38e4986208b61b75f11171b5c6f Mon Sep 17 00:00:00 2001 From: daa84 Date: Fri, 25 Aug 2017 18:32:30 +0300 Subject: [PATCH] pango_itemize/pango_shape --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 1 + src/render/context.rs | 6 +-- src/render/mod.rs | 87 ++++++++++++++++++++++++++++------- src/shell.rs | 31 +++++++------ src/sys/pango/item.rs | 6 +++ src/sys/pango/mod.rs | 9 ++-- src/ui_model/cell.rs | 6 ++- src/ui_model/line.rs | 102 +++++++++++++++++++++++++++++++++++++----- src/ui_model/mod.rs | 12 ++++- 11 files changed, 210 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0bae01a..7c2d413 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,7 @@ dependencies = [ "gio 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index b00bb6c..0008642 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ glib-sys = "0.4" gdk = "0.6" gdk-sys = "0.4" gio = "0.2" +gobject-sys = "0.4" #gdk = { git = 'https://github.com/gtk-rs/gdk' } #gdk-sys = { git = 'https://github.com/gtk-rs/sys' } #glib = { git = 'https://github.com/gtk-rs/glib' } diff --git a/src/main.rs b/src/main.rs index 84c49c5..1216c48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ extern crate gdk_sys; #[macro_use] extern crate glib; extern crate glib_sys as glib_ffi; +extern crate gobject_sys as gobject_ffi; extern crate cairo; extern crate pango; extern crate pango_sys; diff --git a/src/render/context.rs b/src/render/context.rs index 7e319d4..7691847 100644 --- a/src/render/context.rs +++ b/src/render/context.rs @@ -14,16 +14,14 @@ pub struct Context { impl Context { pub fn new(font_desc: &pango::FontDescription) -> Self { - Context { - pango_context: create_pango_context(font_desc), - } + Context { pango_context: create_pango_context(font_desc) } } pub fn update(&mut self, font_desc: &pango::FontDescription) { self.pango_context = create_pango_context(font_desc); } - pub fn itemize(&self, line: &StyledLine)-> Vec { + pub fn itemize(&self, line: &StyledLine) -> Vec { pango_itemize(&self.pango_context, &line.line_str, &line.attr_list) } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 1003592..83b3a6a 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -8,23 +8,80 @@ use pangocairo::{CairoContextExt, FontMap}; use std::ffi::CString; use ui_model; -pub fn render(ctx: &cairo::Context, font_desc: pango::FontDescription) { - let font_map = FontMap::get_default(); - let pango_context = font_map.create_context().unwrap(); - pango_context.set_font_description(&font_desc); +pub fn render( + ctx: &cairo::Context, + font_desc: pango::FontDescription, + ui_model: &mut ui_model::UiModel, +) { + let font_ctx = context::Context::new(&font_desc); - let text = "TEST String".to_owned(); - let attr_list = pango::AttrList::new(); + shape_dirty(&font_ctx, ui_model); - ctx.move_to(0.0, 50.0); - let items = pango_itemize(&pango_context, &text, &attr_list); - for item in items { - let mut glyphs = pango::GlyphString::new(); - let analysis = item.analysis(); - pango_shape(&text, &analysis, &mut glyphs); - let font = analysis.font(); - let (ink, logical) = glyphs.extents(&font); - ctx.show_glyph_string(&font, &glyphs); + + for line in ui_model.model_mut() { + for i in 0..line.line.len() { + let item = line.item_line[i].as_ref(); + if let Some(item) = item { + if let Some(ref glyphs) = item.glyphs { + let analysis = item.item.analysis(); + let font = analysis.font(); + ctx.show_glyph_string(&font, glyphs); + } + } + } } } +fn shape_dirty(ctx: &context::Context, ui_model: &mut ui_model::UiModel) { + for line in ui_model.model_mut() { + if line.dirty_line { + let styled_line = ui_model::StyledLine::from(line); + let items = ctx.itemize(&styled_line); + line.merge(&styled_line, &items); + + for i in 0..line.line.len() { + if line[i].dirty { + let mut item = line.get_item_mut(i).unwrap(); + let mut glyphs = pango::GlyphString::new(); + { + let analysis = item.item.analysis(); + let (offset, length, _) = item.item.offset(); + pango_shape( + &styled_line.line_str, + offset, + length, + &analysis, + &mut glyphs, + ); + } + + item.set_glyphs(glyphs); + } + + line[i].dirty = false; + } + + line.dirty_line = false; + } + } +} + +//pub fn render_test(ctx: &cairo::Context, font_desc: pango::FontDescription) { +//let font_map = FontMap::get_default(); +//let pango_context = font_map.create_context().unwrap(); +//pango_context.set_font_description(&font_desc); + +//let text = "TEST String".to_owned(); +//let attr_list = pango::AttrList::new(); + +//ctx.move_to(0.0, 50.0); +//let items = pango_itemize(&pango_context, &text, &attr_list); +//for item in items { +//let mut glyphs = pango::GlyphString::new(); +//let analysis = item.analysis(); +//pango_shape(&text, &analysis, &mut glyphs); +//let font = analysis.font(); +//let (ink, logical) = glyphs.extents(&font); +//ctx.show_glyph_string(&font, &glyphs); +//} +//} diff --git a/src/shell.rs b/src/shell.rs index c79fd08..c950232 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -621,31 +621,30 @@ fn update_line_metrics(state_arc: &Arc>, ctx: &cairo::Context) { } fn gtk_draw(state_arc: &Arc>, ctx: &cairo::Context) -> Inhibit { - //update_line_metrics(state_arc, ctx); + update_line_metrics(state_arc, ctx); - //if state_arc.borrow_mut().request_nvim_resize { - // try_nvim_resize(state_arc); - //} + if state_arc.borrow_mut().request_nvim_resize { + try_nvim_resize(state_arc); + } - //init_nvim(state_arc); - - //let mut state = state_arc.borrow_mut(); - //if state.nvim.borrow().is_initialized() { - // draw(&*state, ctx); - // request_window_resize(&mut *state); - //} else if state.nvim.borrow().is_initializing() { - // draw_initializing(&*state, ctx); - //} + init_nvim(state_arc); let mut state = state_arc.borrow_mut(); - render(&*state, ctx); + if state.nvim.borrow().is_initialized() { + // draw(&*state, ctx); + render(&mut *state, ctx); + request_window_resize(&mut *state); + } else if state.nvim.borrow().is_initializing() { + draw_initializing(&*state, ctx); + } + Inhibit(false) } -fn render(state: &State, ctx: &cairo::Context) { +fn render(state: &mut State, ctx: &cairo::Context) { let font_desc = state.create_pango_font(); - render::render(ctx, font_desc); + render::render(ctx, font_desc, &mut state.model); } fn show_nvim_start_error(err: nvim::NvimInitError, state_arc: Arc>) { diff --git a/src/sys/pango/item.rs b/src/sys/pango/item.rs index 45e3ca6..a98b35d 100644 --- a/src/sys/pango/item.rs +++ b/src/sys/pango/item.rs @@ -4,6 +4,7 @@ use std::mem; use pango_sys; use glib_ffi; +use gobject_ffi; use glib::translate::*; use super::analysis; @@ -14,6 +15,7 @@ glib_wrapper! { match fn { copy => |ptr| pango_sys::pango_item_copy(ptr as *mut pango_sys::PangoItem), free => |ptr| pango_sys::pango_item_free(ptr), + get_type => || pango_sys::pango_item_get_type(), } } @@ -21,4 +23,8 @@ impl Item { pub fn analysis(&self) -> analysis::Analysis { analysis::Analysis::from(&self.0.analysis) } + + pub fn offset(&self) -> (usize, usize, usize) { + (self.0.offset as usize, self.0.length as usize, self.0.num_chars as usize) + } } diff --git a/src/sys/pango/mod.rs b/src/sys/pango/mod.rs index a6f09f1..65952c3 100644 --- a/src/sys/pango/mod.rs +++ b/src/sys/pango/mod.rs @@ -2,7 +2,6 @@ pub mod item; mod analysis; use std::ptr; -use std::ffi::CStr; use pango; use pango_sys; @@ -28,13 +27,17 @@ pub fn pango_itemize( pub fn pango_shape( text: &String, + offset: usize, + length: usize, analysis: &analysis::Analysis, glyphs: &mut pango::GlyphString, ) { + debug_assert!(offset + length <= text.len()); + unsafe { pango_sys::pango_shape( - text.as_ptr() as *const i8, - text.len() as i32, + (text.as_ptr() as *const i8).offset(offset as isize), + length as i32, analysis.to_glib_ptr(), glyphs.to_glib_none_mut().0, ); diff --git a/src/ui_model/cell.rs b/src/ui_model/cell.rs index 1825ed2..c2ca3b6 100644 --- a/src/ui_model/cell.rs +++ b/src/ui_model/cell.rs @@ -43,15 +43,17 @@ impl Attrs { #[derive(Clone)] pub struct Cell { - pub ch: char, pub attrs: Attrs, + pub ch: char, + pub dirty: bool, } impl Cell { pub fn new(ch: char) -> Cell { Cell { - ch: ch, attrs: Attrs::new(), + ch: ch, + dirty: false, } } diff --git a/src/ui_model/line.rs b/src/ui_model/line.rs index 5972f7e..3bee3fa 100644 --- a/src/ui_model/line.rs +++ b/src/ui_model/line.rs @@ -1,26 +1,42 @@ use std::ops::{Index, IndexMut}; use super::cell::Cell; +use sys::pango::item as sys_pango; use pango; pub struct Item { - item: pango::Item, - glyph_string: Option, + pub item: sys_pango::Item, + pub glyphs: Option, } impl Item { - pub fn new(item: pango::Item) -> Self { + pub fn new(item: sys_pango::Item) -> Self { Item { item, - glyph_string: None, + glyphs: None, } } + + pub fn update(&mut self, item: sys_pango::Item) { + self.item = item; + self.glyphs = None; + } + + pub fn set_glyphs(&mut self, glyphs: pango::GlyphString) { + self.glyphs = Some(glyphs); + } } pub struct Line { pub line: Box<[Cell]>, - item_line: Option>, - cell_to_item: Box<[usize]>, + + // format of item line is + // [Item1, Item2, None, None, Item3] + // Item2 take 3 cells and renders as one + pub item_line: Box<[Option]>, + + item_line_empty: bool, + pub dirty_line: bool, } impl Line { @@ -29,11 +45,16 @@ impl Line { for _ in 0..columns { line.push(Cell::new(' ')); } + let mut item_line = Vec::with_capacity(columns); + for _ in 0..columns { + item_line.push(None); + } Line { - cell_to_item: Vec::with_capacity(line.len()).into_boxed_slice(), line: line.into_boxed_slice(), - item_line: None, + item_line: item_line.into_boxed_slice(), + dirty_line: false, + item_line_empty: true, } } @@ -41,7 +62,63 @@ impl Line { for cell in &mut self.line[left..right + 1] { cell.clear(); } + } + pub fn merge(&mut self, old_items: &StyledLine, new_items: &[sys_pango::Item]) { + for new_item in new_items { + let (offset, length, _) = new_item.offset(); + let start_cell = old_items.cell_to_byte[offset]; + let end_cell = old_items.cell_to_byte[offset + length - 1]; + + // first time initialization + // as cell_to_item is to slow in this case + if !self.item_line_empty { + let start_item = self.cell_to_item(start_cell); + let end_item = self.cell_to_item(end_cell); + + // in case different item length was in previous iteration + // mark all item as dirty + if start_item != end_item { + self.initialize_cells(start_cell, end_cell, new_item); + } else { + self.item_line[offset].as_mut().unwrap().update( + new_item.clone(), + ); + } + } else { + self.initialize_cells(start_cell, end_cell, new_item); + } + } + + self.item_line_empty = false; + } + + fn initialize_cells(&mut self, start_cell: usize, end_cell: usize, new_item: &sys_pango::Item) { + for i in start_cell..end_cell { + self.line[i].dirty = true; + } + for i in start_cell + 1..end_cell { + self.item_line[i] = None; + } + self.item_line[start_cell] = Some(Item::new(new_item.clone())); + } + + pub fn mark_dirty_cell(&mut self, idx: usize) { + self.line[idx].dirty = true; + } + + pub fn get_item_mut(&mut self, cell_idx: usize) -> Option<&mut Item> { + self.item_line[ self.cell_to_item(cell_idx) ].as_mut() + } + + fn cell_to_item(&self, cell_idx: usize) -> usize { + for i in (cell_idx..0).rev() { + if self.item_line[i].is_some() { + return i; + } + } + + unreachable!(); } } @@ -80,11 +157,16 @@ impl StyledLine { line_str.push(cell.ch); let len = line_str.len(); - for i in byte_offset..byte_offset + len { + for _ in byte_offset..byte_offset + len { cell_to_byte.push(cell_idx); } - insert_attrs(cell, &attr_list, byte_offset as u32, (byte_offset + len) as u32); + insert_attrs( + cell, + &attr_list, + byte_offset as u32, + (byte_offset + len) as u32, + ); byte_offset += len; } diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 202ba1f..6a5864d 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -22,7 +22,7 @@ pub struct UiModel { impl UiModel { pub fn new(rows: u64, columns: u64) -> UiModel { let mut model = Vec::with_capacity(rows as usize); - for i in 0..rows as usize { + for _ in 0..rows as usize { model.push(Line::new(columns as usize)); } @@ -43,6 +43,10 @@ impl UiModel { &self.model } + pub fn model_mut(&mut self) -> &mut [Line] { + &mut self.model + } + pub fn limit_to_model(&self, clip: &mut ModelRect) { clip.left = if clip.left >= self.columns { self.columns - 1 @@ -92,7 +96,11 @@ impl UiModel { pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) -> ModelRect { let mut changed_region = self.cur_point(); - let mut cell = &mut self.model[self.cur_row][self.cur_col]; + let mut line = &mut self.model[self.cur_row]; + line.dirty_line = true; + line.mark_dirty_cell(self.cur_col); + + let mut cell = &mut line[self.cur_col]; cell.ch = text.chars().last().unwrap_or(' '); cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(Attrs::new);