ferrum_flow/canvas/
undo.rs1use std::collections::HashMap;
2
3use gpui::{Bounds, Pixels, Point};
4
5use crate::{Edge, EdgeId, Graph, Node, NodeBuilder, NodeId, Port, PortId, Viewport};
6
7pub trait Command {
8 fn name(&self) -> &'static str;
9 fn execute(&mut self, ctx: &mut CanvasState);
10 fn undo(&mut self, ctx: &mut CanvasState);
11}
12
13pub struct CanvasState<'a> {
14 pub graph: &'a mut Graph,
15 pub viewport: &'a mut Viewport,
16}
17
18const MAX_HISTORY: usize = 100;
19
20pub struct History {
21 undo_stack: Vec<Box<dyn Command>>,
22 redo_stack: Vec<Box<dyn Command>>,
23}
24
25impl History {
26 pub fn new() -> Self {
27 Self {
28 undo_stack: Vec::new(),
29 redo_stack: Vec::new(),
30 }
31 }
32
33 pub fn execute(&mut self, mut command: Box<dyn Command>, state: &mut CanvasState) {
34 command.execute(state);
35
36 self.undo_stack.push(command);
37 if self.undo_stack.len() > MAX_HISTORY {
38 self.undo_stack.remove(0);
39 }
40 self.redo_stack.clear();
41 }
42
43 pub fn undo(&mut self, state: &mut CanvasState) {
44 if let Some(mut cmd) = self.undo_stack.pop() {
45 cmd.undo(state);
46 self.redo_stack.push(cmd);
47 }
48 }
49
50 pub fn redo(&mut self, state: &mut CanvasState) {
51 if let Some(mut cmd) = self.redo_stack.pop() {
52 cmd.execute(state);
53 self.undo_stack.push(cmd);
54 }
55 }
56}
57
58pub struct CompositeCommand {
59 commands: Vec<Box<dyn Command>>,
60}
61
62impl CompositeCommand {
63 pub fn new() -> Self {
64 Self {
65 commands: Vec::new(),
66 }
67 }
68 pub fn push(&mut self, command: impl Command + 'static) {
69 self.commands.push(Box::new(command));
70 }
71}
72
73impl Command for CompositeCommand {
74 fn name(&self) -> &'static str {
75 "composite"
76 }
77 fn execute(&mut self, state: &mut CanvasState) {
78 for cmd in &mut self.commands {
79 cmd.execute(state);
80 }
81 }
82
83 fn undo(&mut self, state: &mut CanvasState) {
84 for cmd in self.commands.iter_mut().rev() {
85 cmd.undo(state);
86 }
87 }
88}
89
90impl<'a> CanvasState<'a> {
91 pub fn create_node(&self, node_type: &str) -> NodeBuilder {
92 self.graph.create_node(node_type)
93 }
94
95 pub fn next_node_id(&self) -> NodeId {
96 self.graph.next_node_id()
97 }
98
99 pub fn next_port_id(&self) -> PortId {
100 self.graph.next_port_id()
101 }
102
103 pub fn next_edge_id(&self) -> EdgeId {
104 self.graph.next_edge_id()
105 }
106 pub fn add_node(&mut self, node: Node) {
107 self.graph.add_node(node);
108 }
109
110 pub fn add_point(&mut self, port: Port) {
111 self.graph.add_point(port);
112 }
113
114 pub fn get_node(&self, id: &NodeId) -> Option<&Node> {
115 self.graph.get_node(id)
116 }
117
118 pub fn get_node_mut(&mut self, id: &NodeId) -> Option<&mut Node> {
119 self.graph.get_node_mut(id)
120 }
121 pub fn remove_node(&mut self, id: &NodeId) {
122 self.graph.remove_node(id);
123 }
124 pub fn nodes(&self) -> &HashMap<NodeId, Node> {
125 self.graph.nodes()
126 }
127 pub fn node_order(&self) -> &Vec<NodeId> {
128 self.graph.node_order()
129 }
130
131 pub fn new_edge(&self) -> Edge {
132 self.graph.new_edge()
133 }
134
135 pub fn add_edge(&mut self, edge: Edge) {
136 self.graph.add_edge(edge);
137 }
138
139 pub fn remove_edge(&mut self, edge_id: EdgeId) {
140 self.graph.remove_edge(edge_id);
141 }
142
143 pub fn add_selected_node(&mut self, id: NodeId, shift: bool) {
144 self.graph.add_selected_node(id, shift);
145 }
146 pub fn clear_selected_node(&mut self) {
147 self.graph.clear_selected_node();
148 }
149 pub fn remove_selected_node(&mut self) -> bool {
150 self.graph.remove_selected_node()
151 }
152
153 pub fn add_selected_edge(&mut self, id: EdgeId, shift: bool) {
154 self.graph.add_selected_edge(id, shift);
155 }
156 pub fn clear_selected_edge(&mut self) {
157 self.graph.clear_selected_edge();
158 }
159 pub fn remove_selected_edge(&mut self) -> bool {
160 self.graph.remove_selected_edge()
161 }
162
163 pub fn selection_bounds(&self) -> Option<Bounds<Pixels>> {
164 self.graph.selection_bounds()
165 }
166
167 pub fn selected_nodes_with_positions(&self) -> HashMap<NodeId, Point<Pixels>> {
168 self.graph.selected_nodes_with_positions()
169 }
170
171 pub fn hit_node(&self, mouse: Point<Pixels>) -> Option<NodeId> {
172 self.graph.hit_node(mouse)
173 }
174
175 pub fn bring_node_to_front(&mut self, node_id: NodeId) {
176 self.graph.bring_node_to_front(node_id);
177 }
178
179 pub fn world_to_screen(&self, p: Point<Pixels>) -> Point<Pixels> {
180 self.viewport.world_to_screen(p)
181 }
182
183 pub fn screen_to_world(&self, p: Point<Pixels>) -> Point<Pixels> {
184 self.viewport.screen_to_world(p)
185 }
186}