pango_itemize/pango_shape
This commit is contained in:
parent
17063d5216
commit
d6f6b04ca3
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -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)",
|
||||
|
@ -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' }
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
//}
|
||||
//}
|
||||
|
31
src/shell.rs
31
src/shell.rs
@ -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>>) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user