1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3use slint::{Model, ModelRc, SharedString};
4use std::cell::RefCell;
5use std::collections::{HashMap, HashSet};
6use std::fmt::Debug;
7use std::hash::Hash;
8use std::rc::{Rc, Weak};
9
10slint::slint!(import { MainWindow } from "cells.slint";);
11
12const ROW_COUNT: usize = 100;
13const 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)]
18struct DepGraph<A, B> {
19 dependent_index: HashMap<A, HashSet<B>>,
20 dependency_index: HashMap<B, HashSet<A>>,
21}
22
23impl<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)]
43enum Expr {
44 Value(f32),
45 Cell(usize, usize), // (row, column)
46 Add(Box<(Expr, Expr)>),
47 Sub(Box<(Expr, Expr)>),
48}
49
50fn 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]
84fn 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
109struct RowModel {
110 row: usize,
111 row_elements: RefCell<Vec<CellContent>>,
112 base_model: Weak<CellsModel>,
113 notify: slint::ModelNotify,
114}
115
116impl 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
138struct CellsModel {
139 rows: Vec<Rc<RowModel>>,
140 dep_graph: RefCell<DepGraph<(usize, usize), (usize, usize)>>,
141}
142
143impl 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
210fn 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
225impl 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
241pub 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