summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVille Hakulinen <ville.hakulinen@gmail.com>2020-07-23 00:25:29 +0300
committerVille Hakulinen <ville.hakulinen@gmail.com>2020-07-23 00:35:51 +0300
commit675604525e5feb02f26ef962b2f6d95ea112b1f3 (patch)
tree202cd0a1cc4c57e258f6b0ba929430ea589f658a
parent356c8849630e66885f5182696d6683da4384b61f (diff)
Make cursor more testable
-rw-r--r--src/ui/grid/context.rs34
-rw-r--r--src/ui/grid/cursor.rs105
-rw-r--r--src/ui/grid/grid.rs6
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 {