use std::collections::VecDeque;
pub const DEFAULT_UNDO_CAPACITY: usize = 50;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NSUndoManager<T> {
undo_stack: VecDeque<T>,
redo_stack: VecDeque<T>,
capacity: usize,
active_group_initial: Option<T>,
}
impl<T: Clone + PartialEq> Default for NSUndoManager<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Clone + PartialEq> NSUndoManager<T> {
pub fn new() -> Self {
Self::with_capacity(DEFAULT_UNDO_CAPACITY)
}
pub fn with_capacity(capacity: usize) -> Self {
let cap = capacity.max(1);
Self {
undo_stack: VecDeque::with_capacity(cap),
redo_stack: VecDeque::with_capacity(cap),
capacity: cap,
active_group_initial: None,
}
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn undo_count(&self) -> usize {
self.undo_stack.len()
}
#[inline]
pub fn redo_count(&self) -> usize {
self.redo_stack.len()
}
#[inline]
pub fn can_undo(&self) -> bool {
!self.undo_stack.is_empty()
}
#[inline]
pub fn can_redo(&self) -> bool {
!self.redo_stack.is_empty()
}
#[inline]
pub fn is_grouping(&self) -> bool {
self.active_group_initial.is_some()
}
#[inline]
pub fn peek_undo(&self) -> Option<&T> {
self.undo_stack.back()
}
#[inline]
pub fn peek_redo(&self) -> Option<&T> {
self.redo_stack.back()
}
pub fn begin_grouping(&mut self, initial_state: T) {
if self.active_group_initial.is_none() {
self.active_group_initial = Some(initial_state);
}
}
pub fn end_grouping(&mut self, final_state: &T) -> bool {
if let Some(initial) = self.active_group_initial.take() {
if &initial != final_state {
self.redo_stack.clear();
if self.undo_stack.back() != Some(&initial) {
if self.undo_stack.len() >= self.capacity {
self.undo_stack.pop_front();
}
self.undo_stack.push_back(initial);
}
return true;
}
}
false
}
pub fn push_snapshot(&mut self, state: T) {
if self.active_group_initial.is_some() {
return;
}
self.redo_stack.clear();
if self.undo_stack.back() == Some(&state) {
return;
}
if self.undo_stack.len() >= self.capacity {
self.undo_stack.pop_front();
}
self.undo_stack.push_back(state);
}
pub fn undo(&mut self, current_state: &mut T) -> bool {
if let Some(initial) = self.active_group_initial.take() {
if *current_state != initial {
if self.redo_stack.len() >= self.capacity {
self.redo_stack.pop_front();
}
self.redo_stack.push_back(current_state.clone());
*current_state = initial;
return true;
}
}
if let Some(prev) = self.undo_stack.pop_back() {
if self.redo_stack.len() >= self.capacity {
self.redo_stack.pop_front();
}
self.redo_stack.push_back(current_state.clone());
*current_state = prev;
true
} else {
false
}
}
pub fn redo(&mut self, current_state: &mut T) -> bool {
self.active_group_initial = None;
if let Some(next) = self.redo_stack.pop_back() {
if self.undo_stack.len() >= self.capacity {
self.undo_stack.pop_front();
}
self.undo_stack.push_back(current_state.clone());
*current_state = next;
true
} else {
false
}
}
pub fn clear(&mut self) {
self.undo_stack.clear();
self.redo_stack.clear();
self.active_group_initial = None;
}
}