Merge branch 'invalidate-area'

This commit is contained in:
daa 2017-03-21 22:27:42 +03:00
commit 4e00df7b0b
4 changed files with 504 additions and 76 deletions

View File

@ -1,41 +1,41 @@
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;
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);
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<Value>) {
match method {
"redraw" => {
safe_call(move |ui| {
let mut repaint_mode = RepaintMode::Nothing;
for ev in &params {
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>) {
&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<Value>) {
}
}
ui.on_redraw();
ui.on_redraw(&repaint_mode);
Ok(())
});
}
@ -124,7 +127,7 @@ fn nvim_cb(method: &str, params: Vec<Value>) {
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 {
@ -148,8 +151,8 @@ fn call_gui_event(ui: &mut Shell, method: &str, args: &Vec<Value>) -> result::Re
Ok(())
}
fn call(ui: &mut Shell, method: &str, args: &Vec<Value>) -> result::Result<(), String> {
match method {
fn call(ui: &mut Shell, method: &str, args: &Vec<Value>) -> result::Result<RepaintMode, String> {
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<Value>) -> 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<Value>) -> 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<Value>) -> 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<F>(cb: F)
@ -204,3 +210,47 @@ impl<T> ErrorReport for result::Result<T, CallError> {
}
}
}
#[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)
}
}
}
}
#[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")
}
}
}

View File

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

View File

@ -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::*;
@ -13,20 +14,26 @@ 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 nvim::{RedrawEvents, GuiApi};
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};
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)]
@ -55,7 +62,7 @@ pub struct Shell {
impl Shell {
pub fn new() -> Shell {
Shell {
Shell {
model: UiModel::empty(),
drawing_area: DrawingArea::new(),
nvim: None,
@ -81,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);
@ -138,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())
}
_ => (),
}
});
@ -248,24 +276,31 @@ 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 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;
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);
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() {
@ -286,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(0.0, line_y);
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();
@ -439,7 +474,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);
}
}
@ -453,39 +489,55 @@ 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) {
self.drawing_area.queue_draw();
fn on_redraw(&self, mode: &RepaintMode) {
match mode {
&RepaintMode::All => self.drawing_area.queue_draw(),
&RepaintMode::Area(ref rect) => {
match (&self.line_height, &self.char_width) {
(&Some(line_height), &Some(char_width)) => {
let (x, y, width, height) =
rect.to_area(line_height, char_width);
self.drawing_area.queue_draw_area(x, y, width, height);
}
_ => self.drawing_area.queue_draw(),
}
}
&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);
fn on_scroll(&mut self, count: i64) -> RepaintMode {
RepaintMode::Area(self.model.scroll(count))
}
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 {
@ -519,46 +571,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
}
}
@ -572,4 +632,3 @@ impl GuiApi for Shell {
});
}
}

View File

@ -1,3 +1,4 @@
use std::slice::Iter;
#[derive(Clone, PartialEq)]
pub struct Color(pub f64, pub f64, pub f64);
@ -120,21 +121,68 @@ impl UiModel {
&self.model
}
pub fn set_cursor(&mut self, row: u64, col: u64) {
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)
}
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 = self.cur_point();
self.cur_row = row as usize;
self.cur_col = col as usize;
changed_region.join(&self.cur_point());
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 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
}
pub fn set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) {
@ -144,23 +192,24 @@ 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: i64, offset: i64, left: usize, right: usize) {
for col in left..right + 1 {
let from_row = (row as i64 + offset) as usize;
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: i64) {
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);
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);
}
}
@ -170,6 +219,8 @@ impl UiModel {
} else {
self.clear_region(top as usize, (top - count - 1) as usize, left, right);
}
ModelRect::new(top as usize, bot as usize, left, right)
}
pub fn clear(&mut self) {
@ -177,9 +228,11 @@ impl UiModel {
self.clear_region(0, rows - 1, 0, 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 +243,266 @@ impl UiModel {
}
}
}
#[derive(Clone)]
pub struct ModelRect {
pub top: usize,
pub bot: usize,
pub left: usize,
pub 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
};
}
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- 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 {
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<Cell>>,
}
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, ClipLine<'a>);
fn next(&mut self) -> Option<(usize, ClipLine<'a>)> {
self.pos += 1;
self.iter.next().map(|line| {
(self.rect.top + self.pos - 1, ClipLine::new(line, self.rect))
})
}
}
pub struct ClipLine <'a> {
rect: &'a ModelRect,
line: &'a Vec<Cell>,
}
impl <'a> ClipLine<'a> {
pub fn new(model: &'a Vec<Cell>, 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,
pos: usize,
iter: Iter<'a, Cell>,
}
impl <'a> ClipColIterator<'a> {
pub fn new(model: &'a Vec<Cell>, rect: &'a ModelRect) -> ClipColIterator<'a> {
ClipColIterator {
rect: rect,
pos: 0,
iter: model[rect.left..rect.right + 1].iter(),
}
}
}
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 - 1, line)
})
}
}
#[cfg(test)]
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());
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]
fn test_from_area() {
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);
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]
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.0, 5.0);
assert_eq!(5, x);
assert_eq!(10, y);
assert_eq!(4, width);
assert_eq!(9, 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!(2, 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);
}
}