diff --git a/src/cursor.rs b/src/cursor.rs index 6a5d1bb..5522474 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -3,6 +3,7 @@ use ui_model::Color; use ui::UiMutex; use shell; use mode; +use nvim; use nvim::{RepaintMode, RedrawEvents}; use std::sync::{Arc, Weak}; @@ -123,7 +124,63 @@ impl Cursor { let current_point = ctx.get_current_point(); ctx.set_source_rgba(1.0 - bg.0, 1.0 - bg.1, 1.0 - bg.2, 0.6 * state.alpha.0); - let cursor_width = if shell.mode.is(&mode::NvimMode::Insert) { + let (y, width, height) = + cursor_rect(&shell.mode, char_width, line_height, line_y, double_width); + + ctx.rectangle(current_point.0, y, width, height); + if state.anim_phase == AnimPhase::NoFocus { + ctx.stroke(); + } else { + ctx.fill(); + } + } +} + +fn cursor_rect(mode: &mode::Mode, + char_width: f64, + line_height: f64, + line_y: f64, + double_width: bool) + -> (f64, f64, f64) { + if let Some(mode_info) = mode.mode_info() { + match mode_info.cursor_shape() { + None | + Some(&nvim::CursorShape::Unknown) | + Some(&nvim::CursorShape::Block) => { + let cursor_width = if double_width { + char_width * 2.0 + } else { + char_width + }; + (line_y, cursor_width, line_height) + } + Some(&nvim::CursorShape::Vertical) => { + let cell_percentage = mode_info.cell_percentage(); + let cursor_width = if cell_percentage > 0 { + (char_width * cell_percentage as f64) / 100.0 + } else { + char_width + }; + (line_y, cursor_width, line_height) + } + Some(&nvim::CursorShape::Horizontal) => { + let cell_percentage = mode_info.cell_percentage(); + let cursor_width = if double_width { + char_width * 2.0 + } else { + char_width + }; + + if cell_percentage > 0 { + let height = (line_height * cell_percentage as f64) / 100.0; + (line_y + line_height - height, cursor_width, height) + } else { + (line_y, cursor_width, line_height) + } + } + } + } else { + let cursor_width = if mode.is(&mode::NvimMode::Insert) { char_width / 5.0 } else { if double_width { @@ -133,15 +190,9 @@ impl Cursor { } }; - ctx.rectangle(current_point.0, line_y, cursor_width, line_height); - if state.anim_phase == AnimPhase::NoFocus { - ctx.stroke(); - } else { - ctx.fill(); - } + (line_y, cursor_width, line_height) } } - fn anim_step(state: &Arc>) -> glib::Continue { let mut mut_state = state.borrow_mut(); @@ -201,3 +252,62 @@ impl Drop for Cursor { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cursor_rect_horizontal() { + let mut mode = mode::Mode::new(); + let mode_info = nvim::ModeInfo::new(&vec![(From::from("cursor_shape"), + From::from("horizontal")), + (From::from("cell_percentage"), From::from(25))]); + mode.update("insert", 0); + mode.set_info(true, vec![mode_info.unwrap()]); + let char_width = 50.0; + let line_height = 30.0; + let line_y = 0.0; + + let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, false); + assert_eq!(line_y + line_height - line_height / 4.0, y); + assert_eq!(char_width, width); + assert_eq!(line_height / 4.0, height); + } + + #[test] + fn test_cursor_rect_horizontal_doublewidth() { + let mut mode = mode::Mode::new(); + let mode_info = nvim::ModeInfo::new(&vec![(From::from("cursor_shape"), + From::from("horizontal")), + (From::from("cell_percentage"), From::from(25))]); + mode.update("insert", 0); + mode.set_info(true, vec![mode_info.unwrap()]); + let char_width = 50.0; + let line_height = 30.0; + let line_y = 0.0; + + let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, true); + assert_eq!(line_y + line_height - line_height / 4.0, y); + assert_eq!(char_width * 2.0, width); + assert_eq!(line_height / 4.0, height); + } + + #[test] + fn test_cursor_rect_vertical() { + let mut mode = mode::Mode::new(); + let mode_info = nvim::ModeInfo::new(&vec![(From::from("cursor_shape"), + From::from("vertical")), + (From::from("cell_percentage"), From::from(25))]); + mode.update("insert", 0); + mode.set_info(true, vec![mode_info.unwrap()]); + let char_width = 50.0; + let line_height = 30.0; + let line_y = 0.0; + + let (y, width, height) = cursor_rect(&mode, char_width, line_height, line_y, false); + assert_eq!(line_y, y); + assert_eq!(char_width / 4.0, width); + assert_eq!(line_height, height); + } +} diff --git a/src/mode.rs b/src/mode.rs index fab111f..e8fdc60 100644 --- a/src/mode.rs +++ b/src/mode.rs @@ -26,11 +26,10 @@ impl Mode { self.mode == *mode } - pub fn mode_info(&self) -> nvim::ModeInfo { + pub fn mode_info(&self) -> Option<&nvim::ModeInfo> { self.info .as_ref() - .and_then(|i| i.get(self.idx).cloned()) - .unwrap_or_else(nvim::ModeInfo::default) + .and_then(|i| i.get(self.idx)) } pub fn update(&mut self, mode: &str, idx: usize) { diff --git a/src/nvim.rs b/src/nvim.rs index b0409dd..eabcae5 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -139,13 +139,6 @@ pub struct ModeInfo { } impl ModeInfo { - pub fn default() -> Self { - ModeInfo { - cursor_shape: Some(CursorShape::Block), - cell_percentage: None, - } - } - pub fn new(mode_info_arr: &Vec<(Value, Value)>) -> Result { let mode_info_map = mode_info_arr.to_attrs_map()?; @@ -167,12 +160,12 @@ impl ModeInfo { }) } - pub fn cursor_shape(&self) -> CursorShape { - self.cursor_shape.as_ref().cloned().unwrap_or(CursorShape::Block) + pub fn cursor_shape(&self) -> Option<&CursorShape> { + self.cursor_shape.as_ref() } pub fn cell_percentage(&self) -> u64 { - self.cell_percentage.unwrap_or(100) + self.cell_percentage.unwrap_or(0) } }