From a92b42f784faa22390cb9fe0c7c1d2e9dd07ad4f Mon Sep 17 00:00:00 2001 From: daa Date: Fri, 17 Mar 2017 23:38:12 +0300 Subject: [PATCH 1/9] Calculate changed area for given event --- src/shell.rs | 2 +- src/ui_model.rs | 93 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/shell.rs b/src/shell.rs index 1bd81c1..b8242ed 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -482,7 +482,7 @@ impl RedrawEvents for Shell { } fn on_scroll(&mut self, count: i64) { - self.model.scroll(count); + self.model.scroll(count as usize); } fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) { diff --git a/src/ui_model.rs b/src/ui_model.rs index 125b0d0..59e1ee6 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -120,21 +120,32 @@ impl UiModel { &self.model } - pub fn set_cursor(&mut self, row: u64, col: u64) { + pub fn set_cursor(&mut self, row: u64, col: u64) -> ModelRect { + let mut changed_region = ModelRect::point(self.cur_row, self.cur_col); + self.cur_row = row as usize; self.cur_col = col as usize; + + changed_region.join(&ModelRect::point(self.cur_row, self.cur_col)); + + changed_region + } pub fn get_cursor(&self) -> (usize, usize) { (self.cur_row, self.cur_col) } - pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) { + pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) -> ModelRect { let mut cell = &mut self.model[self.cur_row][self.cur_col]; + let changed_region = ModelRect::point(self.cur_row, self.cur_col); + cell.ch = text.chars().last().unwrap_or(' '); cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(|| Attrs::new()); cell.attrs.double_width = text.len() == 0; self.cur_col += 1; + + changed_region } pub fn set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) { @@ -144,42 +155,49 @@ impl UiModel { self.right = right as usize; } - fn copy_row(&mut self, row: usize, offset: i64, left: usize, right: usize) { + #[inline] + fn copy_row(&mut self, row: usize, offset: usize, left: usize, right: usize) { for col in left..right + 1 { - let from_row = (row as i64 + offset) as usize; + let from_row = row + offset; let from_cell = self.model[from_row][col].clone(); self.model[row][col] = from_cell; } } - pub fn scroll(&mut self, count: i64) { - let (top, bot, left, right) = (self.top as i64, self.bot as i64, self.left, self.right); + pub fn scroll(&mut self, count: usize) -> ModelRect { + let (top, bot, left, right) = (self.top, self.bot, self.left, self.right); if count > 0 { - for row in top as usize..(bot - count + 1) as usize { + for row in top..(bot - count + 1) { self.copy_row(row, count, left, right); } } else { - for row in ((top - count) as usize..(bot + 1) as usize).rev() { + for row in ((top - count)..(bot + 1)).rev() { self.copy_row(row, count, left, right); } } if count > 0 { - self.clear_region((bot - count + 1) as usize, bot as usize, left, right); + self.clear_region((bot - count + 1), bot, left, right); } else { - self.clear_region(top as usize, (top - count - 1) as usize, left, right); + self.clear_region(top, (top - count - 1), left, right); } + + ModelRect::new(top, bot, left ,right) } - pub fn clear(&mut self) { + pub fn clear(&mut self) -> ModelRect { let (rows, columns) = (self.rows, self.columns); self.clear_region(0, rows - 1, 0, columns - 1); + + ModelRect::new(0, 0, rows -1, columns -1) } - pub fn eol_clear(&mut self) { + pub fn eol_clear(&mut self) -> ModelRect { let (cur_row, cur_col, columns) = (self.cur_row, self.cur_col, self.columns); self.clear_region(cur_row, cur_row, cur_col, columns - 1); + + ModelRect::new(cur_row, cur_col, cur_col, columns -1) } fn clear_region(&mut self, top: usize, bot: usize, left: usize, right: usize) { @@ -190,3 +208,54 @@ impl UiModel { } } } + +pub struct ModelRect { + top: usize, + bot: usize, + left: usize, + right: usize, +} + +impl ModelRect { + pub fn new(top: usize, bot: usize, left: usize, right: usize) -> ModelRect { + ModelRect { + top: top, + bot: bot, + left: left, + right: right, + } + } + + pub fn point(x: usize, y: usize) -> ModelRect { + ModelRect { + top: x, + bot: x, + left: y, + right: y, + } + } + + pub fn join(&mut self, rect: &ModelRect) { + self.top = if self.top < rect.top { + self.top + } else { + rect.top + }; + self.left = if self.left < rect.left { + self.left + } else { + rect.left + }; + + self.bot = if self.bot > rect.bot { + self.bot + } else { + rect.bot + }; + self.right = if self.right > rect.right { + self.right + } else { + rect.right + }; + } +} From 51dcd7f4e846f4537e81fa86c27940244fdea9ed Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Mar 2017 01:18:41 +0300 Subject: [PATCH 2/9] Repaint event on given area --- src/nvim.rs | 14 ++++++++++---- src/settings.rs | 5 ++++- src/shell.rs | 26 ++++++++++++++++++++++---- src/ui_model.rs | 6 ++++++ 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/nvim.rs b/src/nvim.rs index 4ad463c..0305d88 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -1,7 +1,7 @@ use neovim_lib::{Neovim, NeovimApi, Session, Value, Integer, UiAttachOptions, CallError}; use std::io::{Result, Error, ErrorKind}; use std::result; -use ui_model::UiModel; +use ui_model::{UiModel, ModelRect}; use ui; use shell::Shell; use glib; @@ -15,7 +15,7 @@ pub trait RedrawEvents { fn on_resize(&mut self, columns: u64, rows: u64); - fn on_redraw(&self); + fn on_redraw(&self, mode: &RepaintMode); fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>); @@ -114,7 +114,7 @@ fn nvim_cb(method: &str, params: Vec) { } } - ui.on_redraw(); + ui.on_redraw(&RepaintMode::All); Ok(()) }); } @@ -124,7 +124,7 @@ fn nvim_cb(method: &str, params: Vec) { let args = params.iter().skip(1).cloned().collect(); safe_call(move |ui| { call_gui_event(ui, &ev_name, &args)?; - ui.on_redraw(); + ui.on_redraw(&RepaintMode::All); Ok(()) }); } else { @@ -204,3 +204,9 @@ impl ErrorReport for result::Result { } } } + +pub enum RepaintMode { + All, + Area(ModelRect), +} + diff --git a/src/settings.rs b/src/settings.rs index 4da9491..860aec8 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,6 +1,9 @@ #[cfg(unix)] use ui::{UI, SET}; +#[cfg(unix)] +use nvim::RepaintMode; + #[cfg(unix)] use nvim::RedrawEvents; @@ -77,7 +80,7 @@ fn monospace_font_changed() { // rpc is priority for font if set.font_source != FontSource::Rpc { set.update_font(&mut ui.shell); - ui.shell.on_redraw(); + ui.shell.on_redraw(&RepaintMode::All); } }); }); diff --git a/src/shell.rs b/src/shell.rs index b8242ed..eea40c3 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -14,19 +14,25 @@ use neovim_lib::{Neovim, NeovimApi, Value, Integer}; use settings; use ui_model::{UiModel, Cell, Attrs, Color, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; -use nvim::{RedrawEvents, GuiApi}; +use nvim::{RedrawEvents, GuiApi, RepaintMode}; use input::{convert_key, keyval_to_input_string}; use ui::{UI, Ui, SET}; const DEFAULT_FONT_NAME: &'static str = "DejaVu Sans Mono 12"; macro_rules! SHELL { + (&$id:ident = $expr:expr) => ( + UI.with(|ui_cell| { + let $id = &ui_cell.borrow().shell; + $expr + }); + ); ($id:ident = $expr:expr) => ( UI.with(|ui_cell| { let mut $id = &mut ui_cell.borrow_mut().shell; $expr }); - ) + ); } #[derive(PartialEq)] @@ -473,8 +479,20 @@ impl RedrawEvents for Shell { self.model = UiModel::new(rows, columns); } - fn on_redraw(&self) { - self.drawing_area.queue_draw(); + fn on_redraw(&self, mode: &RepaintMode) { + match mode { + &RepaintMode::All => self.drawing_area.queue_draw(), + &RepaintMode::Area(ref rect) => { + SHELL!(&shell = { + if let Some(line_height) = shell.line_height { + if let Some(char_width) = shell.char_width { + let (x, y, width, height) = rect.to_area(line_height as i32, char_width as i32); + self.drawing_area.queue_draw_area(x, y, width, height); + } + } + }); + }, + } } fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) { diff --git a/src/ui_model.rs b/src/ui_model.rs index 59e1ee6..d77a53d 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -258,4 +258,10 @@ impl ModelRect { rect.right }; } + + pub fn to_area(&self, line_height: i32, char_width: i32) -> (i32, i32, i32, i32) { + let y = self.top as i32 * line_height; + let x = self.left as i32 * char_width; + (x, y, self.right as i32 * char_width - x, self.bot as i32 * line_height - y) + } } From 89e6600325dd5610a6030cfc85246ba0cbed90cf Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Mar 2017 12:45:05 +0300 Subject: [PATCH 3/9] Invalidate rect passed to draw --- src/nvim.rs | 67 ++++++++++++++++++++++----------- src/shell.rs | 48 +++++++++++++++--------- src/ui_model.rs | 98 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 162 insertions(+), 51 deletions(-) diff --git a/src/nvim.rs b/src/nvim.rs index 0305d88..b15e9de 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -7,35 +7,35 @@ use shell::Shell; use glib; pub trait RedrawEvents { - fn on_cursor_goto(&mut self, row: u64, col: u64); + fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode; - fn on_put(&mut self, text: &str); + fn on_put(&mut self, text: &str) -> RepaintMode; - fn on_clear(&mut self); + fn on_clear(&mut self) -> RepaintMode; - fn on_resize(&mut self, columns: u64, rows: u64); + fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode; fn on_redraw(&self, mode: &RepaintMode); - fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>); + fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) -> RepaintMode; - fn on_eol_clear(&mut self); + fn on_eol_clear(&mut self) -> RepaintMode; - fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64); + fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode; - fn on_scroll(&mut self, count: i64); + fn on_scroll(&mut self, count: i64) -> RepaintMode; - fn on_update_bg(&mut self, bg: i64); + fn on_update_bg(&mut self, bg: i64) -> RepaintMode; - fn on_update_fg(&mut self, fg: i64); + fn on_update_fg(&mut self, fg: i64) -> RepaintMode; - fn on_update_sp(&mut self, sp: i64); + fn on_update_sp(&mut self, sp: i64) -> RepaintMode; - fn on_mode_change(&mut self, mode: &str); + fn on_mode_change(&mut self, mode: &str) -> RepaintMode; - fn on_mouse_on(&mut self); + fn on_mouse_on(&mut self) -> RepaintMode; - fn on_mouse_off(&mut self); + fn on_mouse_off(&mut self) -> RepaintMode; } pub trait GuiApi { @@ -96,6 +96,8 @@ fn nvim_cb(method: &str, params: Vec) { match method { "redraw" => { safe_call(move |ui| { + let mut repaint_mode = RepaintMode::Nothing; + for ev in ¶ms { if let &Value::Array(ref ev_args) = ev { if let Value::String(ref ev_name) = ev_args[0] { @@ -104,7 +106,8 @@ fn nvim_cb(method: &str, params: Vec) { &Value::Array(ref ar) => ar.clone(), _ => vec![], }; - call(ui, ev_name, &args)?; + let call_reapint_mode = call(ui, ev_name, &args)?; + repaint_mode = repaint_mode.join(&call_reapint_mode); } } else { println!("Unsupported event {:?}", ev_args); @@ -114,7 +117,7 @@ fn nvim_cb(method: &str, params: Vec) { } } - ui.on_redraw(&RepaintMode::All); + ui.on_redraw(&repaint_mode); Ok(()) }); } @@ -148,8 +151,8 @@ fn call_gui_event(ui: &mut Shell, method: &str, args: &Vec) -> result::Re Ok(()) } -fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), String> { - match method { +fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result { + Ok(match method { "cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])), "put" => ui.on_put(try_str!(args[0])), "clear" => ui.on_clear(), @@ -160,6 +163,7 @@ fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), S } else { panic!("Supports only map value as argument"); } + RepaintMode::Nothing } "eol_clear" => ui.on_eol_clear(), "set_scroll_region" => { @@ -167,6 +171,7 @@ fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), S try_uint!(args[1]), try_uint!(args[2]), try_uint!(args[3])); + RepaintMode::Nothing } "scroll" => ui.on_scroll(try_int!(args[0])), "update_bg" => ui.on_update_bg(try_int!(args[0])), @@ -175,10 +180,11 @@ fn call(ui: &mut Shell, method: &str, args: &Vec) -> result::Result<(), S "mode_change" => ui.on_mode_change(try_str!(args[0])), "mouse_on" => ui.on_mouse_on(), "mouse_off" => ui.on_mouse_off(), - _ => println!("Event {}({:?})", method, args), - }; - - Ok(()) + _ => { + println!("Event {}({:?})", method, args); + RepaintMode::Nothing + }, + }) } fn safe_call(cb: F) @@ -205,8 +211,25 @@ impl ErrorReport for result::Result { } } +#[derive(Clone)] pub enum RepaintMode { + Nothing, All, Area(ModelRect), } +impl RepaintMode { + pub fn join(&self, mode: &RepaintMode) -> RepaintMode { + match (self, mode) { + (&RepaintMode::Nothing, m) => m.clone(), + (m, &RepaintMode::Nothing) => m.clone(), + (&RepaintMode::All, _) => RepaintMode::All, + (_, &RepaintMode::All) => RepaintMode::All, + (&RepaintMode::Area(ref mr1), &RepaintMode::Area(ref mr2)) => { + let mut area = mr1.clone(); + area.join(mr2); + RepaintMode::Area(area) + } + } + } +} diff --git a/src/shell.rs b/src/shell.rs index eea40c3..5fadf04 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -459,24 +459,26 @@ fn gtk_configure_event(_: &DrawingArea, ev: &EventConfigure) -> bool { } impl RedrawEvents for Shell { - fn on_cursor_goto(&mut self, row: u64, col: u64) { - self.model.set_cursor(row, col); + fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode { + RepaintMode::Area(self.model.set_cursor(row, col)) } - fn on_put(&mut self, text: &str) { - self.model.put(text, self.cur_attrs.as_ref()); + fn on_put(&mut self, text: &str) -> RepaintMode { + RepaintMode::Area(self.model.put(text, self.cur_attrs.as_ref())) } - fn on_clear(&mut self) { + fn on_clear(&mut self) -> RepaintMode { self.model.clear(); + RepaintMode::All } - fn on_eol_clear(&mut self) { - self.model.eol_clear(); + fn on_eol_clear(&mut self) -> RepaintMode { + RepaintMode::Area(self.model.eol_clear()) } - fn on_resize(&mut self, columns: u64, rows: u64) { + fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode { self.model = UiModel::new(rows, columns); + RepaintMode::All } fn on_redraw(&self, mode: &RepaintMode) { @@ -492,18 +494,20 @@ impl RedrawEvents for Shell { } }); }, + &RepaintMode::Nothing => (), } } - fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) { + fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode { self.model.set_scroll_region(top, bot, left, right); + RepaintMode::Nothing } - fn on_scroll(&mut self, count: i64) { - self.model.scroll(count as usize); + fn on_scroll(&mut self, count: i64) -> RepaintMode { + RepaintMode::Area(self.model.scroll(count as usize)) } - fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) { + fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) -> RepaintMode { let mut model_attrs = Attrs::new(); for &(ref key_val, ref val) in attrs { @@ -537,46 +541,54 @@ impl RedrawEvents for Shell { } self.cur_attrs = Some(model_attrs); + RepaintMode::Nothing } - fn on_update_bg(&mut self, bg: i64) { + fn on_update_bg(&mut self, bg: i64) -> RepaintMode { if bg >= 0 { self.bg_color = split_color(bg as u64); } else { self.bg_color = COLOR_BLACK; } + RepaintMode::Nothing } - fn on_update_fg(&mut self, fg: i64) { + fn on_update_fg(&mut self, fg: i64) -> RepaintMode { if fg >= 0 { self.fg_color = split_color(fg as u64); } else { self.fg_color = COLOR_WHITE; } + RepaintMode::Nothing } - fn on_update_sp(&mut self, sp: i64) { + fn on_update_sp(&mut self, sp: i64) -> RepaintMode { if sp >= 0 { self.sp_color = split_color(sp as u64); } else { self.sp_color = COLOR_RED; } + RepaintMode::Nothing } - fn on_mode_change(&mut self, mode: &str) { + fn on_mode_change(&mut self, mode: &str) -> RepaintMode { match mode { "normal" => self.mode = NvimMode::Normal, "insert" => self.mode = NvimMode::Insert, _ => self.mode = NvimMode::Other, } + + RepaintMode::Area(self.model.cur_point()) } - fn on_mouse_on(&mut self) { + fn on_mouse_on(&mut self) -> RepaintMode { self.mouse_enabled = true; + RepaintMode::Nothing } - fn on_mouse_off(&mut self) { + fn on_mouse_off(&mut self) -> RepaintMode { self.mouse_enabled = false; + RepaintMode::Nothing } } diff --git a/src/ui_model.rs b/src/ui_model.rs index d77a53d..b47b5e1 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -120,13 +120,17 @@ impl UiModel { &self.model } + pub fn cur_point(&self) -> ModelRect { + ModelRect::point(self.cur_row, self.cur_col) + } + pub fn set_cursor(&mut self, row: u64, col: u64) -> ModelRect { - let mut changed_region = ModelRect::point(self.cur_row, self.cur_col); + let mut changed_region = self.cur_point(); self.cur_row = row as usize; self.cur_col = col as usize; - changed_region.join(&ModelRect::point(self.cur_row, self.cur_col)); + changed_region.join(&self.cur_point()); changed_region @@ -137,8 +141,8 @@ impl UiModel { } pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) -> ModelRect { + let changed_region = self.cur_point(); let mut cell = &mut self.model[self.cur_row][self.cur_col]; - let changed_region = ModelRect::point(self.cur_row, self.cur_col); cell.ch = text.chars().last().unwrap_or(' '); cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(|| Attrs::new()); @@ -183,21 +187,19 @@ impl UiModel { self.clear_region(top, (top - count - 1), left, right); } - ModelRect::new(top, bot, left ,right) + ModelRect::new(top, bot, left, right) } - pub fn clear(&mut self) -> ModelRect { + pub fn clear(&mut self) { let (rows, columns) = (self.rows, self.columns); self.clear_region(0, rows - 1, 0, columns - 1); - - ModelRect::new(0, 0, rows -1, columns -1) } pub fn eol_clear(&mut self) -> ModelRect { let (cur_row, cur_col, columns) = (self.cur_row, self.cur_col, self.columns); self.clear_region(cur_row, cur_row, cur_col, columns - 1); - ModelRect::new(cur_row, cur_col, cur_col, columns -1) + ModelRect::new(cur_row, cur_col, cur_col, columns - 1) } fn clear_region(&mut self, top: usize, bot: usize, left: usize, right: usize) { @@ -209,6 +211,7 @@ impl UiModel { } } +#[derive(Clone)] pub struct ModelRect { top: usize, bot: usize, @@ -260,8 +263,81 @@ impl ModelRect { } pub fn to_area(&self, line_height: i32, char_width: i32) -> (i32, i32, i32, i32) { - let y = self.top as i32 * line_height; - let x = self.left as i32 * char_width; - (x, y, self.right as i32 * char_width - x, self.bot as i32 * line_height - y) + (self.left as i32 * char_width, + self.top as i32 * line_height, + (self.right - self.left + 1) as i32 * char_width, + (self.bot - self.top + 1) as i32 * line_height) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cursor_area() { + let mut model = UiModel::new(10, 20); + + model.set_cursor(1, 1); + + let rect = model.set_cursor(5, 5); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(5, rect.bot); + assert_eq!(5, rect.right); + } + + #[test] + fn test_eol_clear_area() { + let mut model = UiModel::new(10, 20); + + model.set_cursor(1, 1); + + let rect = model.eol_clear(); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(1, rect.bot); + 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, 5); + + 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); + + model.set_cursor(1, 1); + + let rect = model.put(" ", None); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(1, rect.bot); + assert_eq!(1, rect.right); + } + + #[test] + fn test_scroll_area() { + let mut model = UiModel::new(10, 20); + + model.set_scroll_region(1, 5, 1, 5); + + let rect = model.scroll(3); + + assert_eq!(1, rect.top); + assert_eq!(1, rect.left); + assert_eq!(5, rect.bot); + assert_eq!(5, rect.right); } } From 700c8cd522c8712c6facf2cca91b683d5c85f8ef Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Mar 2017 12:49:30 +0300 Subject: [PATCH 4/9] Fix borrow error, rustfmt --- src/shell.rs | 57 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/src/shell.rs b/src/shell.rs index 5fadf04..6691c34 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -4,7 +4,8 @@ use cairo; use pangocairo as pc; use pango; use pango::FontDescription; -use gdk::{ModifierType, EventKey, EventConfigure, EventButton, EventMotion, EventType, EventScroll, ScrollDirection}; +use gdk::{ModifierType, EventKey, EventConfigure, EventButton, EventMotion, EventType, EventScroll, + ScrollDirection}; use gdk_sys; use glib; use gtk::prelude::*; @@ -61,7 +62,7 @@ pub struct Shell { impl Shell { pub fn new() -> Shell { - Shell { + Shell { model: UiModel::empty(), drawing_area: DrawingArea::new(), nvim: None, @@ -87,8 +88,9 @@ impl Shell { self.drawing_area .set_events((gdk_sys::GDK_BUTTON_RELEASE_MASK | gdk_sys::GDK_BUTTON_PRESS_MASK | - gdk_sys::GDK_BUTTON_MOTION_MASK | gdk_sys::GDK_SCROLL_MASK) - .bits() as i32); + gdk_sys::GDK_BUTTON_MOTION_MASK | + gdk_sys::GDK_SCROLL_MASK) + .bits() as i32); self.drawing_area.connect_button_press_event(gtk_button_press); self.drawing_area.connect_button_release_event(gtk_button_release); self.drawing_area.connect_motion_notify_event(gtk_motion_notify); @@ -144,10 +146,30 @@ fn gtk_scroll_event(_: &DrawingArea, ev: &EventScroll) -> Inhibit { } match ev.as_ref().direction { - ScrollDirection::Right => mouse_input(&mut shell, "ScrollWheelRight", ev.get_state(), ev.get_position()), - ScrollDirection::Left => mouse_input(&mut shell, "ScrollWheelLeft", ev.get_state(), ev.get_position()), - ScrollDirection::Up => mouse_input(&mut shell, "ScrollWheelUp", ev.get_state(), ev.get_position()), - ScrollDirection::Down => mouse_input(&mut shell, "ScrollWheelDown", ev.get_state(), ev.get_position()), + ScrollDirection::Right => { + mouse_input(&mut shell, + "ScrollWheelRight", + ev.get_state(), + ev.get_position()) + } + ScrollDirection::Left => { + mouse_input(&mut shell, + "ScrollWheelLeft", + ev.get_state(), + ev.get_position()) + } + ScrollDirection::Up => { + mouse_input(&mut shell, + "ScrollWheelUp", + ev.get_state(), + ev.get_position()) + } + ScrollDirection::Down => { + mouse_input(&mut shell, + "ScrollWheelDown", + ev.get_state(), + ev.get_position()) + } _ => (), } }); @@ -445,7 +467,8 @@ fn gtk_configure_event(_: &DrawingArea, ev: &EventConfigure) -> bool { let rows = (height as f64 / line_height).trunc() as usize; let columns = (width as f64 / char_width).trunc() as usize; if shell.model.rows != rows || shell.model.columns != columns { - if let Err(err) = shell.nvim().ui_try_resize(columns as u64, rows as u64) { + if let Err(err) = shell.nvim() + .ui_try_resize(columns as u64, rows as u64) { println!("Error trying resize nvim {}", err); } } @@ -485,15 +508,14 @@ impl RedrawEvents for Shell { match mode { &RepaintMode::All => self.drawing_area.queue_draw(), &RepaintMode::Area(ref rect) => { - SHELL!(&shell = { - if let Some(line_height) = shell.line_height { - if let Some(char_width) = shell.char_width { - let (x, y, width, height) = rect.to_area(line_height as i32, char_width as i32); - self.drawing_area.queue_draw_area(x, y, width, height); - } + if let Some(line_height) = self.line_height { + if let Some(char_width) = self.char_width { + let (x, y, width, height) = + rect.to_area(line_height as i32, char_width as i32); + self.drawing_area.queue_draw_area(x, y, width, height); } - }); - }, + } + } &RepaintMode::Nothing => (), } } @@ -602,4 +624,3 @@ impl GuiApi for Shell { }); } } - From ff4aa6c848b761ac74dfac1f3917be455a05c99a Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Mar 2017 13:21:20 +0300 Subject: [PATCH 5/9] Fix scroll --- src/shell.rs | 2 +- src/ui_model.rs | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/shell.rs b/src/shell.rs index 6691c34..ef2a700 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -526,7 +526,7 @@ impl RedrawEvents for Shell { } fn on_scroll(&mut self, count: i64) -> RepaintMode { - RepaintMode::Area(self.model.scroll(count as usize)) + RepaintMode::Area(self.model.scroll(count)) } fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) -> RepaintMode { diff --git a/src/ui_model.rs b/src/ui_model.rs index b47b5e1..6edc374 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -141,13 +141,18 @@ impl UiModel { } pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) -> ModelRect { - let changed_region = self.cur_point(); + let mut changed_region = self.cur_point(); let mut cell = &mut self.model[self.cur_row][self.cur_col]; cell.ch = text.chars().last().unwrap_or(' '); cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(|| Attrs::new()); cell.attrs.double_width = text.len() == 0; self.cur_col += 1; + if self.cur_col >= self.columns { + self.cur_col -= 1; + } + + changed_region.join(&ModelRect::point(self.cur_row, self.cur_col)); changed_region } @@ -160,16 +165,17 @@ impl UiModel { } #[inline] - fn copy_row(&mut self, row: usize, offset: usize, left: usize, right: usize) { + fn copy_row(&mut self, row: i64, offset: i64, left: usize, right: usize) { for col in left..right + 1 { - let from_row = row + offset; + let from_row = (row + offset) as usize; let from_cell = self.model[from_row][col].clone(); - self.model[row][col] = from_cell; + self.model[row as usize][col] = from_cell; } } - pub fn scroll(&mut self, count: usize) -> ModelRect { - let (top, bot, left, right) = (self.top, self.bot, self.left, self.right); + pub fn scroll(&mut self, count: i64) -> ModelRect { + let (top, bot, left, right) = (self.top as i64, self.bot as i64, self.left, self.right); + println!("{}, {}, {}, {}", top, bot, left, right); if count > 0 { for row in top..(bot - count + 1) { @@ -182,12 +188,12 @@ impl UiModel { } if count > 0 { - self.clear_region((bot - count + 1), bot, left, right); + self.clear_region((bot - count + 1) as usize, bot as usize, left, right); } else { - self.clear_region(top, (top - count - 1), left, right); + self.clear_region(top as usize, (top - count - 1) as usize, left, right); } - ModelRect::new(top, bot, left, right) + ModelRect::new(top as usize, bot as usize, left, right) } pub fn clear(&mut self) { @@ -324,7 +330,7 @@ mod tests { assert_eq!(1, rect.top); assert_eq!(1, rect.left); assert_eq!(1, rect.bot); - assert_eq!(1, rect.right); + assert_eq!(2, rect.right); } #[test] From f7c088eca7b288a8f39af3270deadfcc38c72810 Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Mar 2017 13:21:42 +0300 Subject: [PATCH 6/9] Cleanup --- src/ui_model.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui_model.rs b/src/ui_model.rs index 6edc374..7016dd8 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -175,7 +175,6 @@ impl UiModel { pub fn scroll(&mut self, count: i64) -> ModelRect { let (top, bot, left, right) = (self.top as i64, self.bot as i64, self.left, self.right); - println!("{}, {}, {}, {}", top, bot, left, right); if count > 0 { for row in top..(bot - count + 1) { From c2a6ec88c72fb18330d66b2d5b9ea03c685d005b Mon Sep 17 00:00:00 2001 From: daa Date: Sat, 18 Mar 2017 14:19:36 +0300 Subject: [PATCH 7/9] fn from_area --- src/ui_model.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/ui_model.rs b/src/ui_model.rs index 7016dd8..4e29fb2 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -273,12 +273,31 @@ impl ModelRect { (self.right - self.left + 1) as i32 * char_width, (self.bot - self.top + 1) as i32 * line_height) } + + pub fn from_area(line_height: i32, char_width: i32, x1: f64, y1: f64, x2: f64, y2: f64) -> ModelRect { + let left = (x1 / char_width as f64) as usize; + let right = (x2 / char_width as f64) as usize; + let top = (y1 / line_height as f64) as usize; + let bot = (y2 / line_height as f64) as usize; + + ModelRect::new(top, bot, left, right) + } } #[cfg(test)] mod tests { use super::*; + #[test] + fn test_from_area() { + let rect = ModelRect::from_area(10, 5, 3.0, 3.0, 9.0, 17.0); + + assert_eq!(0, rect.top); + assert_eq!(0, rect.left); + assert_eq!(1, rect.bot); + assert_eq!(1, rect.right); + } + #[test] fn test_cursor_area() { let mut model = UiModel::new(10, 20); From 778cbd0af28acd15e435f77e5e1016c60e032ebf Mon Sep 17 00:00:00 2001 From: daa84 Date: Mon, 20 Mar 2017 18:18:08 +0300 Subject: [PATCH 8/9] Clip Iterator --- src/nvim.rs | 21 +++++++++ src/shell.rs | 24 +++++++---- src/ui_model.rs | 110 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 132 insertions(+), 23 deletions(-) diff --git a/src/nvim.rs b/src/nvim.rs index b15e9de..4c8f457 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -233,3 +233,24 @@ impl RepaintMode { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mode() { + let mode = RepaintMode::Area(ModelRect::point(1, 1)); + mode.join(&RepaintMode::Nothing); + + match mode { + RepaintMode::Area(ref rect) => { + assert_eq!(1, rect.top); + assert_eq!(1, rect.bot); + assert_eq!(1, rect.left); + assert_eq!(1, rect.right); + } + _ => panic!("mode is worng") + } + } +} diff --git a/src/shell.rs b/src/shell.rs index ef2a700..cb7e827 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -14,7 +14,7 @@ use gtk::DrawingArea; use neovim_lib::{Neovim, NeovimApi, Value, Integer}; use settings; -use ui_model::{UiModel, Cell, Attrs, Color, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; +use ui_model::{UiModel, Cell, Attrs, Color, ModelRect, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; use nvim::{RedrawEvents, GuiApi, RepaintMode}; use input::{convert_key, keyval_to_input_string}; use ui::{UI, Ui, SET}; @@ -276,17 +276,24 @@ fn draw(shell: &Shell, ctx: &cairo::Context) { let line_height = shell.line_height.unwrap(); let char_width = shell.char_width.unwrap(); + let clip = ctx.clip_extents(); + let model_clip = ModelRect::from_area(line_height, char_width, + clip.0, clip.1, clip.2, clip.3); + + let line_x = model_clip.left as f64 * char_width; + let mut line_y: f64 = model_clip.top as f64 * line_height; + let (row, col) = shell.model.get_cursor(); let mut buf = String::with_capacity(4); - let mut line_y: f64 = 0.0; let layout = pc::create_layout(ctx); let mut desc = shell.create_pango_font(); - for (line_idx, line) in shell.model.model().iter().enumerate() { - ctx.move_to(0.0, line_y); + // FIXME: col_idx is wrong + for (line_idx, line) in shell.model.clip_model(&model_clip) { + ctx.move_to(line_x, line_y); // first draw background // here we join same bg color for given line @@ -319,7 +326,7 @@ fn draw(shell: &Shell, ctx: &cairo::Context) { line_height, from_bg.take().unwrap()); - ctx.move_to(0.0, line_y); + ctx.move_to(line_x, line_y); for (col_idx, cell) in line.iter().enumerate() { let double_width = line.get(col_idx + 1).map(|c| c.attrs.double_width).unwrap_or(false); @@ -508,12 +515,13 @@ impl RedrawEvents for Shell { match mode { &RepaintMode::All => self.drawing_area.queue_draw(), &RepaintMode::Area(ref rect) => { - if let Some(line_height) = self.line_height { - if let Some(char_width) = self.char_width { + match (&self.line_height, &self.char_width) { + (&Some(line_height), &Some(char_width)) => { let (x, y, width, height) = - rect.to_area(line_height as i32, char_width as i32); + rect.to_area(line_height, char_width); self.drawing_area.queue_draw_area(x, y, width, height); } + _ => self.drawing_area.queue_draw(), } } &RepaintMode::Nothing => (), diff --git a/src/ui_model.rs b/src/ui_model.rs index 4e29fb2..5b1e330 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -1,3 +1,4 @@ +use std::slice::Iter; #[derive(Clone, PartialEq)] pub struct Color(pub f64, pub f64, pub f64); @@ -120,6 +121,10 @@ impl UiModel { &self.model } + pub fn clip_model<'a> (&'a self, clip: &'a ModelRect) -> ClipRowIterator<'a> { + ClipRowIterator::new(self, clip) + } + pub fn cur_point(&self) -> ModelRect { ModelRect::point(self.cur_row, self.cur_col) } @@ -218,10 +223,10 @@ impl UiModel { #[derive(Clone)] pub struct ModelRect { - top: usize, - bot: usize, - left: usize, - right: usize, + pub top: usize, + pub bot: usize, + pub left: usize, + pub right: usize, } impl ModelRect { @@ -267,30 +272,105 @@ impl ModelRect { }; } - pub fn to_area(&self, line_height: i32, char_width: i32) -> (i32, i32, i32, i32) { - (self.left as i32 * char_width, - self.top as i32 * line_height, - (self.right - self.left + 1) as i32 * char_width, - (self.bot - self.top + 1) as i32 * line_height) + pub fn to_area(&self, line_height: f64, char_width: f64) -> (i32, i32, i32, i32) { + (self.left as i32 * char_width as i32, + self.top as i32 * line_height as i32, + (self.right - self.left + 1) as i32 * char_width as i32, + (self.bot - self.top + 1) as i32 * line_height as i32) } - pub fn from_area(line_height: i32, char_width: i32, x1: f64, y1: f64, x2: f64, y2: f64) -> ModelRect { - let left = (x1 / char_width as f64) as usize; - let right = (x2 / char_width as f64) as usize; - let top = (y1 / line_height as f64) as usize; - let bot = (y2 / line_height as f64) as usize; + pub fn from_area(line_height: f64, char_width: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> ModelRect { + let left = (x1 / char_width) as usize; + let right = (x2 / char_width) as usize; + let top = (y1 / line_height) as usize; + let bot = (y2 / line_height) as usize; ModelRect::new(top, bot, left, right) } } +pub struct ClipRowIterator<'a> { + rect: &'a ModelRect, + pos: usize, + iter: Iter<'a, Vec>, +} + +impl <'a> ClipRowIterator<'a> { + pub fn new(model: &'a UiModel, rect: &'a ModelRect) -> ClipRowIterator<'a> { + ClipRowIterator { + rect: rect, + pos: 0, + iter: model.model()[rect.top..rect.bot + 1].iter(), + } + } +} + +impl <'a> Iterator for ClipRowIterator<'a> { + type Item = (usize, ClipColIterator<'a>); + + fn next(&mut self) -> Option<(usize, ClipColIterator<'a>)> { + self.pos += 1; + self.iter.next().map(|line| { + (self.rect.top + self.pos, ClipColIterator::new(line, self.rect)) + }) + } +} + +pub struct ClipColIterator<'a> { + rect: &'a ModelRect, + len: usize, + line: &'a Vec, + pos: usize, + iter: Iter<'a, Cell>, +} + +impl <'a> ClipColIterator<'a> { + pub fn new(model: &'a Vec, rect: &'a ModelRect) -> ClipColIterator<'a> { + ClipColIterator { + len: rect.right - rect.left + 1, + line: model, + rect: rect, + pos: 0, + iter: model[rect.left..rect.right + 1].iter(), + } + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn get(&self, idx: usize) -> Option<&Cell> { + self.line.get(idx) + } +} + +impl <'a>Iterator for ClipColIterator<'a> { + type Item = (usize, &'a Cell); + + fn next(&mut self) -> Option<(usize, &'a Cell)> { + self.pos += 1; + self.iter.next().map(|line| { + (self.rect.left + self.pos, line) + }) + } +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn test_iterator() { + let model = UiModel::new(10, 20); + let rect = ModelRect::new(1, 2, 1, 2); + + assert_eq!(2, model.clip_model(&rect).count()); + assert_eq!(2, model.clip_model(&rect).nth(0).unwrap().count()); + } + #[test] fn test_from_area() { - let rect = ModelRect::from_area(10, 5, 3.0, 3.0, 9.0, 17.0); + let rect = ModelRect::from_area(10.0, 5.0, 3.0, 3.0, 9.0, 17.0); assert_eq!(0, rect.top); assert_eq!(0, rect.left); From e676af8f91e8b25513f84f7cd9e814a81906d9ae Mon Sep 17 00:00:00 2001 From: daa84 Date: Tue, 21 Mar 2017 13:10:55 +0300 Subject: [PATCH 9/9] Finish area readrawing optimization --- src/shell.rs | 12 +++--- src/ui_model.rs | 103 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/shell.rs b/src/shell.rs index cb7e827..24ee56f 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -277,8 +277,9 @@ fn draw(shell: &Shell, ctx: &cairo::Context) { let line_height = shell.line_height.unwrap(); let char_width = shell.char_width.unwrap(); let clip = ctx.clip_extents(); - let model_clip = ModelRect::from_area(line_height, char_width, + let mut model_clip = ModelRect::from_area(line_height, char_width, clip.0, clip.1, clip.2, clip.3); + shell.model.limit_to_model(&mut model_clip); let line_x = model_clip.left as f64 * char_width; let mut line_y: f64 = model_clip.top as f64 * line_height; @@ -291,16 +292,15 @@ fn draw(shell: &Shell, ctx: &cairo::Context) { let layout = pc::create_layout(ctx); let mut desc = shell.create_pango_font(); - // FIXME: col_idx is wrong for (line_idx, line) in shell.model.clip_model(&model_clip) { ctx.move_to(line_x, line_y); // first draw background // here we join same bg color for given line // this gives less drawing primitives - let mut from_col_idx = 0; + let mut from_col_idx = model_clip.left; let mut from_bg = None; - for (col_idx, cell) in line.iter().enumerate() { + for (col_idx, cell) in line.iter() { let (bg, _) = shell.colors(cell); if from_bg.is_none() { @@ -321,14 +321,14 @@ fn draw(shell: &Shell, ctx: &cairo::Context) { draw_joined_rect(shell, ctx, from_col_idx, - line.len(), + model_clip.right + 1, char_width, line_height, from_bg.take().unwrap()); ctx.move_to(line_x, line_y); - for (col_idx, cell) in line.iter().enumerate() { + for (col_idx, cell) in line.iter() { let double_width = line.get(col_idx + 1).map(|c| c.attrs.double_width).unwrap_or(false); let current_point = ctx.get_current_point(); diff --git a/src/ui_model.rs b/src/ui_model.rs index 5b1e330..4515fcb 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -121,6 +121,29 @@ impl UiModel { &self.model } + pub fn limit_to_model(&self, clip: &mut ModelRect) { + clip.left = if clip.left >= self.columns { + self.columns - 1 + } else { + clip.left + }; + clip.right = if clip.right >= self.columns { + self.columns - 1 + } else { + clip.right + }; + clip.top = if clip.top >= self.rows { + self.rows - 1 + } else { + clip.top + }; + clip.bot = if clip.bot >= self.rows { + self.rows - 1 + } else { + clip.bot + }; + } + pub fn clip_model<'a> (&'a self, clip: &'a ModelRect) -> ClipRowIterator<'a> { ClipRowIterator::new(self, clip) } @@ -275,8 +298,8 @@ impl ModelRect { pub fn to_area(&self, line_height: f64, char_width: f64) -> (i32, i32, i32, i32) { (self.left as i32 * char_width as i32, self.top as i32 * line_height as i32, - (self.right - self.left + 1) as i32 * char_width as i32, - (self.bot - self.top + 1) as i32 * line_height as i32) + (self.right - self.left + 1) as i32 * char_width as i32- 1, + (self.bot - self.top + 1) as i32 * line_height as i32 - 1) } pub fn from_area(line_height: f64, char_width: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> ModelRect { @@ -306,20 +329,40 @@ impl <'a> ClipRowIterator<'a> { } impl <'a> Iterator for ClipRowIterator<'a> { - type Item = (usize, ClipColIterator<'a>); + type Item = (usize, ClipLine<'a>); - fn next(&mut self) -> Option<(usize, ClipColIterator<'a>)> { + fn next(&mut self) -> Option<(usize, ClipLine<'a>)> { self.pos += 1; self.iter.next().map(|line| { - (self.rect.top + self.pos, ClipColIterator::new(line, self.rect)) + (self.rect.top + self.pos - 1, ClipLine::new(line, self.rect)) }) } } +pub struct ClipLine <'a> { + rect: &'a ModelRect, + line: &'a Vec, +} + +impl <'a> ClipLine<'a> { + pub fn new(model: &'a Vec, rect: &'a ModelRect) -> ClipLine<'a> { + ClipLine { + line: model, + rect: rect, + } + } + + pub fn get(&self, idx: usize) -> Option<&Cell> { + self.line.get(idx) + } + + pub fn iter(&self) -> ClipColIterator<'a> { + ClipColIterator::new(self.line, self.rect) + } +} + pub struct ClipColIterator<'a> { rect: &'a ModelRect, - len: usize, - line: &'a Vec, pos: usize, iter: Iter<'a, Cell>, } @@ -327,21 +370,12 @@ pub struct ClipColIterator<'a> { impl <'a> ClipColIterator<'a> { pub fn new(model: &'a Vec, rect: &'a ModelRect) -> ClipColIterator<'a> { ClipColIterator { - len: rect.right - rect.left + 1, - line: model, rect: rect, pos: 0, iter: model[rect.left..rect.right + 1].iter(), } } - pub fn len(&self) -> usize { - self.len - } - - pub fn get(&self, idx: usize) -> Option<&Cell> { - self.line.get(idx) - } } impl <'a>Iterator for ClipColIterator<'a> { @@ -350,7 +384,7 @@ impl <'a>Iterator for ClipColIterator<'a> { fn next(&mut self) -> Option<(usize, &'a Cell)> { self.pos += 1; self.iter.next().map(|line| { - (self.rect.left + self.pos, line) + (self.rect.left + self.pos - 1, line) }) } } @@ -359,13 +393,32 @@ impl <'a>Iterator for ClipColIterator<'a> { mod tests { use super::*; + #[test] + fn test_iterator_border() { + let model = UiModel::new(10, 20); + let rect = ModelRect::new(0, 9, 0, 19); + + assert_eq!(10, model.clip_model(&rect).count()); + let (_, first_line) = model.clip_model(&rect).nth(0).unwrap(); + assert_eq!(20, first_line.iter().count()); + + let (idx, _) = first_line.iter().nth(19).unwrap(); + assert_eq!(19, idx); + } + #[test] fn test_iterator() { let model = UiModel::new(10, 20); let rect = ModelRect::new(1, 2, 1, 2); assert_eq!(2, model.clip_model(&rect).count()); - assert_eq!(2, model.clip_model(&rect).nth(0).unwrap().count()); + let (idx, first_line) = model.clip_model(&rect).nth(0).unwrap(); + + assert_eq!(1, idx); + assert_eq!(2, first_line.iter().count()); + + let (idx, _) = first_line.iter().nth(0).unwrap(); + assert_eq!(1, idx); } #[test] @@ -376,6 +429,14 @@ mod tests { assert_eq!(0, rect.left); assert_eq!(1, rect.bot); assert_eq!(1, rect.right); + + + let rect = ModelRect::from_area(10.0, 5.0, 0.0, 0.0, 9.0, 19.0); + + assert_eq!(0, rect.top); + assert_eq!(0, rect.left); + assert_eq!(1, rect.bot); + assert_eq!(1, rect.right); } #[test] @@ -409,12 +470,12 @@ mod tests { #[test] fn test_repaint_rect() { let rect = ModelRect::point(1, 1); - let (x, y, width, height) = rect.to_area(10, 5); + 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); + assert_eq!(4, width); + assert_eq!(9, height); } #[test]