1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: MIT |
3 | use slint::{Model, ModelRc, SharedString}; |
4 | use std::cell::RefCell; |
5 | use std::collections::{HashMap, HashSet}; |
6 | use std::fmt::Debug; |
7 | use std::hash::Hash; |
8 | use std::rc::{Rc, Weak}; |
9 | |
10 | slint::slint!(import { MainWindow } from "cells.slint" ;); |
11 | |
12 | const ROW_COUNT: usize = 100; |
13 | const COL_COUNT: usize = 26; |
14 | |
15 | /// Graph of dependencies so that node B depends on node A. |
16 | /// So when node A is updated, node B also need to re updated. |
17 | #[derive (Default)] |
18 | struct DepGraph<A, B> { |
19 | dependent_index: HashMap<A, HashSet<B>>, |
20 | dependency_index: HashMap<B, HashSet<A>>, |
21 | } |
22 | |
23 | impl<A: Clone + Eq + Hash, B: Clone + Eq + Hash> DepGraph<A, B> { |
24 | pub fn add_dep(&mut self, a: A, b: B) { |
25 | self.dependent_index.entry(key:a.clone()).or_default().insert(b.clone()); |
26 | self.dependency_index.entry(key:b).or_default().insert(a); |
27 | } |
28 | |
29 | pub fn dependents<'a>(&'a self, a: &A) -> impl Iterator<Item = &B> + 'a { |
30 | self.dependent_index.get(a).into_iter().flat_map(|x: &HashSet| x.iter()) |
31 | } |
32 | |
33 | pub fn remove_dependencies(&mut self, b: &B) { |
34 | if let Some(h: HashSet) = self.dependency_index.remove(b) { |
35 | for a: A in h { |
36 | self.dependent_index.get_mut(&a).map(|x: &mut HashSet| x.remove(b)); |
37 | } |
38 | } |
39 | } |
40 | } |
41 | |
42 | #[derive (Debug, PartialEq)] |
43 | enum Expr { |
44 | Value(f32), |
45 | Cell(usize, usize), // (row, column) |
46 | Add(Box<(Expr, Expr)>), |
47 | Sub(Box<(Expr, Expr)>), |
48 | } |
49 | |
50 | fn parse_formula(formula: &str) -> Option<Expr> { |
51 | let formula = formula.trim(); |
52 | let alpha_n = formula.find(|c: char| !c.is_ascii_alphabetic()).unwrap_or(formula.len()); |
53 | let num_n = formula[alpha_n..] |
54 | .find(|c: char| !c.is_ascii_digit() && c != '.' ) |
55 | .map_or(formula.len(), |x| x + alpha_n); |
56 | |
57 | let e = if alpha_n > 0 { |
58 | if alpha_n != 1 { |
59 | return None; |
60 | }; |
61 | let col = formula.as_bytes()[0].to_ascii_lowercase() - b'a' ; |
62 | let row = formula[alpha_n..num_n].parse().ok()?; |
63 | Expr::Cell(row, col as usize) |
64 | } else if num_n > 0 { |
65 | Expr::Value(formula[alpha_n..num_n].parse().ok()?) |
66 | } else { |
67 | return None; |
68 | }; |
69 | |
70 | let rest = formula[num_n..].trim(); |
71 | |
72 | if rest.is_empty() { |
73 | Some(e) |
74 | } else if let Some(x) = rest.strip_prefix("+" ) { |
75 | Some(Expr::Add(Box::new((e, parse_formula(x)?)))) |
76 | } else if let Some(x) = rest.strip_prefix("-" ) { |
77 | Some(Expr::Sub(Box::new((e, parse_formula(x)?)))) |
78 | } else { |
79 | None |
80 | } |
81 | } |
82 | |
83 | #[test ] |
84 | fn test_parse_formula() { |
85 | assert_eq!(parse_formula("42" ), Some(Expr::Value(42.0))); |
86 | assert_eq!(parse_formula(" 49.5 " ), Some(Expr::Value(49.5))); |
87 | assert_eq!(parse_formula("B4" ), Some(Expr::Cell(4, 1))); |
88 | assert_eq!(parse_formula("B 4" ), None); |
89 | assert_eq!(parse_formula("B4.2" ), None); |
90 | assert_eq!(parse_formula("AB5" ), None); |
91 | assert_eq!(parse_formula("4B" ), None); |
92 | assert_eq!( |
93 | parse_formula("8 + C6" ), |
94 | Some(Expr::Add(Box::new((Expr::Value(8.), Expr::Cell(6, 2))))) |
95 | ); |
96 | assert_eq!( |
97 | parse_formula(" a9-b2 " ), |
98 | Some(Expr::Sub(Box::new((Expr::Cell(9, 0), Expr::Cell(2, 1))))) |
99 | ); |
100 | assert_eq!( |
101 | parse_formula("D22+B12+85.5" ), |
102 | Some(Expr::Add(Box::new(( |
103 | Expr::Cell(22, 3), |
104 | Expr::Add(Box::new((Expr::Cell(12, 1), Expr::Value(85.5)))) |
105 | )))) |
106 | ); |
107 | } |
108 | |
109 | struct RowModel { |
110 | row: usize, |
111 | row_elements: RefCell<Vec<CellContent>>, |
112 | base_model: Weak<CellsModel>, |
113 | notify: slint::ModelNotify, |
114 | } |
115 | |
116 | impl slint::Model for RowModel { |
117 | type Data = CellContent; |
118 | |
119 | fn row_count(&self) -> usize { |
120 | self.row_elements.borrow().len() |
121 | } |
122 | |
123 | fn row_data(&self, row: usize) -> Option<Self::Data> { |
124 | self.row_elements.borrow().get(index:row).cloned() |
125 | } |
126 | |
127 | fn model_tracker(&self) -> &dyn slint::ModelTracker { |
128 | &self.notify |
129 | } |
130 | |
131 | fn set_row_data(&self, index: usize, data: CellContent) { |
132 | if let Some(cells: Rc) = self.base_model.upgrade() { |
133 | cells.update_cell(self.row, col:index, new_formula:Some(data.formula)); |
134 | } |
135 | } |
136 | } |
137 | |
138 | struct CellsModel { |
139 | rows: Vec<Rc<RowModel>>, |
140 | dep_graph: RefCell<DepGraph<(usize, usize), (usize, usize)>>, |
141 | } |
142 | |
143 | impl CellsModel { |
144 | fn new() -> Rc<Self> { |
145 | Rc::new_cyclic(|w| Self { |
146 | rows: (0..ROW_COUNT) |
147 | .map(|row| { |
148 | Rc::new(RowModel { |
149 | row, |
150 | row_elements: vec![CellContent::default(); COL_COUNT].into(), |
151 | base_model: w.clone(), |
152 | notify: Default::default(), |
153 | }) |
154 | }) |
155 | .collect(), |
156 | dep_graph: Default::default(), |
157 | }) |
158 | } |
159 | |
160 | fn get_cell_value(&self, row: usize, col: usize) -> Option<f32> { |
161 | self.rows.get(row)?.row_elements.borrow().get(col)?.value.into() |
162 | } |
163 | |
164 | /// Update a cell to a new formula, or re-evaluate the current formula of that cell |
165 | fn update_cell(&self, row: usize, col: usize, new_formula: Option<SharedString>) -> Option<()> { |
166 | let r_model = self.rows.get(row)?; |
167 | let mut r = r_model.row_elements.borrow_mut(); |
168 | let data = r.get_mut(col)?; |
169 | let new_form = new_formula.is_some(); |
170 | if let Some(new_formula) = new_formula { |
171 | data.formula = new_formula; |
172 | }; |
173 | let exp = parse_formula(&data.formula).unwrap_or(Expr::Value(0.)); |
174 | |
175 | drop(r); |
176 | self.dep_graph.borrow_mut().remove_dependencies(&(row, col)); |
177 | let new = self.eval(&exp); |
178 | let mut r = r_model.row_elements.borrow_mut(); |
179 | let data = r.get_mut(col)?; |
180 | if data.value != new { |
181 | data.value = new; |
182 | drop(r); |
183 | r_model.notify.row_changed(col); |
184 | let deps = self.dep_graph.borrow().dependents(&(row, col)).cloned().collect::<Vec<_>>(); |
185 | for (r, c) in deps { |
186 | self.update_cell(r, c, None); |
187 | } |
188 | } else if new_form { |
189 | drop(r); |
190 | r_model.notify.row_changed(col); |
191 | } |
192 | |
193 | make_deps(&mut *self.dep_graph.borrow_mut(), (row, col), &exp); |
194 | |
195 | Some(()) |
196 | } |
197 | |
198 | /// Evaluate an expression recursively |
199 | fn eval(&self, exp: &Expr) -> f32 { |
200 | match exp { |
201 | Expr::Value(x) => *x, |
202 | Expr::Cell(row, col) => self.get_cell_value(*row, *col).unwrap_or(0.), |
203 | Expr::Add(x) => self.eval(&x.0) + self.eval(&x.1), |
204 | Expr::Sub(x) => self.eval(&x.0) - self.eval(&x.1), |
205 | } |
206 | } |
207 | } |
208 | |
209 | /// Traverse a given expression to register the dependencies |
210 | fn make_deps( |
211 | graph: &mut DepGraph<(usize, usize), (usize, usize)>, |
212 | orig: (usize, usize), |
213 | exp: &Expr, |
214 | ) { |
215 | match exp { |
216 | Expr::Value(_) => {} |
217 | Expr::Cell(row: &usize, col: &usize) => graph.add_dep((*row, *col), b:orig), |
218 | Expr::Add(x: &Box<(Expr, Expr)>) | Expr::Sub(x: &Box<(Expr, Expr)>) => { |
219 | make_deps(graph, orig, &x.0); |
220 | make_deps(graph, orig, &x.1) |
221 | } |
222 | } |
223 | } |
224 | |
225 | impl Model for CellsModel { |
226 | type Data = ModelRc<CellContent>; |
227 | |
228 | fn row_count(&self) -> usize { |
229 | ROW_COUNT |
230 | } |
231 | |
232 | fn row_data(&self, row: usize) -> Option<Self::Data> { |
233 | self.rows.get(index:row).map(|x: &Rc| x.clone().into()) |
234 | } |
235 | |
236 | fn model_tracker(&self) -> &dyn slint::ModelTracker { |
237 | &() |
238 | } |
239 | } |
240 | |
241 | pub fn main() { |
242 | let main_window = MainWindow::new().unwrap(); |
243 | let cells_model: Rc = CellsModel::new(); |
244 | main_window.set_cells(ModelRc::from(cells_model)); |
245 | main_window.run().unwrap(); |
246 | } |
247 | |