use re_viewer_context::ItemCollection;
const MAX_SELECTION_HISTORY_LENGTH: usize = 100;
#[derive(Clone, Default, Debug)]
pub struct SelectionHistory {
pub current: usize,
pub stack: Vec<ItemCollection>,
}
impl SelectionHistory {
pub fn current(&self) -> Option<&ItemCollection> {
self.stack.get(self.current)
}
pub fn previous(&self) -> Option<(usize, &ItemCollection)> {
let prev = self.current.checked_sub(1)?;
Some((prev, self.stack.get(prev)?))
}
pub fn next(&self) -> Option<(usize, &ItemCollection)> {
let next = self.current + 1;
Some((next, self.stack.get(next)?))
}
#[must_use]
pub fn select_previous(&mut self) -> Option<ItemCollection> {
let (prev_index, _) = self.previous()?;
self.current = prev_index;
self.current().cloned()
}
#[must_use]
pub fn select_next(&mut self) -> Option<ItemCollection> {
let (next_index, _) = self.next()?;
self.current = next_index;
self.current().cloned()
}
pub fn update_selection(&mut self, selection: &ItemCollection) {
if selection.is_empty() {
return;
}
if self.current().map(|c| c == selection).unwrap_or(false) {
return;
}
self.stack.truncate(self.current + 1);
self.stack.push(selection.clone());
if self.stack.len() > MAX_SELECTION_HISTORY_LENGTH {
let excess = self.stack.len() - MAX_SELECTION_HISTORY_LENGTH;
self.stack.drain(0..excess);
}
self.current = self.stack.len() - 1;
}
}
#[cfg(test)]
mod tests {
use re_log_types::ApplicationId;
use re_viewer_context::Item;
use super::*;
fn selection(name: &str) -> ItemCollection {
ItemCollection::from(Item::AppId(ApplicationId::new_or_unknown(name)))
}
#[test]
fn record_then_navigate_back_and_forward() {
let mut history = SelectionHistory::default();
history.update_selection(&selection("a"));
history.update_selection(&selection("b"));
history.update_selection(&selection("c"));
assert_eq!(history.current(), Some(&selection("c")));
assert_eq!(history.select_previous(), Some(selection("b")));
assert_eq!(history.select_previous(), Some(selection("a")));
assert_eq!(history.select_previous(), None); assert_eq!(history.select_next(), Some(selection("b")));
assert_eq!(history.select_next(), Some(selection("c")));
assert_eq!(history.select_next(), None); }
#[test]
fn identical_and_empty_selections_are_not_recorded() {
let mut history = SelectionHistory::default();
history.update_selection(&selection("a"));
history.update_selection(&selection("a"));
history.update_selection(&ItemCollection::default());
assert_eq!(history.stack.len(), 1);
}
#[test]
fn diverging_clears_forward_history() {
let mut history = SelectionHistory::default();
history.update_selection(&selection("a"));
history.update_selection(&selection("b"));
history.update_selection(&selection("c"));
history.select_previous().unwrap();
history.update_selection(&selection("d"));
assert_eq!(history.current(), Some(&selection("d")));
assert_eq!(history.stack.len(), 3); assert_eq!(history.next(), None);
}
#[test]
fn length_is_bounded() {
let mut history = SelectionHistory::default();
for i in 0..(2 * MAX_SELECTION_HISTORY_LENGTH) {
history.update_selection(&selection(&i.to_string()));
}
assert_eq!(history.stack.len(), MAX_SELECTION_HISTORY_LENGTH);
assert_eq!(history.current, MAX_SELECTION_HISTORY_LENGTH - 1);
assert_eq!(
history.stack.first(),
Some(&selection(&MAX_SELECTION_HISTORY_LENGTH.to_string()))
);
}
}