pango_itemize/pango_shape

This commit is contained in:
daa84 2017-08-25 18:32:30 +03:00
parent 17063d5216
commit d6f6b04ca3
11 changed files with 210 additions and 52 deletions

1
Cargo.lock generated
View File

@ -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)",

View File

@ -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' }

View File

@ -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;

View File

@ -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<item::Item> {
pub fn itemize(&self, line: &StyledLine) -> Vec<item::Item> {
pango_itemize(&self.pango_context, &line.line_str, &line.attr_list)
}
}

View File

@ -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);
//}
//}

View File

@ -621,31 +621,30 @@ fn update_line_metrics(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) {
}
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, 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<UiMutex<State>>) {

View File

@ -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)
}
}

View File

@ -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,
);

View File

@ -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,
}
}

View File

@ -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<pango::GlyphString>,
pub item: sys_pango::Item,
pub glyphs: Option<pango::GlyphString>,
}
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<Box<[Item]>>,
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>]>,
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;
}

View File

@ -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);