2017-08-23 09:45:56 +00:00
|
|
|
mod cell;
|
|
|
|
mod line;
|
2017-09-06 15:31:13 +00:00
|
|
|
mod item;
|
|
|
|
mod model_rect;
|
2018-01-02 22:26:04 +00:00
|
|
|
mod model_layout;
|
2016-03-31 16:19:08 +00:00
|
|
|
|
2017-08-23 09:45:56 +00:00
|
|
|
pub use self::cell::{Cell, Attrs};
|
2017-09-12 15:28:01 +00:00
|
|
|
pub use self::line::{Line, StyledLine};
|
2017-09-06 15:31:13 +00:00
|
|
|
pub use self::item::Item;
|
2017-09-07 14:48:10 +00:00
|
|
|
pub use self::model_rect::{ModelRect, ModelRectVec};
|
2018-01-02 22:26:04 +00:00
|
|
|
pub use self::model_layout::ModelLayout;
|
2016-04-03 13:04:52 +00:00
|
|
|
|
2016-03-19 08:47:23 +00:00
|
|
|
|
|
|
|
pub struct UiModel {
|
2016-04-03 22:34:44 +00:00
|
|
|
pub columns: usize,
|
|
|
|
pub rows: usize,
|
2016-03-28 15:05:10 +00:00
|
|
|
cur_row: usize,
|
|
|
|
cur_col: usize,
|
2017-08-23 09:45:56 +00:00
|
|
|
model: Box<[Line]>,
|
2016-04-03 15:13:18 +00:00
|
|
|
top: usize,
|
|
|
|
bot: usize,
|
|
|
|
left: usize,
|
|
|
|
right: usize,
|
2016-03-19 08:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UiModel {
|
2016-03-28 15:05:10 +00:00
|
|
|
pub fn new(rows: u64, columns: u64) -> UiModel {
|
|
|
|
let mut model = Vec::with_capacity(rows as usize);
|
2017-08-25 15:32:30 +00:00
|
|
|
for _ in 0..rows as usize {
|
2017-08-23 09:45:56 +00:00
|
|
|
model.push(Line::new(columns as usize));
|
2016-03-19 08:47:23 +00:00
|
|
|
}
|
|
|
|
|
2016-03-31 16:19:08 +00:00
|
|
|
UiModel {
|
2016-03-28 15:05:10 +00:00
|
|
|
columns: columns as usize,
|
|
|
|
rows: rows as usize,
|
2016-03-19 08:47:23 +00:00
|
|
|
cur_row: 0,
|
|
|
|
cur_col: 0,
|
2017-08-23 09:45:56 +00:00
|
|
|
model: model.into_boxed_slice(),
|
2016-04-03 15:13:18 +00:00
|
|
|
top: 0,
|
2017-03-09 13:51:34 +00:00
|
|
|
bot: (rows - 1) as usize,
|
2016-04-03 15:13:18 +00:00
|
|
|
left: 0,
|
2017-03-09 13:51:34 +00:00
|
|
|
right: (columns - 1) as usize,
|
2016-03-19 08:47:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-11 15:55:29 +00:00
|
|
|
pub fn empty() -> UiModel {
|
|
|
|
UiModel {
|
|
|
|
columns: 0,
|
|
|
|
rows: 0,
|
|
|
|
cur_row: 0,
|
|
|
|
cur_col: 0,
|
|
|
|
model: Box::new([]),
|
|
|
|
top: 0,
|
|
|
|
bot: 0,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-12 15:28:01 +00:00
|
|
|
#[inline]
|
2017-08-23 09:45:56 +00:00
|
|
|
pub fn model(&self) -> &[Line] {
|
2016-03-31 16:19:08 +00:00
|
|
|
&self.model
|
2016-03-31 10:09:34 +00:00
|
|
|
}
|
|
|
|
|
2017-09-12 15:28:01 +00:00
|
|
|
#[inline]
|
2017-08-25 15:32:30 +00:00
|
|
|
pub fn model_mut(&mut self) -> &mut [Line] {
|
|
|
|
&mut self.model
|
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
pub fn cur_point(&self) -> ModelRect {
|
2017-04-19 08:40:53 +00:00
|
|
|
ModelRect::point(self.cur_col, self.cur_row)
|
2017-03-18 09:45:05 +00:00
|
|
|
}
|
|
|
|
|
2017-04-22 19:18:59 +00:00
|
|
|
pub fn set_cursor(&mut self, row: usize, col: usize) -> ModelRectVec {
|
2017-11-13 09:51:22 +00:00
|
|
|
// it is possible in some cases that cursor moved out of visible rect
|
|
|
|
// see https://github.com/daa84/neovim-gtk/issues/20
|
|
|
|
if row >= self.model.len() || col >= self.model[row].line.len() {
|
|
|
|
return ModelRectVec::empty();
|
|
|
|
}
|
|
|
|
|
2017-04-22 19:18:59 +00:00
|
|
|
let mut changed_region = ModelRectVec::new(self.cur_point());
|
2017-03-17 20:38:12 +00:00
|
|
|
|
2017-03-24 19:23:22 +00:00
|
|
|
self.cur_row = row;
|
|
|
|
self.cur_col = col;
|
2017-03-17 20:38:12 +00:00
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
changed_region.join(&self.cur_point());
|
2017-03-17 20:38:12 +00:00
|
|
|
|
|
|
|
changed_region
|
|
|
|
|
2016-05-03 17:25:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_cursor(&self) -> (usize, usize) {
|
|
|
|
(self.cur_row, self.cur_col)
|
2016-03-28 15:05:10 +00:00
|
|
|
}
|
|
|
|
|
2017-11-19 09:44:28 +00:00
|
|
|
pub fn put(&mut self, ch: char, double_width: bool, attrs: Option<&Attrs>) -> ModelRect {
|
2017-03-18 10:21:20 +00:00
|
|
|
let mut changed_region = self.cur_point();
|
2017-09-11 15:31:15 +00:00
|
|
|
let line = &mut self.model[self.cur_row];
|
2017-08-25 15:32:30 +00:00
|
|
|
line.dirty_line = true;
|
|
|
|
|
2017-09-11 15:31:15 +00:00
|
|
|
let cell = &mut line[self.cur_col];
|
2017-03-17 20:38:12 +00:00
|
|
|
|
2017-11-19 09:44:28 +00:00
|
|
|
cell.ch = ch;
|
2017-07-25 14:55:31 +00:00
|
|
|
cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(Attrs::new);
|
2017-11-19 09:44:28 +00:00
|
|
|
cell.attrs.double_width = double_width;
|
2017-09-12 12:09:17 +00:00
|
|
|
cell.dirty = true;
|
2016-03-28 15:05:10 +00:00
|
|
|
self.cur_col += 1;
|
2017-03-18 10:21:20 +00:00
|
|
|
if self.cur_col >= self.columns {
|
|
|
|
self.cur_col -= 1;
|
|
|
|
}
|
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
changed_region.join(&ModelRect::point(self.cur_col, self.cur_row));
|
2017-03-17 20:38:12 +00:00
|
|
|
|
|
|
|
changed_region
|
2016-03-19 08:47:23 +00:00
|
|
|
}
|
2016-03-29 09:22:16 +00:00
|
|
|
|
2016-04-03 15:13:18 +00:00
|
|
|
pub fn set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) {
|
|
|
|
self.top = top as usize;
|
|
|
|
self.bot = bot as usize;
|
|
|
|
self.left = left as usize;
|
|
|
|
self.right = right as usize;
|
|
|
|
}
|
|
|
|
|
2018-01-13 18:32:21 +00:00
|
|
|
/// Copy rows from 0 to to_row, col from 0 self.columns
|
|
|
|
///
|
|
|
|
/// Don't do any validation!
|
|
|
|
pub fn copy_rows(&self, target: &mut UiModel, to_row: usize) {
|
|
|
|
for (row_idx, line) in self.model[0..to_row + 1].iter().enumerate() {
|
|
|
|
let mut target_row = &mut target.model[row_idx];
|
2018-01-14 19:18:40 +00:00
|
|
|
line.copy_to(target_row, 0, self.columns - 1);
|
2018-01-13 18:32:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 20:38:12 +00:00
|
|
|
#[inline]
|
2017-09-01 10:14:16 +00:00
|
|
|
fn copy_row(&mut self, target_row: i64, offset: i64, left_col: usize, right_col: usize) {
|
2017-09-11 15:31:15 +00:00
|
|
|
debug_assert_ne!(0, offset);
|
2017-09-01 10:14:16 +00:00
|
|
|
|
|
|
|
let from_row = (target_row + offset) as usize;
|
|
|
|
|
|
|
|
let (left, right) = if offset > 0 {
|
|
|
|
self.model.split_at_mut(from_row)
|
|
|
|
} else {
|
|
|
|
self.model.split_at_mut(target_row as usize)
|
|
|
|
};
|
|
|
|
|
|
|
|
let (source_row, target_row) = if offset > 0 {
|
|
|
|
(&right[0], &mut left[target_row as usize])
|
|
|
|
} else {
|
|
|
|
(&left[from_row], &mut right[0])
|
|
|
|
};
|
|
|
|
|
|
|
|
source_row.copy_to(target_row, left_col, right_col);
|
2016-04-03 15:13:18 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 10:21:20 +00:00
|
|
|
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);
|
2016-04-03 15:13:18 +00:00
|
|
|
|
|
|
|
if count > 0 {
|
2017-03-17 20:38:12 +00:00
|
|
|
for row in top..(bot - count + 1) {
|
2016-04-03 15:13:18 +00:00
|
|
|
self.copy_row(row, count, left, right);
|
|
|
|
}
|
|
|
|
} else {
|
2017-03-17 20:38:12 +00:00
|
|
|
for row in ((top - count)..(bot + 1)).rev() {
|
2016-04-03 15:13:18 +00:00
|
|
|
self.copy_row(row, count, left, right);
|
2016-03-29 09:22:16 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-03 15:13:18 +00:00
|
|
|
|
|
|
|
if count > 0 {
|
2017-03-18 10:21:20 +00:00
|
|
|
self.clear_region((bot - count + 1) as usize, bot as usize, left, right);
|
2016-04-03 15:13:18 +00:00
|
|
|
} else {
|
2017-03-18 10:21:20 +00:00
|
|
|
self.clear_region(top as usize, (top - count - 1) as usize, left, right);
|
2016-04-03 15:13:18 +00:00
|
|
|
}
|
2017-03-17 20:38:12 +00:00
|
|
|
|
2017-03-18 10:21:20 +00:00
|
|
|
ModelRect::new(top as usize, bot as usize, left, right)
|
2016-04-03 15:13:18 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
pub fn clear(&mut self) {
|
2016-04-03 15:13:18 +00:00
|
|
|
let (rows, columns) = (self.rows, self.columns);
|
|
|
|
self.clear_region(0, rows - 1, 0, columns - 1);
|
2016-03-29 09:22:16 +00:00
|
|
|
}
|
2016-04-03 13:04:52 +00:00
|
|
|
|
2017-03-17 20:38:12 +00:00
|
|
|
pub fn eol_clear(&mut self) -> ModelRect {
|
2016-04-03 13:04:52 +00:00
|
|
|
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);
|
2017-03-17 20:38:12 +00:00
|
|
|
|
2017-04-22 14:22:01 +00:00
|
|
|
ModelRect::new(cur_row, cur_row, cur_col, columns - 1)
|
2016-04-03 13:04:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn clear_region(&mut self, top: usize, bot: usize, left: usize, right: usize) {
|
2016-04-03 15:13:18 +00:00
|
|
|
for row in &mut self.model[top..bot + 1] {
|
2017-08-24 14:41:20 +00:00
|
|
|
row.clear(left, right);
|
2016-04-03 13:04:52 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-01 13:49:10 +00:00
|
|
|
|
2017-09-07 14:48:10 +00:00
|
|
|
pub fn clear_glyphs(&mut self) {
|
2017-09-01 13:49:10 +00:00
|
|
|
for row in &mut self.model.iter_mut() {
|
2017-09-07 14:48:10 +00:00
|
|
|
row.clear_glyphs();
|
2017-09-01 13:49:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-19 08:47:23 +00:00
|
|
|
}
|
2017-03-17 20:38:12 +00:00
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2017-04-22 19:18:59 +00:00
|
|
|
#[test]
|
|
|
|
fn test_vec_join_inside() {
|
2017-04-23 10:42:05 +00:00
|
|
|
let mut list = ModelRectVec::new(ModelRect::new(0, 23, 0, 69));
|
2017-04-22 19:18:59 +00:00
|
|
|
|
|
|
|
let inside = ModelRect::new(23, 23, 68, 69);
|
|
|
|
|
|
|
|
list.join(&inside);
|
|
|
|
assert_eq!(1, list.list.len());
|
|
|
|
}
|
|
|
|
|
2017-04-22 11:31:06 +00:00
|
|
|
#[test]
|
|
|
|
fn test_vec_join_top() {
|
|
|
|
let mut list = ModelRectVec::new(ModelRect::point(0, 0));
|
|
|
|
|
|
|
|
let neighbor = ModelRect::point(1, 0);
|
|
|
|
|
|
|
|
list.join(&neighbor);
|
|
|
|
assert_eq!(1, list.list.len());
|
|
|
|
}
|
|
|
|
|
2017-04-22 19:18:59 +00:00
|
|
|
#[test]
|
|
|
|
fn test_model_vec_join_right() {
|
|
|
|
let mut list = ModelRectVec::new(ModelRect::new(23, 23, 69, 69));
|
|
|
|
|
|
|
|
let neighbor = ModelRect::new(23, 23, 69, 70);
|
|
|
|
|
|
|
|
list.join(&neighbor);
|
|
|
|
assert_eq!(1, list.list.len());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_model_vec_join_right2() {
|
|
|
|
let mut list = ModelRectVec::new(ModelRect::new(0, 1, 0, 9));
|
|
|
|
|
|
|
|
let neighbor = ModelRect::new(1, 1, 9, 10);
|
|
|
|
|
|
|
|
list.join(&neighbor);
|
|
|
|
assert_eq!(1, list.list.len());
|
|
|
|
}
|
|
|
|
|
2017-04-21 15:26:06 +00:00
|
|
|
#[test]
|
|
|
|
fn test_model_vec_join() {
|
|
|
|
let mut list = ModelRectVec::new(ModelRect::point(5, 5));
|
|
|
|
|
|
|
|
let neighbor = ModelRect::point(6, 5);
|
|
|
|
|
2017-04-22 11:31:06 +00:00
|
|
|
list.join(&neighbor);
|
2017-04-21 15:26:06 +00:00
|
|
|
assert_eq!(1, list.list.len());
|
2017-04-21 19:15:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_model_vec_no_join() {
|
|
|
|
let mut list = ModelRectVec::new(ModelRect::point(5, 5));
|
|
|
|
|
|
|
|
let not_neighbor = ModelRect::point(6, 6);
|
2017-04-21 15:26:06 +00:00
|
|
|
|
2017-04-22 11:31:06 +00:00
|
|
|
list.join(¬_neighbor);
|
2017-04-21 15:26:06 +00:00
|
|
|
assert_eq!(2, list.list.len());
|
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cursor_area() {
|
|
|
|
let mut model = UiModel::new(10, 20);
|
|
|
|
|
|
|
|
model.set_cursor(1, 1);
|
|
|
|
|
|
|
|
let rect = model.set_cursor(5, 5);
|
|
|
|
|
2017-04-22 19:18:59 +00:00
|
|
|
assert_eq!(2, rect.list.len());
|
|
|
|
|
|
|
|
assert_eq!(1, rect.list[0].top);
|
|
|
|
assert_eq!(1, rect.list[0].left);
|
|
|
|
assert_eq!(1, rect.list[0].bot);
|
|
|
|
assert_eq!(1, rect.list[0].right);
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(5, rect.list[1].top);
|
|
|
|
assert_eq!(5, rect.list[1].left);
|
|
|
|
assert_eq!(5, rect.list[1].bot);
|
|
|
|
assert_eq!(5, rect.list[1].right);
|
2017-03-18 09:45:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_eol_clear_area() {
|
|
|
|
let mut model = UiModel::new(10, 20);
|
|
|
|
|
2017-04-22 14:22:01 +00:00
|
|
|
model.set_cursor(1, 2);
|
2017-03-18 09:45:05 +00:00
|
|
|
|
|
|
|
let rect = model.eol_clear();
|
|
|
|
|
|
|
|
assert_eq!(1, rect.top);
|
2017-04-22 14:22:01 +00:00
|
|
|
assert_eq!(2, rect.left);
|
2017-03-18 09:45:05 +00:00
|
|
|
assert_eq!(1, rect.bot);
|
|
|
|
assert_eq!(19, rect.right);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_put_area() {
|
|
|
|
let mut model = UiModel::new(10, 20);
|
|
|
|
|
|
|
|
model.set_cursor(1, 1);
|
|
|
|
|
2017-11-23 14:57:39 +00:00
|
|
|
let rect = model.put(' ', false, None);
|
2017-03-18 09:45:05 +00:00
|
|
|
|
|
|
|
assert_eq!(1, rect.top);
|
|
|
|
assert_eq!(1, rect.left);
|
|
|
|
assert_eq!(1, rect.bot);
|
2017-03-18 10:21:20 +00:00
|
|
|
assert_eq!(2, rect.right);
|
2017-03-18 09:45:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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);
|
2017-03-17 22:18:41 +00:00
|
|
|
}
|
2017-03-17 20:38:12 +00:00
|
|
|
}
|