1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4use slint::Model;
5use slint::VecModel;
6use std::cell::RefCell;
7use std::rc::Rc;
8
9slint::slint!(import { MainWindow } from "circledraw.slint";);
10
11enum Change {
12 CircleAdded { row: usize },
13 CircleRemoved { row: usize, circle: Circle },
14 CircleResized { row: usize, old_d: f32 },
15}
16
17struct 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
24impl<F> UndoStack<F>
25where
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
63pub 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