forked from SpaceManiac/SpacemanDMM
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhistory.rs
More file actions
102 lines (85 loc) · 2.63 KB
/
history.rs
File metadata and controls
102 lines (85 loc) · 2.63 KB
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
//! Reified undo/redo history tree.
use std::cell::Cell;
use petgraph::Direction;
use petgraph::graph::{NodeIndex, Graph};
use petgraph::visit::EdgeRef;
pub struct History<T, E> {
current: T,
idx: NodeIndex,
clean_idx: Cell<NodeIndex>,
graph: Graph<Entry, Edit<T, E>>,
}
struct Entry {
desc: String,
}
struct Edit<T, E> {
redo: Box<dyn Fn(&E, &mut T) -> Box<dyn Fn(&E, &mut T)>>,
undo: Box<dyn Fn(&E, &mut T)>,
}
impl<T, E> History<T, E> {
pub fn new(desc: String, current: T) -> Self {
let mut graph = Graph::default();
let idx = graph.add_node(Entry { desc });
History {
current,
idx,
clean_idx: Cell::new(idx),
graph,
}
}
pub fn current(&self) -> &T {
&self.current
}
pub fn replace_current(&mut self, new: T) {
// TODO: somehow verify that the new value is semantically equivalent
// to what we currently have.
self.current = new;
}
pub fn is_dirty(&self) -> bool {
self.clean_idx.get() != self.idx
}
pub fn mark_clean(&self) {
self.clean_idx.set(self.idx);
}
pub fn edit<F: 'static + Fn(&E, &mut T) -> Box<dyn Fn(&E, &mut T)>>(&mut self, env: &E, desc: String, f: F) {
// perform the edit immediately
let undo = f(env, &mut self.current);
// save the edit to the history
let new_idx = self.graph.add_node(Entry {
desc,
});
self.graph.add_edge(self.idx, new_idx, Edit {
redo: Box::new(f),
undo,
});
self.idx = new_idx;
}
fn parent(&self, idx: NodeIndex) -> Option<NodeIndex> {
self.graph.neighbors_directed(idx, Direction::Incoming).next()
}
pub fn can_undo(&self) -> bool {
self.parent(self.idx).is_some()
}
pub fn undo(&mut self, env: &E) {
if let Some(edge) = self.graph.edges_directed(self.idx, Direction::Incoming).last() {
(edge.weight().undo)(env, &mut self.current);
self.idx = edge.source();
}
}
pub fn can_redo(&self) -> bool {
self.graph
.neighbors_directed(self.idx, Direction::Outgoing)
.next()
.is_some()
}
pub fn redo(&mut self, env: &E) {
let mut undo = None;
if let Some(edge) = self.graph.edges_directed(self.idx, Direction::Outgoing).last() {
undo = Some((edge.id(), (edge.weight().redo)(env, &mut self.current)));
self.idx = edge.target();
}
if let Some((id, undo)) = undo {
self.graph.edge_weight_mut(id).unwrap().undo = undo;
}
}
}