use cell_sheet_core::model::CellPos;
pub struct Viewport {
pub row_offset: usize,
pub col_offset: usize,
pub visible_rows: usize,
pub visible_cols: usize,
}
impl Viewport {
pub fn new() -> Self {
Viewport {
row_offset: 0,
col_offset: 0,
visible_rows: 20,
visible_cols: 10,
}
}
pub fn ensure_visible(&mut self, cursor: CellPos) {
let (row, col) = cursor;
if row < self.row_offset {
self.row_offset = row;
} else if row >= self.row_offset + self.visible_rows {
self.row_offset = row - self.visible_rows + 1;
}
if col < self.col_offset {
self.col_offset = col;
} else if col >= self.col_offset + self.visible_cols {
self.col_offset = col - self.visible_cols + 1;
}
}
pub fn top_on(&mut self, row: usize) {
self.row_offset = row;
}
pub fn center_on(&mut self, row: usize) {
let half = self.visible_rows / 2;
self.row_offset = row.saturating_sub(half);
}
pub fn bottom_on(&mut self, row: usize) {
self.row_offset = (row + 1).saturating_sub(self.visible_rows);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_visible_cursor_within_view_does_not_scroll() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.row_offset = 5;
vp.ensure_visible((10, 0)); assert_eq!(vp.row_offset, 5);
}
#[test]
fn ensure_visible_cursor_at_last_row_does_not_scroll() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.row_offset = 0;
vp.ensure_visible((9, 0)); assert_eq!(vp.row_offset, 0);
}
#[test]
fn ensure_visible_cursor_one_past_last_row_scrolls_down() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.row_offset = 0;
vp.ensure_visible((10, 0)); assert_eq!(vp.row_offset, 1);
}
#[test]
fn ensure_visible_cursor_above_viewport_scrolls_up() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.row_offset = 5;
vp.ensure_visible((3, 0)); assert_eq!(vp.row_offset, 3);
}
#[test]
fn top_on_places_row_at_top() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.top_on(50);
assert_eq!(vp.row_offset, 50);
}
#[test]
fn center_on_places_cursor_in_middle() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.center_on(50);
assert_eq!(vp.row_offset, 45);
}
#[test]
fn center_on_clamps_to_zero_near_top() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.center_on(2);
assert_eq!(vp.row_offset, 0);
}
#[test]
fn bottom_on_places_row_at_bottom() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.bottom_on(50);
assert_eq!(vp.row_offset, 41);
}
#[test]
fn bottom_on_clamps_to_zero_near_top() {
let mut vp = Viewport::new();
vp.visible_rows = 10;
vp.bottom_on(3);
assert_eq!(vp.row_offset, 0);
}
}