use crate::{bitmap::MapPoint, brush::Brush}; #[derive(Debug, Clone)] pub enum ModifyRecord { Paint(Vec), Invert, Brush { old: Brush, new: Brush }, } #[derive(Debug, Copy, Clone)] pub struct PaintRecord { pub point: MapPoint, pub old: bool, pub new: bool, } impl PaintRecord { pub fn new(point: MapPoint, old: bool, new: bool) -> Self { Self { point, old, new } } } pub enum OpKind { Undo, Redo, } #[derive(Debug)] pub struct UndoStack { operations: Vec, position: Option, } impl UndoStack where T: Clone, { pub fn new() -> Self { Self { operations: Vec::with_capacity(64), position: None, } } pub fn push(&mut self, op: T) { if let Some(p) = self.position { // remove all operations past the newly pushed operation for _ in 1 + (p as usize)..self.operations.len() { self.operations.pop(); } // advance position self.position = Some(p + 1); // add new operation self.operations.push(op); } else { // empty ops list or undone till start of stack // remove all operations past the newly pushed operation self.operations.clear(); // advance position self.position = Some(0); // add new operation self.operations.push(op); } } pub fn undo(&mut self) -> Option { if let Some(p) = self.position { self.position = p.checked_sub(1); // we want to return a clone and not a reference because push deletes the item Some(self.operations[p as usize].clone()) } else { None } } pub fn redo(&mut self) -> Option { if let Some(p) = self.position { if p < self.operations.len() as u32 - 1 { self.position = Some(p + 1); return Some(self.operations[1 + p as usize].clone()); } } else if !self.operations.is_empty() { self.position = Some(0); return Some(self.operations[0].clone()); } None } } impl std::default::Default for UndoStack where T: Clone, { fn default() -> Self { UndoStack::new() } } #[cfg(test)] mod tests { use super::*; fn setup() -> UndoStack { let mut stack = UndoStack::new(); stack.push(10); stack.push(5); stack.push(2); stack } #[test] fn undo_works() { let mut stack = setup(); assert_eq!(stack.undo(), Some(2)); assert_eq!(stack.undo(), Some(5)); assert_eq!(stack.undo(), Some(10)); } #[test] fn redo_works() { let mut stack = setup(); stack.undo(); stack.undo(); stack.undo(); assert_eq!(stack.redo(), Some(10)); assert_eq!(stack.redo(), Some(5)); assert_eq!(stack.redo(), Some(2)); } #[test] fn undo_push_redo() { let mut stack = setup(); stack.undo(); stack.push(16); assert_eq!(stack.redo(), None); assert_eq!(stack.undo(), Some(16)); } #[test] fn stack_identity() { let mut stack = setup(); stack.undo(); stack.redo(); stack.undo(); assert_eq!(stack.operations, setup().operations); } }