diff --git a/src/cursor.rs b/src/cursor.rs index 1b846db..0de86c0 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -276,7 +276,12 @@ mod tests { let line_height = 30.0; let line_y = 0.0; - let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, false); + let (y, width, height) = cursor_rect( + &mode, + &CellMetrics::new_hw(line_height, char_width), + line_y, + false, + ); assert_eq!(line_y + line_height - line_height / 4.0, y); assert_eq!(char_width, width); assert_eq!(line_height / 4.0, height); @@ -295,7 +300,12 @@ mod tests { let line_height = 30.0; let line_y = 0.0; - let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, true); + let (y, width, height) = cursor_rect( + &mode, + &CellMetrics::new_hw(line_height, char_width), + line_y, + true, + ); assert_eq!(line_y + line_height - line_height / 4.0, y); assert_eq!(char_width * 2.0, width); assert_eq!(line_height / 4.0, height); @@ -314,7 +324,12 @@ mod tests { let line_height = 30.0; let line_y = 0.0; - let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, false); + let (y, width, height) = cursor_rect( + &mode, + &CellMetrics::new_hw(line_height, char_width), + line_y, + false, + ); assert_eq!(line_y, y); assert_eq!(char_width / 4.0, width); assert_eq!(line_height, height); diff --git a/src/render/context.rs b/src/render/context.rs index 963d97b..74eb7bb 100644 --- a/src/render/context.rs +++ b/src/render/context.rs @@ -3,8 +3,10 @@ use pango::prelude::*; use pango; use sys::pango as sys_pango; +use sys::pango::AttrIteratorFactory; use ui_model::StyledLine; +use super::itemize::ItemizeIterator; pub struct Context { state: ContextState, @@ -20,11 +22,20 @@ impl Context { } pub fn itemize(&self, line: &StyledLine) -> Vec { - sys_pango::pango_itemize( - &self.state.pango_context, - line.line_str.trim_right(), - &line.attr_list, - ) + let mut attr_iter = line.attr_list.get_iterator(); + + ItemizeIterator::new(&line.line_str) + .flat_map(|(offset, len)| { + sys_pango::pango_itemize( + &self.state.pango_context, + &line.line_str, + offset, + len, + &line.attr_list, + Some(&mut attr_iter), + ) + }) + .collect() } #[inline] @@ -94,4 +105,18 @@ impl CellMetrics { pango::SCALE as f64, } } + + #[cfg(test)] + pub fn new_hw(line_height: f64, char_width: f64) -> Self { + CellMetrics { + pango_ascent: 0, + pango_descent: 0, + pango_char_width: 0, + ascent: 0.0, + line_height, + char_width, + underline_position: 0.0, + underline_thickness: 0.0, + } + } } diff --git a/src/render/itemize.rs b/src/render/itemize.rs new file mode 100644 index 0000000..9a5d703 --- /dev/null +++ b/src/render/itemize.rs @@ -0,0 +1,58 @@ +use std::str::CharIndices; + +pub struct ItemizeIterator <'a> { + char_iter: CharIndices<'a>, + line: &'a str, +} + +impl <'a> ItemizeIterator <'a> { + pub fn new(line: &'a str) -> Self { + ItemizeIterator { + char_iter: line.char_indices(), + line, + } + } +} + +impl <'a> Iterator for ItemizeIterator<'a> { + type Item = (usize, usize); + + fn next(&mut self) -> Option { + let mut start_index = None; + + let end_index = loop { + if let Some((index, ch)) = self.char_iter.next() { + let is_whitespace = ch.is_whitespace(); + + if start_index.is_none() && !is_whitespace { + start_index = Some(index); + } + if start_index.is_some() && is_whitespace { + break index; + } + } else { + break self.line.len(); + } + }; + + if let Some(start_index) = start_index { + Some((start_index, end_index - start_index)) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_iterator() { + let mut iter = ItemizeIterator::new("Test line "); + + assert_eq!(Some((0, 4)), iter.next()); + assert_eq!(Some((6, 4)), iter.next()); + assert_eq!(None, iter.next()); + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs index 139ec28..73218a6 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,4 +1,5 @@ mod context; +mod itemize; pub use self::context::Context; pub use self::context::CellMetrics; @@ -77,6 +78,7 @@ pub fn render( if cell.attrs.underline || cell.attrs.undercurl { if cell.attrs.undercurl { + // TODO: properly draw undercurl let sp = color_model.actual_cell_sp(cell); ctx.set_source_rgba(sp.0, sp.1, sp.2, 0.7); ctx.set_dash(&[4.0, 2.0], 0.0); diff --git a/src/shell.rs b/src/shell.rs index 0209c73..f7391e3 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -679,60 +679,6 @@ fn init_nvim(state_arc: &Arc>) { } } -//#[inline] -//fn get_model_clip( -// state: &State, -// line_height: f64, -// char_width: f64, -// clip: (f64, f64, f64, f64), -//) -> ModelRect { -// let mut model_clip = -// ModelRect::from_area(line_height, char_width, clip.0, clip.1, clip.2, clip.3); -// // in some cases symbols from previous row affect next row -// // for example underscore symbol or 'g' -// // also for italic text it is possible that symbol can affect next one -// // see deference between logical rect and ink rect -// model_clip.extend(1, 0, 1, 0); -// state.model.limit_to_model(&mut model_clip); -// -// model_clip -//} -// -//#[inline] -//fn draw_backgound( -// state: &State, -// draw_bitmap: &ModelBitamp, -// ctx: &cairo::Context, -// line_height: f64, -// char_width: f64, -// model_clip: &ModelRect, -//) { -// let line_x = model_clip.left as f64 * char_width; -// let mut line_y: f64 = model_clip.top as f64 * line_height; -// -// for (line_idx, line) in state.model.clip_model(model_clip) { -// ctx.move_to(line_x, line_y); -// -// for (col_idx, cell) in line.iter() { -// let current_point = ctx.get_current_point(); -// -// if !draw_bitmap.get(col_idx, line_idx) { -// let (bg, _) = state.colors(cell); -// -// if &state.bg_color != bg { -// ctx.set_source_rgb(bg.0, bg.1, bg.2); -// ctx.rectangle(current_point.0, current_point.1, char_width, line_height); -// ctx.fill(); -// } -// -// } -// -// ctx.move_to(current_point.0 + char_width, current_point.1); -// } -// line_y += line_height; -// } -//} - fn draw_initializing(state: &State, ctx: &cairo::Context) { let layout = ctx.create_pango_layout(); let desc = state.get_font_desc(); @@ -773,142 +719,6 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) { ); } -//fn draw(state: &State, ctx: &cairo::Context) { -// let layout = ctx.create_pango_layout(); -// let mut desc = state.create_pango_font(); -// let mut buf = String::with_capacity(4); -// -// let (row, col) = state.model.get_cursor(); -// -// let line_height = state.line_height.unwrap(); -// let char_width = state.char_width.unwrap(); -// let mut draw_bitmap = ModelBitamp::new(state.model.columns, state.model.rows); -// -// ctx.set_source_rgb(state.bg_color.0, state.bg_color.1, state.bg_color.2); -// ctx.paint(); -// -// let clip_rects = &ctx.copy_clip_rectangle_list().rectangles; -// for clip_idx in 0..clip_rects.len() { -// let clip = clip_rects.get(clip_idx).unwrap(); -// -// let model_clip = get_model_clip(state, line_height, char_width, ( -// clip.x, -// clip.y, -// clip.x + clip.width, -// clip.y + clip.height, -// )); -// -// let line_x = model_clip.left as f64 * char_width; -// let mut line_y: f64 = model_clip.top as f64 * line_height; -// -// draw_backgound( -// state, -// &draw_bitmap, -// ctx, -// line_height, -// char_width, -// &model_clip, -// ); -// -// for (line_idx, line) in state.model.clip_model(&model_clip) { -// -// ctx.move_to(line_x, line_y); -// -// for (col_idx, cell) in line.iter() { -// let current_point = ctx.get_current_point(); -// -// if !draw_bitmap.get(col_idx, line_idx) { -// let double_width = line.is_double_width(col_idx); -// -// let (bg, fg) = state.colors(cell); -// -// if row == line_idx && col == col_idx { -// state.cursor.as_ref().unwrap().draw( -// ctx, -// state, -// char_width, -// line_height, -// line_y, -// double_width, -// bg, -// ); -// -// ctx.move_to(current_point.0, current_point.1); -// } -// -// -// if !cell.ch.is_whitespace() { -// update_font_description(&mut desc, &cell.attrs); -// -// layout.set_font_description(&desc); -// buf.clear(); -// buf.push(cell.ch); -// layout.set_text(&buf); -// -// // correct layout for double_width chars -// if double_width { -// let (dw_width, dw_height) = layout.get_pixel_size(); -// let x_offset = (char_width * 2.0 - dw_width as f64) / 2.0; -// let y_offset = (line_height - dw_height as f64) / 2.0; -// ctx.rel_move_to(x_offset, y_offset); -// } -// -// ctx.set_source_rgb(fg.0, fg.1, fg.2); -// ctx.update_pango_layout(&layout); -// ctx.show_pango_layout(&layout); -// } -// -// if cell.attrs.underline || cell.attrs.undercurl { -// // [TODO]: Current gtk-rs bindings does not provide fontmetrics access -// // so it is not possible to find right position for underline or undercurl position -// // > update_font_description(&mut desc, &cell.attrs); -// // > layout.get_context().unwrap().get_metrics(); -// let top_offset = line_height * 0.9; -// -// if cell.attrs.undercurl { -// let sp = if let Some(ref sp) = cell.attrs.special { -// sp -// } else { -// &state.sp_color -// }; -// ctx.set_source_rgba(sp.0, sp.1, sp.2, 0.7); -// ctx.set_dash(&[4.0, 2.0], 0.0); -// ctx.set_line_width(2.0); -// ctx.move_to(current_point.0, line_y + top_offset); -// ctx.line_to(current_point.0 + char_width, line_y + top_offset); -// ctx.stroke(); -// ctx.set_dash(&[], 0.0); -// } else if cell.attrs.underline { -// ctx.set_source_rgb(fg.0, fg.1, fg.2); -// ctx.set_line_width(1.0); -// ctx.move_to(current_point.0, line_y + top_offset); -// ctx.line_to(current_point.0 + char_width, line_y + top_offset); -// ctx.stroke(); -// } -// } -// } -// -// ctx.move_to(current_point.0 + char_width, current_point.1); -// } -// -// line_y += line_height; -// } -// -// draw_bitmap.fill_from_model(&model_clip); -// } -//} - -//#[inline] -//fn update_font_description(desc: &mut FontDescription, attrs: &Attrs) { -// desc.unset_fields(pango::FONT_MASK_STYLE | pango::FONT_MASK_WEIGHT); -// if attrs.italic { -// desc.set_style(pango::Style::Italic); -// } -// if attrs.bold { -// desc.set_weight(pango::Weight::Bold); -// } -//} - fn request_window_resize(state: &mut State) { if !state.request_resize { return; diff --git a/src/sys/pango/attr_iterator.rs b/src/sys/pango/attr_iterator.rs new file mode 100644 index 0000000..6d066a2 --- /dev/null +++ b/src/sys/pango/attr_iterator.rs @@ -0,0 +1,31 @@ +use std::ptr; +use std::mem; + +use pango_sys; +use pango; + +use glib_ffi; +use glib::translate::*; + +glib_wrapper! { + pub struct AttrIterator(Boxed); + + match fn { + copy => |ptr| pango_sys::pango_attr_iterator_copy(ptr as *mut _), + free => |ptr| pango_sys::pango_attr_iterator_destroy(ptr), + } +} + +pub trait AttrIteratorFactory { + fn get_iterator(&self) -> AttrIterator; +} + +impl AttrIteratorFactory for pango::AttrList { + fn get_iterator(&self) -> AttrIterator { + unsafe { + from_glib_none(pango_sys::pango_attr_list_get_iterator( + self.to_glib_none().0, + )) + } + } +} diff --git a/src/sys/pango/mod.rs b/src/sys/pango/mod.rs index 01c0788..a67224a 100644 --- a/src/sys/pango/mod.rs +++ b/src/sys/pango/mod.rs @@ -1,8 +1,10 @@ mod item; mod analysis; +mod attr_iterator; pub use self::item::Item; pub use self::analysis::Analysis; +pub use self::attr_iterator::{AttrIterator, AttrIteratorFactory}; use std::ptr; @@ -14,16 +16,19 @@ use glib::translate::*; pub fn pango_itemize( context: &pango::Context, text: &str, - attrs: &pango::AttrList + start_index: usize, + length: usize, + attrs: &pango::AttrList, + cached_iter: Option<&mut AttrIterator>, ) -> Vec { unsafe { FromGlibPtrContainer::from_glib_container(pango_sys::pango_itemize( context.to_glib_none().0, text.as_ptr() as *const i8, - 0, - text.len() as i32, + start_index as i32, + length as i32, attrs.to_glib_none().0, - ptr::null_mut(), + cached_iter.map(|iter| iter.to_glib_none_mut().0).unwrap_or(ptr::null_mut()), )) } } diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index d66e934..98d2822 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -292,17 +292,6 @@ mod tests { assert_eq!(19, rect.right); } - #[test] - fn test_repaint_rect() { - let rect = ModelRect::point(1, 1); - let (x, y, width, height) = rect.to_area(10.0, 5.0); - - assert_eq!(5, x); - assert_eq!(10, y); - assert_eq!(5, width); - assert_eq!(10, height); - } - #[test] fn test_put_area() { let mut model = UiModel::new(10, 20); diff --git a/src/ui_model/model_rect.rs b/src/ui_model/model_rect.rs index 3e95751..886d995 100644 --- a/src/ui_model/model_rect.rs +++ b/src/ui_model/model_rect.rs @@ -263,3 +263,19 @@ impl AsRef for ModelRect { self } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_repaint_rect() { + let rect = ModelRect::point(1, 1); + let (x, y, width, height) = rect.to_area(&CellMetrics::new_hw(10.0, 5.0)); + + assert_eq!(5, x); + assert_eq!(10, y); + assert_eq!(5, width); + assert_eq!(10, height); + } +}