1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: MIT |
3 | |
4 | use slint::Model; |
5 | use slint::VecModel; |
6 | use std::cell::RefCell; |
7 | use std::rc::Rc; |
8 | |
9 | slint::slint!(import { MainWindow } from "circledraw.slint" ;); |
10 | |
11 | enum Change { |
12 | CircleAdded { row: usize }, |
13 | CircleRemoved { row: usize, circle: Circle }, |
14 | CircleResized { row: usize, old_d: f32 }, |
15 | } |
16 | |
17 | struct UndoStack<F> { |
18 | stack: Vec<Option<Change>>, |
19 | // Everything at and after this is a redo action |
20 | redo_offset: usize, |
21 | undo2redo: F, |
22 | } |
23 | |
24 | impl<F> UndoStack<F> |
25 | where |
26 | F: Fn(Change) -> Change, |
27 | { |
28 | fn new(undo2redo: F) -> Self { |
29 | Self { stack: Vec::new(), redo_offset: 0, undo2redo } |
30 | } |
31 | |
32 | fn push(&mut self, change: Change) { |
33 | self.stack.truncate(self.redo_offset); |
34 | self.stack.push(Some(change)); |
35 | self.redo_offset += 1; |
36 | } |
37 | |
38 | fn undoable(&self) -> bool { |
39 | self.redo_offset > 0 |
40 | } |
41 | |
42 | fn redoable(&self) -> bool { |
43 | self.redo_offset < self.stack.len() |
44 | } |
45 | |
46 | fn undo(&mut self) { |
47 | self.redo_offset -= 1; |
48 | |
49 | let undo = self.stack.get_mut(self.redo_offset).unwrap().take().unwrap(); |
50 | let redo = (self.undo2redo)(undo); |
51 | self.stack[self.redo_offset] = Some(redo); |
52 | } |
53 | |
54 | fn redo(&mut self) { |
55 | let redo = self.stack.get_mut(self.redo_offset).unwrap().take().unwrap(); |
56 | let undo = (self.undo2redo)(redo); |
57 | self.stack[self.redo_offset] = Some(undo); |
58 | |
59 | self.redo_offset += 1; |
60 | } |
61 | } |
62 | |
63 | pub fn main() { |
64 | let main_window = MainWindow::new().unwrap(); |
65 | |
66 | let model = Rc::new(VecModel::default()); |
67 | main_window.set_model(model.clone().into()); |
68 | |
69 | let undo_stack; |
70 | { |
71 | let model = model.clone(); |
72 | undo_stack = Rc::new(RefCell::new(UndoStack::new(move |change| match change { |
73 | Change::CircleAdded { row } => { |
74 | let circle = model.row_data(row).unwrap(); |
75 | model.remove(row); |
76 | Change::CircleRemoved { row, circle } |
77 | } |
78 | Change::CircleRemoved { row, circle } => { |
79 | model.insert(row, circle); |
80 | Change::CircleAdded { row } |
81 | } |
82 | Change::CircleResized { row, old_d } => { |
83 | let mut circle = model.row_data(row).unwrap(); |
84 | let d = circle.d; |
85 | circle.d = old_d; |
86 | model.set_row_data(row, circle); |
87 | Change::CircleResized { row, old_d: d } |
88 | } |
89 | }))); |
90 | } |
91 | |
92 | { |
93 | let model = model.clone(); |
94 | let undo_stack = undo_stack.clone(); |
95 | let window_weak = main_window.as_weak(); |
96 | main_window.on_background_clicked(move |x, y| { |
97 | let mut undo_stack = undo_stack.borrow_mut(); |
98 | let main_window = window_weak.unwrap(); |
99 | |
100 | model.push(Circle { x: x as f32, y: y as f32, d: 30.0 }); |
101 | undo_stack.push(Change::CircleAdded { row: model.row_count() - 1 }); |
102 | |
103 | main_window.set_undoable(undo_stack.undoable()); |
104 | main_window.set_redoable(undo_stack.redoable()); |
105 | }); |
106 | } |
107 | |
108 | { |
109 | let undo_stack = undo_stack.clone(); |
110 | let window_weak = main_window.as_weak(); |
111 | main_window.on_undo_clicked(move || { |
112 | let mut undo_stack = undo_stack.borrow_mut(); |
113 | let main_window = window_weak.unwrap(); |
114 | undo_stack.undo(); |
115 | main_window.set_undoable(undo_stack.undoable()); |
116 | main_window.set_redoable(undo_stack.redoable()); |
117 | }); |
118 | } |
119 | |
120 | { |
121 | let undo_stack = undo_stack.clone(); |
122 | let window_weak = main_window.as_weak(); |
123 | main_window.on_redo_clicked(move || { |
124 | let mut undo_stack = undo_stack.borrow_mut(); |
125 | let main_window = window_weak.unwrap(); |
126 | undo_stack.redo(); |
127 | main_window.set_undoable(undo_stack.undoable()); |
128 | main_window.set_redoable(undo_stack.redoable()); |
129 | }); |
130 | } |
131 | |
132 | { |
133 | let model = model.clone(); |
134 | let undo_stack = undo_stack.clone(); |
135 | let window_weak = main_window.as_weak(); |
136 | main_window.on_circle_resized(move |row, diameter| { |
137 | let row = row as usize; |
138 | let mut undo_stack = undo_stack.borrow_mut(); |
139 | let main_window = window_weak.unwrap(); |
140 | |
141 | let mut circle = model.row_data(row).unwrap(); |
142 | let old_d = circle.d; |
143 | circle.d = diameter; |
144 | model.set_row_data(row, circle); |
145 | undo_stack.push(Change::CircleResized { row, old_d }); |
146 | |
147 | main_window.set_undoable(undo_stack.undoable()); |
148 | main_window.set_redoable(undo_stack.redoable()); |
149 | }); |
150 | } |
151 | |
152 | main_window.run().unwrap(); |
153 | } |
154 | |