1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
//! An undo/redo library. //! //! It uses the [Command Pattern] where the user implements the `UndoCmd` trait for each command //! and then the commands can be used with the `UndoStack`. //! //! The `UndoStack` has two different states, clean and dirty. The stack is in a clean state when //! there are no more commands that can be redone, otherwise it's in a dirty state. The stack //! can be configured to call a given method when this state changes, using the [on_clean] and //! [on_dirty] methods. //! //! The `UndoStack` also supports automatic merging of commands that has the same [id]. //! //! # Example //! ``` //! use std::rc::Rc; //! use std::cell::RefCell; //! use undo::{UndoCmd, UndoStack}; //! //! /// Pops an element from a vector. //! #[derive(Clone)] //! struct PopCmd { //! vec: Rc<RefCell<Vec<i32>>>, //! e: Option<i32>, //! } //! //! impl UndoCmd for PopCmd { //! fn redo(&mut self) { //! self.e = self.vec.borrow_mut().pop(); //! } //! //! fn undo(&mut self) { //! self.vec.borrow_mut().push(self.e.unwrap()); //! self.e = None; //! } //! } //! //! // We need to use Rc<RefCell> since all commands are going to mutate the vec. //! let vec = Rc::new(RefCell::new(vec![1, 2, 3])); //! let mut stack = UndoStack::new() //! .on_clean(|| println!("This is called when the stack changes from dirty to clean!")) //! .on_dirty(|| println!("This is called when the stack changes from clean to dirty!")); //! //! let cmd = PopCmd { vec: vec.clone(), e: None }; //! stack.push(cmd.clone()); //! stack.push(cmd.clone()); //! stack.push(cmd.clone()); //! //! assert!(vec.borrow().is_empty()); //! //! stack.undo(); // on_dirty is going to be called here. //! stack.undo(); //! stack.undo(); //! //! assert_eq!(vec.borrow().len(), 3); //! ``` //! //! [Command Pattern]: https://en.wikipedia.org/wiki/Command_pattern //! [on_clean]: struct.UndoStack.html#method.on_clean //! [on_dirty]: struct.UndoStack.html#method.on_dirty //! [id]: trait.UndoCmd.html#method.id extern crate fnv; mod group; mod stack; pub use group::{Uid, UndoGroup}; pub use stack::UndoStack; /// Every command needs to implement the `UndoCmd` trait to be able to be used with the `UndoStack`. pub trait UndoCmd { /// Executes the desired command. fn redo(&mut self); /// Restores the state as it was before [`redo`] was called. /// /// [`redo`]: trait.UndoCmd.html#tymethod.redo fn undo(&mut self); /// Used for merging of `UndoCmd`s. /// /// When two commands are merged together, undoing and redoing them are done in one step. /// An example where this is useful is a text editor where you might want to undo a whole word /// instead of each character. /// /// Two commands are merged together when a command is pushed on the `UndoStack`, and it has /// the same id as the top command already on the stack. It is normal to have an unique /// id for each implementation of `UndoCmd`, but this is not mandatory. /// /// Default implementation returns `None`, which means the command will never be merged. /// /// # Example /// ``` /// use std::rc::Rc; /// use std::cell::RefCell; /// use undo::{UndoCmd, UndoStack}; /// /// /// Pops an element from a vector. /// #[derive(Clone)] /// struct PopCmd { /// vec: Rc<RefCell<Vec<i32>>>, /// e: Option<i32>, /// } /// /// impl UndoCmd for PopCmd { /// fn redo(&mut self) { /// self.e = self.vec.borrow_mut().pop(); /// } /// /// fn undo(&mut self) { /// self.vec.borrow_mut().push(self.e.unwrap()); /// self.e = None; /// } /// /// fn id(&self) -> Option<u64> { /// Some(1) /// } /// } /// /// fn main() { /// // We need to use Rc<RefCell> since all commands are going to mutate the vec. /// let vec = Rc::new(RefCell::new(vec![1, 2, 3])); /// let mut stack = UndoStack::new(); /// /// let cmd = PopCmd { vec: vec.clone(), e: None }; /// stack.push(cmd.clone()); /// stack.push(cmd.clone()); /// stack.push(cmd.clone()); /// /// assert!(vec.borrow().is_empty()); /// /// stack.undo(); /// /// assert_eq!(vec, Rc::new(RefCell::new(vec![1,2,3]))); /// /// stack.redo(); /// /// assert!(vec.borrow().is_empty()); /// } /// ``` fn id(&self) -> Option<u64> { None } }