Skip to main content

ferrum_flow/canvas/
undo.rs

1use std::collections::HashMap;
2
3use gpui::{Bounds, Pixels, Point};
4
5use crate::{
6    Edge, EdgeBuilder, EdgeId, Graph, GraphOp, Node, NodeBuilder, NodeId, Port, PortId,
7    RendererRegistry, Viewport,
8    canvas::PortLayoutCache,
9    plugin::{
10        cache_all_node_port_offset, cache_node_port_offset, is_edge_visible, is_node_visible,
11        port_offset_cached,
12    },
13};
14
15pub trait Command {
16    fn name(&self) -> &'static str;
17
18    /// execute command detail, e.g: move node
19    fn execute(&mut self, ctx: &mut CommandContext);
20
21    // undo command , when open sync plugin, this is diabeled.
22    fn undo(&mut self, ctx: &mut CommandContext);
23
24    /// used by sync plugin
25    /// when open sync plugin, execute method is diasbeld, and using to_ops send graph intent
26    fn to_ops(&self, ctx: &mut CommandContext) -> Vec<GraphOp>;
27}
28
29pub trait HistoryProvider {
30    fn undo(&mut self, ctx: &mut CommandContext);
31    fn redo(&mut self, ctx: &mut CommandContext);
32    fn push(&mut self, command: Box<dyn Command>, ctx: &mut CommandContext);
33    fn clear(&mut self);
34}
35
36pub struct CommandContext<'a> {
37    pub graph: &'a mut Graph,
38    pub port_offset_cache: &'a mut PortLayoutCache,
39    pub viewport: &'a mut Viewport,
40    pub renderers: &'a mut RendererRegistry,
41    pub(crate) notify: &'a mut dyn FnMut(),
42}
43const MAX_HISTORY: usize = 100;
44pub struct LocalHistory {
45    undo_stack: Vec<Box<dyn Command>>,
46    redo_stack: Vec<Box<dyn Command>>,
47}
48
49impl LocalHistory {
50    pub fn new() -> Self {
51        Self {
52            undo_stack: vec![],
53            redo_stack: vec![],
54        }
55    }
56}
57
58impl HistoryProvider for LocalHistory {
59    fn push(&mut self, mut command: Box<dyn Command>, ctx: &mut CommandContext) {
60        command.execute(ctx);
61
62        self.undo_stack.push(command);
63
64        self.redo_stack.clear();
65
66        if self.undo_stack.len() > MAX_HISTORY {
67            self.undo_stack.remove(0);
68        }
69    }
70    fn undo(&mut self, ctx: &mut CommandContext) {
71        if let Some(mut cmd) = self.undo_stack.pop() {
72            cmd.undo(ctx);
73            self.redo_stack.push(cmd);
74        }
75    }
76
77    fn redo(&mut self, ctx: &mut CommandContext) {
78        if let Some(mut cmd) = self.redo_stack.pop() {
79            cmd.execute(ctx);
80            self.undo_stack.push(cmd);
81        }
82    }
83
84    fn clear(&mut self) {
85        self.undo_stack.clear();
86        self.redo_stack.clear();
87    }
88}
89
90pub struct CompositeCommand {
91    commands: Vec<Box<dyn Command>>,
92}
93
94impl CompositeCommand {
95    pub fn new() -> Self {
96        Self {
97            commands: Vec::new(),
98        }
99    }
100    pub fn push(&mut self, command: impl Command + 'static) {
101        self.commands.push(Box::new(command));
102    }
103}
104
105impl Command for CompositeCommand {
106    fn name(&self) -> &'static str {
107        "composite"
108    }
109    fn execute(&mut self, state: &mut CommandContext) {
110        for cmd in &mut self.commands {
111            cmd.execute(state);
112        }
113    }
114
115    fn undo(&mut self, state: &mut CommandContext) {
116        for cmd in self.commands.iter_mut().rev() {
117            cmd.undo(state);
118        }
119    }
120    fn to_ops(&self, ctx: &mut CommandContext) -> Vec<GraphOp> {
121        let mut list = vec![];
122        for cmd in &self.commands {
123            list.extend(cmd.to_ops(ctx));
124        }
125
126        vec![GraphOp::Batch(list)]
127    }
128}
129
130impl<'a> CommandContext<'a> {
131    pub fn create_node(&self, node_type: &str) -> NodeBuilder {
132        self.graph.create_node(node_type)
133    }
134
135    pub fn create_edge(&self) -> EdgeBuilder {
136        self.graph.create_dege()
137    }
138
139    pub fn next_node_id(&self) -> NodeId {
140        self.graph.next_node_id()
141    }
142
143    pub fn next_port_id(&self) -> PortId {
144        self.graph.next_port_id()
145    }
146
147    pub fn next_edge_id(&self) -> EdgeId {
148        self.graph.next_edge_id()
149    }
150    pub fn add_node(&mut self, node: Node) {
151        self.graph.add_node(node);
152    }
153
154    pub fn add_port(&mut self, port: Port) {
155        self.graph.add_port(port);
156    }
157
158    pub fn remove_port(&mut self, id: &PortId) {
159        self.graph.remove_port(id);
160    }
161
162    pub fn get_node(&self, id: &NodeId) -> Option<&Node> {
163        self.graph.get_node(id)
164    }
165
166    pub fn get_node_mut(&mut self, id: &NodeId) -> Option<&mut Node> {
167        self.graph.get_node_mut(id)
168    }
169    pub fn remove_node(&mut self, id: &NodeId) {
170        self.graph.remove_node(id);
171        self.port_offset_cache.clear_node(id);
172    }
173    pub fn nodes(&self) -> &HashMap<NodeId, Node> {
174        self.graph.nodes()
175    }
176    pub fn node_order(&self) -> &Vec<NodeId> {
177        self.graph.node_order()
178    }
179
180    pub fn new_edge(&self) -> Edge {
181        self.graph.new_edge()
182    }
183
184    pub fn add_edge(&mut self, edge: Edge) {
185        self.graph.add_edge(edge);
186    }
187
188    pub fn remove_edge(&mut self, edge_id: EdgeId) {
189        self.graph.remove_edge(edge_id);
190    }
191
192    pub fn add_selected_node(&mut self, id: NodeId, shift: bool) {
193        self.graph.add_selected_node(id, shift);
194    }
195    pub fn clear_selected_node(&mut self) {
196        self.graph.clear_selected_node();
197    }
198    pub fn remove_selected_node(&mut self) -> bool {
199        self.graph.remove_selected_node()
200    }
201
202    pub fn add_selected_edge(&mut self, id: EdgeId, shift: bool) {
203        self.graph.add_selected_edge(id, shift);
204    }
205    pub fn clear_selected_edge(&mut self) {
206        self.graph.clear_selected_edge();
207    }
208    pub fn remove_selected_edge(&mut self) -> bool {
209        self.graph.remove_selected_edge()
210    }
211
212    pub fn selection_bounds(&self) -> Option<Bounds<Pixels>> {
213        self.graph.selection_bounds()
214    }
215
216    pub fn selected_nodes_with_positions(&self) -> HashMap<NodeId, Point<Pixels>> {
217        self.graph.selected_nodes_with_positions()
218    }
219
220    pub fn hit_node(&self, mouse: Point<Pixels>) -> Option<NodeId> {
221        self.graph.hit_node(mouse)
222    }
223
224    pub fn bring_node_to_front(&mut self, node_id: NodeId) {
225        self.graph.bring_node_to_front(node_id);
226    }
227
228    pub fn world_to_screen(&self, p: Point<Pixels>) -> Point<Pixels> {
229        self.viewport.world_to_screen(p)
230    }
231
232    pub fn screen_to_world(&self, p: Point<Pixels>) -> Point<Pixels> {
233        self.viewport.screen_to_world(p)
234    }
235
236    pub fn is_node_visible(&self, node_id: &NodeId) -> bool {
237        is_node_visible(self.graph, self.viewport, node_id)
238    }
239
240    pub fn is_edge_visible(&self, edge: &Edge) -> bool {
241        is_edge_visible(self.graph, self.viewport, edge)
242    }
243
244    pub fn port_offset_cached(&self, node_id: &NodeId, port_id: &PortId) -> Option<Point<Pixels>> {
245        port_offset_cached(self.port_offset_cache, node_id, port_id)
246    }
247
248    pub fn cache_all_node_port_offset(&mut self) {
249        cache_all_node_port_offset(self.graph, self.renderers, self.port_offset_cache)
250    }
251
252    pub fn cache_node_port_offset(&mut self, node_id: &NodeId) {
253        cache_node_port_offset(self.graph, self.renderers, self.port_offset_cache, node_id);
254    }
255
256    pub fn notify(&mut self) {
257        (self.notify)();
258    }
259}