diff options
author | Ville Hakulinen <ville.hakulinen@gmail.com> | 2020-07-23 00:25:29 +0300 |
---|---|---|
committer | Ville Hakulinen <ville.hakulinen@gmail.com> | 2020-07-23 00:35:51 +0300 |
commit | 675604525e5feb02f26ef962b2f6d95ea112b1f3 (patch) | |
tree | 202cd0a1cc4c57e258f6b0ba929430ea589f658a | |
parent | 356c8849630e66885f5182696d6683da4384b61f (diff) |
Make cursor more testable
-rw-r--r-- | src/ui/grid/context.rs | 34 | ||||
-rw-r--r-- | src/ui/grid/cursor.rs | 105 | ||||
-rw-r--r-- | src/ui/grid/grid.rs | 6 |
3 files changed, 97 insertions, 48 deletions
diff --git a/src/ui/grid/context.rs b/src/ui/grid/context.rs index 3c563d6..9709ead 100644 --- a/src/ui/grid/context.rs +++ b/src/ui/grid/context.rs @@ -7,7 +7,7 @@ use crate::ui::color::HlDefs; use crate::ui::font::Font; use crate::ui::grid::cursor::Cursor; use crate::ui::grid::render; -use crate::ui::grid::row::Row; +use crate::ui::grid::row::{Cell, Row}; /// Context is manipulated by Grid. pub struct Context { @@ -208,24 +208,13 @@ impl Context { /// Returns x, y, width and height for current cursor location. pub fn get_cursor_rect(&self) -> (i32, i32, i32, i32) { let double_width = self - .rows - .get(self.cursor.pos.0 as usize) - .and_then(|row| { - Some( - row.cell_at(self.cursor.pos.1 as usize) - .map(|c| c.double_width) - .unwrap_or(false), - ) - }) + .cell_at_cursor() + .and_then(|cell| Some(cell.double_width)) .unwrap_or(false); + let pos = self.cursor.pos.unwrap_or((0.0, 0.0)); let cm = &self.cell_metrics; - let (x, y) = render::get_coords( - cm.height, - cm.width, - self.cursor.pos.0, - self.cursor.pos.1, - ); + let (x, y) = render::get_coords(cm.height, cm.width, pos.0, pos.1); ( x.floor() as i32, y.floor() as i32, @@ -247,7 +236,8 @@ impl Context { f64::from(w), f64::from(h), )); - self.cursor.goto(row as f64, col as f64, clock); + self.cursor + .goto(row as f64, col as f64, clock.get_frame_time()); // Mark the new cursor position to be drawn. let (x, y, w, h) = self.get_cursor_rect(); @@ -263,7 +253,7 @@ impl Context { let (x, y, w, h) = self.get_cursor_rect(); da.queue_draw_area(x, y, w, h); - self.cursor.tick(clock); + self.cursor.tick(clock.get_frame_time()); let (x, y, w, h) = self.get_cursor_rect(); @@ -290,6 +280,14 @@ impl Context { // on each tick so the cursor blinks. da.queue_draw_area(x, y, w, h); } + + pub fn cell_at_cursor(&self) -> Option<&Cell> { + self.cursor.pos.and_then(|pos| { + self.rows + .get(pos.0.ceil() as usize) + .and_then(|row| row.cell_at(pos.1.ceil() as usize)) + }) + } } /// Cell metrics tells the size (and other metrics) of the cells in a grid. diff --git a/src/ui/grid/cursor.rs b/src/ui/grid/cursor.rs index 9290159..da1532b 100644 --- a/src/ui/grid/cursor.rs +++ b/src/ui/grid/cursor.rs @@ -1,5 +1,3 @@ -use gdk; - use crate::ui::color::Color; #[derive(Default)] @@ -13,8 +11,7 @@ struct Animation { #[derive(Default)] pub struct Cursor { /// Position, (row, col). - pub pos: (f64, f64), - got_first_goto: bool, + pub pos: Option<(f64, f64)>, animation: Animation, /// Alpha color. Used to make the cursor blink. @@ -28,25 +25,23 @@ pub struct Cursor { } impl Cursor { - pub fn goto(&mut self, row: f64, col: f64, clock: &gdk::FrameClock) { - if !self.got_first_goto { - self.pos = (row, col); - self.got_first_goto = true; + pub fn goto(&mut self, row: f64, col: f64, frame_time: i64) { + if self.pos.is_none() { + self.pos = Some((row, col)); } - let now = clock.get_frame_time(); let duration = 200; self.animation = Animation { - start: self.pos, + start: self.pos.unwrap(), end: (row, col), - start_time: now, - end_time: now + 1000 * duration, + start_time: frame_time, + end_time: frame_time + 1000 * duration, }; } - pub fn tick(&mut self, clock: &gdk::FrameClock) { + pub fn tick(&mut self, frame_time: i64) { self.blink(); - self.animate_position(clock); + self.animate_position(frame_time); } fn blink(&mut self) { @@ -63,18 +58,26 @@ impl Cursor { } } - fn animate_position(&mut self, clock: &gdk::FrameClock) { - let now = clock.get_frame_time(); - if now < self.animation.end_time && self.pos != self.animation.end { - let mut t = (now - self.animation.start_time) as f64 - / (self.animation.end_time - self.animation.start_time) as f64; + fn animate_position(&mut self, frame_time: i64) { + let Animation { + start, + end, + start_time, + end_time, + } = self.animation; + + let mut pos = self.pos.unwrap_or((0.0, 0.0)); + + if frame_time < end_time && pos != end { + let mut t = (frame_time - start_time) as f64 + / (end_time - start_time) as f64; t = ease_out_cubic(t); - self.pos.0 = self.animation.start.0 - + t * (self.animation.end.0 - self.animation.start.0); - self.pos.1 = self.animation.start.1 - + t * (self.animation.end.1 - self.animation.start.1); - } else if self.pos != self.animation.end { - self.pos = self.animation.end; + pos.0 = start.0 + t * (end.0 - start.0); + pos.1 = start.1 + t * (end.1 - start.1); + + self.pos = Some(pos); + } else if pos != end { + self.pos = Some(end); } } } @@ -85,3 +88,55 @@ fn ease_out_cubic(t: f64) -> f64 { let p = t - 1f64; return p * p * p + 1f64; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cursor_blink100() { + let mut cursor = Cursor::default(); + cursor.blink_on = 100; + cursor.alpha = 1.0; + + cursor.blink(); + assert_eq!(cursor.alpha, 1.1666666666666667); + } + + #[test] + fn test_cursor_blink0() { + let mut cursor = Cursor::default(); + cursor.blink_on = 0; + cursor.alpha = 1.0; + + cursor.blink(); + assert_eq!(cursor.alpha, 1.0); + } + + #[test] + fn test_first_position() { + let mut cursor = Cursor::default(); + + // When we first set the position, it should be set immediately. + cursor.goto(15.0, 15.0, 1); + assert_eq!(cursor.pos, Some((15.0, 15.0))); + + // When we've set the position once already, the subsequent goto positions should be set + // with some delay by the animation. + cursor.goto(10.0, 10.0, 1); + assert_eq!(cursor.pos, Some((15.0, 15.0))); + } + + #[test] + fn test_animate_position() { + let mut cursor = Cursor::default(); + + // When we first set the position, it should be set immediately. + cursor.goto(15.0, 15.0, 1); + assert_eq!(cursor.pos, Some((15.0, 15.0))); + + cursor.goto(10.0, 10.0, 1); + cursor.tick(25000); + assert_eq!(cursor.pos, Some((13.349666797203126, 13.349666797203126))); + } +} diff --git a/src/ui/grid/grid.rs b/src/ui/grid/grid.rs index 412eff5..77939ca 100644 --- a/src/ui/grid/grid.rs +++ b/src/ui/grid/grid.rs @@ -128,11 +128,7 @@ impl Grid { pub fn flush(&self, hl_defs: &HlDefs) { let mut ctx = self.context.borrow_mut(); - if let Some(Some(cell)) = ctx - .rows - .get(ctx.cursor.pos.0 as usize) - .map(|row| row.cell_at(ctx.cursor.pos.1 as usize)) - { + if let Some(cell) = ctx.cell_at_cursor() { // If cursor isn't blinking, drawn the inverted cell into // the cursor's cairo context. if ctx.cursor.blink_on == 0 { |