1use geop_core_program::{numeric::NumericInput, reader::Reader};
14
15use crate::{
16 editing::{
17 Action, CanvasEvent, Choice, Event, FileEntry, Form, Layout, Number, Table, TableEdit,
18 Value,
19 },
20 selectable::{Selectable, selected},
21};
22
23pub struct Edit<'e, A, T> {
26 pub args: &'e mut A,
27 pub session: &'e mut T,
28 pub selection: &'e mut Vec<String>,
29}
30
31impl<A, T> Edit<'_, A, T> {
32 pub fn again(&mut self) -> Edit<'_, A, T> {
34 Edit {
35 args: &mut *self.args,
36 session: &mut *self.session,
37 selection: &mut *self.selection,
38 }
39 }
40}
41
42#[derive(Clone, Debug, Default)]
46pub struct FormState<T> {
47 pub session: T,
48 pub selection: Vec<String>,
49}
50
51impl<T: Clone> FormState<T> {
52 pub fn handle<'f, A: Clone>(
57 &self,
58 args: &A,
59 event: Event,
60 form: impl FnOnce(&A, &Self) -> EditForm<'f, A, T>,
61 canvas: impl FnOnce(Edit<'_, A, T>, &CanvasEvent),
62 ) -> (A, Self) {
63 let mut edited = args.clone();
64 let mut state = self.clone();
65 let edit = Edit {
66 args: &mut edited,
67 session: &mut state.session,
68 selection: &mut state.selection,
69 };
70 match event {
71 Event::Field { key, value } => form(args, self).set(&key, edit, value),
72 Event::Select(keys) => *edit.selection = keys,
73 Event::Canvas(event) => canvas(edit, &event),
74 }
75 (edited, state)
76 }
77}
78
79type Setter<'a, A, T> = Box<dyn Fn(Edit<'_, A, T>, Value) + 'a>;
81
82pub struct EditForm<'a, A, T = ()> {
85 pub form: Form,
86 setters: Vec<(String, Setter<'a, A, T>)>,
87}
88
89impl<A, T> Default for EditForm<'_, A, T> {
90 fn default() -> Self {
91 Self {
92 form: Form::default(),
93 setters: Vec::new(),
94 }
95 }
96}
97
98impl<'a, A, T> EditForm<'a, A, T> {
99 pub fn into_form(self) -> Form {
101 self.form
102 }
103
104 pub fn set(&self, key: &str, edit: Edit<'_, A, T>, value: Value) {
107 if let Some((_, setter)) = self.setters.iter().find(|(k, _)| k == key) {
108 setter(edit, value);
109 }
110 }
111
112 pub fn on(&mut self, key: impl Into<String>, setter: impl Fn(Edit<'_, A, T>, Value) + 'a) {
115 self.setters.push((key.into(), Box::new(setter)));
116 }
117
118 pub fn layout(&mut self, layout: Layout) -> &mut Self {
120 self.form.layout = layout;
121 self
122 }
123
124 pub fn input(
127 &mut self,
128 key: &str,
129 label: impl Into<String>,
130 value: impl Into<String>,
131 placeholder: Option<&str>,
132 set: impl Fn(Edit<'_, A, T>, &str) + 'a,
133 ) -> &mut Self {
134 self.form.dialog.input(key, label, value, placeholder);
135 self.on(key, move |edit, value| {
136 if let Value::Text(text) = value {
137 set(edit, &text);
138 }
139 });
140 self
141 }
142
143 pub fn table(
149 &mut self,
150 key: &str,
151 table: Table,
152 edit: impl Fn(Edit<'_, A, T>, TableEdit) + 'a,
153 ) -> &mut Self {
154 let (rows, columns) = (table.rows.len(), table.columns.len());
155 self.form.dialog.table(key, table);
156 self.on(key, move |mut e, value| {
157 let Value::Table(what) = value else {
158 return;
159 };
160 let TableEdit::Paste { row, column, cells } = what else {
161 return edit(e, what);
162 };
163 let mut rows = rows;
164 for (r, line) in cells.into_iter().enumerate() {
165 let row = row + r;
166 while row >= rows {
167 edit(e.again(), TableEdit::AddRow);
168 rows += 1;
169 }
170 for (c, text) in line.into_iter().enumerate() {
171 let column = column + c;
172 if column < columns {
173 edit(e.again(), TableEdit::Cell { row, column, text });
174 }
175 }
176 }
177 });
178 self
179 }
180
181 pub fn actions(
183 &mut self,
184 key: &str,
185 actions: Vec<Action>,
186 run: impl Fn(Edit<'_, A, T>, &str) + 'a,
187 ) -> &mut Self {
188 self.form.dialog.actions(key, actions);
189 self.on(key, move |edit, value| {
190 if let Value::Choice(choice) = value {
191 run(edit, &choice);
192 }
193 });
194 self
195 }
196
197 pub fn checkbox(
198 &mut self,
199 key: &str,
200 label: impl Into<String>,
201 value: bool,
202 set: impl Fn(Edit<'_, A, T>, bool) + 'a,
203 ) -> &mut Self {
204 self.form.dialog.checkbox(key, label, value);
205 self.on(key, move |edit, value| {
206 if let Value::Bool(b) = value {
207 set(edit, b);
208 }
209 });
210 self
211 }
212
213 pub fn number(
214 &mut self,
215 key: &str,
216 number: Number,
217 set: impl Fn(Edit<'_, A, T>, f64) + 'a,
218 ) -> &mut Self {
219 self.form.dialog.number(key, number);
220 self.on(key, move |edit, value| {
221 if let Value::Number(v) = value {
222 set(edit, v);
223 }
224 });
225 self
226 }
227
228 pub fn numeric(
234 &mut self,
235 key: &str,
236 number: Number,
237 set: impl Fn(Edit<'_, A, T>, NumericInput) + 'a,
238 ) -> &mut Self {
239 self.form.dialog.number(key, number);
240 self.on(key, move |edit, value| {
241 if let Some(input) = value.numeric_input() {
242 set(edit, input);
243 }
244 });
245 self
246 }
247
248 pub fn select(
250 &mut self,
251 key: &str,
252 label: impl Into<String>,
253 value: impl Into<String>,
254 options: Vec<Choice>,
255 searchable: bool,
256 set: impl Fn(Edit<'_, A, T>, &str) + 'a,
257 ) -> &mut Self {
258 (self.form.dialog).select(key, label, value, options, searchable);
259 self.on(key, move |edit, value| {
260 if let Value::Choice(choice) = value {
261 set(edit, &choice);
262 }
263 });
264 self
265 }
266
267 pub fn file(
270 &mut self,
271 key: &str,
272 label: impl Into<String>,
273 value: impl Into<String>,
274 files: Vec<FileEntry>,
275 upload: &[&str],
276 set: impl Fn(Edit<'_, A, T>, &str) + 'a,
277 ) -> &mut Self {
278 self.form.dialog.file(key, label, value, files, upload);
279 self.on(key, move |edit, value| {
280 if let Value::Choice(choice) = value {
281 set(edit, &choice);
282 }
283 });
284 self
285 }
286
287 pub fn color(
289 &mut self,
290 key: &str,
291 label: impl Into<String>,
292 value: impl Into<String>,
293 set: impl Fn(Edit<'_, A, T>, &str) + 'a,
294 ) -> &mut Self {
295 self.form.dialog.color(key, label, value);
296 self.on(key, move |edit, value| {
297 if let Value::Text(color) = value {
298 set(edit, &color);
299 }
300 });
301 self
302 }
303
304 pub fn pick<P: Selectable>(
309 &mut self,
310 key: &str,
311 label: impl Into<String>,
312 reader: &Reader<'a>,
313 value: &[P],
314 multiple: bool,
315 set: impl Fn(Edit<'_, A, T>, Vec<P>) + 'a,
316 ) -> &mut Self {
317 (self.form.dialog).pick(key, label, reader, value, multiple);
318 let reader = reader.clone();
319 self.on(key, move |edit, value| {
320 if let Value::Elements(picks) = value {
321 set(edit, selected(&reader, &picks));
322 }
323 });
324 self
325 }
326
327 pub fn pick_one<P: Selectable>(
331 &mut self,
332 key: &str,
333 label: impl Into<String>,
334 reader: &Reader<'a>,
335 value: Option<&P>,
336 set: impl Fn(Edit<'_, A, T>, P) + 'a,
337 ) -> &mut Self {
338 let value = value.map(std::slice::from_ref).unwrap_or_default();
339 self.pick(key, label, reader, value, false, move |edit, mut picked| {
340 if let Some(one) = picked.pop() {
341 set(edit, one);
342 }
343 })
344 }
345}
346
347pub fn choices<T: PartialEq>(options: &[(T, &str, &str)], chosen: &T) -> (String, Vec<Choice>) {
350 let value = (options.iter().find(|o| o.0 == *chosen)).map_or("", |o| o.1);
351 let choices = (options.iter()).map(|&(_, v, l)| Choice::new(v, l));
352 (value.to_string(), choices.collect())
353}
354
355pub fn chosen<T: Copy>(options: &[(T, &str, &str)], value: &str) -> Option<T> {
358 options.iter().find(|o| o.1 == value).map(|o| o.0)
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364 use crate::editing::{Cell, Column, ColumnKind, TableRow};
365
366 #[test]
370 fn a_paste_is_taken_apart_into_rows_added_and_cells_set() {
371 let columns = vec![
372 Column::new("a", ColumnKind::Text),
373 Column::new("b", ColumnKind::Text),
374 ];
375 let rows = vec![TableRow::new(vec![Cell::new(""), Cell::new("")])];
376 let table = Table::new(columns, rows, "");
377 let mut form = EditForm::<Vec<TableEdit>>::default();
378 form.table("t", table, |edit, what| edit.args.push(what));
379 let mut seen = Vec::new();
380 let mut state = FormState::<()>::default();
381 let edit = Edit {
382 args: &mut seen,
383 session: &mut state.session,
384 selection: &mut state.selection,
385 };
386 let paste = TableEdit::Paste {
387 row: 0,
388 column: 1,
389 cells: vec![
390 vec!["x".into(), "over".into()],
391 vec!["y".into()],
392 vec!["z".into()],
393 ],
394 };
395 form.set("t", edit, Value::Table(paste));
396 let cell = |row, column, text: &str| TableEdit::Cell {
397 row,
398 column,
399 text: text.into(),
400 };
401 assert_eq!(
402 seen,
403 [
404 cell(0, 1, "x"),
405 TableEdit::AddRow,
406 cell(1, 1, "y"),
407 TableEdit::AddRow,
408 cell(2, 1, "z"),
409 ]
410 );
411 }
412}