use std::ops::Range;
pub const NEAREST_ITEMS_SLIDING_WINDOW_SIZE: usize = 30;
pub const NEAREST_ITEMS_EXTRA_COUNT: usize = 100;
#[derive(Debug, Clone)]
pub struct NearestRangeState {
value: Range<usize>,
last_first_visible_item: usize,
sliding_window_size: usize,
extra_item_count: usize,
}
impl Default for NearestRangeState {
fn default() -> Self {
Self::new(0)
}
}
impl NearestRangeState {
pub fn new(first_visible_item: usize) -> Self {
Self::with_sizes(
first_visible_item,
NEAREST_ITEMS_SLIDING_WINDOW_SIZE,
NEAREST_ITEMS_EXTRA_COUNT,
)
}
pub fn with_sizes(
first_visible_item: usize,
sliding_window_size: usize,
extra_item_count: usize,
) -> Self {
let value =
Self::calculate_range(first_visible_item, sliding_window_size, extra_item_count);
Self {
value,
last_first_visible_item: first_visible_item,
sliding_window_size,
extra_item_count,
}
}
pub fn range(&self) -> Range<usize> {
self.value.clone()
}
pub fn update(&mut self, first_visible_item: usize) {
if first_visible_item != self.last_first_visible_item {
self.last_first_visible_item = first_visible_item;
self.value = Self::calculate_range(
first_visible_item,
self.sliding_window_size,
self.extra_item_count,
);
}
}
fn calculate_range(
first_visible_item: usize,
sliding_window_size: usize,
extra_item_count: usize,
) -> Range<usize> {
let sliding_window_start =
sliding_window_size.saturating_mul(first_visible_item / sliding_window_size);
let start = sliding_window_start.saturating_sub(extra_item_count);
let end = sliding_window_start
.saturating_add(sliding_window_size)
.saturating_add(extra_item_count);
start..end
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initial_range() {
let state = NearestRangeState::new(0);
assert_eq!(state.range(), 0..130);
}
#[test]
fn test_range_after_small_scroll() {
let mut state = NearestRangeState::new(0);
state.update(5);
assert_eq!(state.range(), 0..130);
}
#[test]
fn test_range_after_crossing_window() {
let mut state = NearestRangeState::new(0);
state.update(35);
assert_eq!(state.range(), 0..160);
}
#[test]
fn test_range_far_scroll() {
let mut state = NearestRangeState::new(0);
state.update(1000);
assert_eq!(state.range(), 890..1120);
}
}