Merge branch 'invalidate-area'
This commit is contained in:
commit
4e00df7b0b
100
src/nvim.rs
100
src/nvim.rs
@ -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 ¶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>) {
|
||||
&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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
141
src/shell.rs
141
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::*;
|
||||
@ -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 {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
334
src/ui_model.rs
334
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,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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user