Skip to main content

geop_ops/ui/
form.rs

1//! [`Form`]: what an operation shows for a step — its dialog and the
2//! [`Visual`]s it draws — and, for each field, what setting it does.
3//!
4//! A field is described once: the control it shows and the setter its
5//! value goes to, side by side (see [`Form::number`], [`Form::reference`],
6//! ...). [`crate::Operation::set`] finds a field's setter by its key, so an
7//! operation never matches keys itself. A setter may capture what the form
8//! was built from — the part before the step — for what setting a field
9//! implies for others: another sketch picked brings its own axis, a
10//! selection picks the construction it fits.
11
12use geop_core_math::{primitives::CoordinateSystem, scalars::Scalar};
13
14use super::{
15    Action, Choice, Control, Dialog, ListItem, Number, Picked, Reference, Tone, Value, Visual,
16};
17use crate::operation::{EntityRef, Role};
18
19/// What a setter edits: the step's arguments, its session, and the keys of
20/// the visuals selected.
21pub struct Edit<'e, A, T> {
22    pub args: &'e mut A,
23    pub session: &'e mut T,
24    pub selection: &'e mut Vec<String>,
25}
26
27/// What setting a field does.
28type Setter<'a, A, T> = Box<dyn Fn(Edit<'_, A, T>, Value) + 'a>;
29
30/// What an operation shows for a step: its fields and what setting each
31/// does, and what it draws. `A` is its arguments, `T` its session; a form
32/// an editor reads (see [`Form::erase`]) has neither.
33pub struct Form<'a, S: Scalar, A = (), T = ()> {
34    pub dialog: Dialog<S>,
35    pub visuals: Vec<Visual<S>>,
36    /// A plane to work in, its `u`/`v` plane: the viewer faces it head on,
37    /// stops orbiting, and draws a grid on it. Draggable visuals are
38    /// dragged in it.
39    pub focus: Option<CoordinateSystem<S>>,
40    /// Whether the operation has a tool in hand — drawing a line, say — so
41    /// that clicks go to it rather than select.
42    pub tool: bool,
43    setters: Vec<(String, Setter<'a, A, T>)>,
44}
45
46impl<S: Scalar, A, T> Default for Form<'_, S, A, T> {
47    fn default() -> Self {
48        Self {
49            dialog: Dialog::new(),
50            visuals: Vec::new(),
51            focus: None,
52            tool: false,
53            setters: Vec::new(),
54        }
55    }
56}
57
58impl<'a, S: Scalar, A, T> Form<'a, S, A, T> {
59    pub fn new() -> Self {
60        Self::default()
61    }
62
63    /// What the field `key` does with `value`: its setter's. Nothing for a
64    /// key no field has — a field shown only to be read.
65    pub fn set(&self, key: &str, edit: Edit<'_, A, T>, value: Value) {
66        if let Some((_, setter)) = self.setters.iter().find(|(k, _)| k == key) {
67            setter(edit, value);
68        }
69    }
70
71    /// The form as an editor reads it: what it shows, without what setting
72    /// its fields does.
73    pub fn erase(self) -> Form<'a, S> {
74        Form {
75            dialog: self.dialog,
76            visuals: self.visuals,
77            focus: self.focus,
78            tool: self.tool,
79            setters: Vec::new(),
80        }
81    }
82
83    /// `setter` for the key `key`: for a control already in the dialog, or
84    /// one of its items — a list's entries each have their own key.
85    pub fn on(&mut self, key: impl Into<String>, setter: impl Fn(Edit<'_, A, T>, Value) + 'a) {
86        self.setters.push((key.into(), Box::new(setter)));
87    }
88
89    /// Appends `control` under `key`, set by `setter`.
90    fn field(
91        &mut self,
92        key: &str,
93        control: Control<S>,
94        setter: impl Fn(Edit<'_, A, T>, Value) + 'a,
95    ) -> &mut Self {
96        self.dialog.push(key, control);
97        self.on(key, setter);
98        self
99    }
100
101    pub fn heading(&mut self, key: &str, text: impl Into<String>) -> &mut Self {
102        self.dialog
103            .push(key, Control::Heading { text: text.into() });
104        self
105    }
106
107    pub fn text(&mut self, key: &str, text: impl Into<String>, tone: Tone) -> &mut Self {
108        self.dialog.push(
109            key,
110            Control::Text {
111                text: text.into(),
112                tone,
113            },
114        );
115        self
116    }
117
118    /// A list of entries, each with a key of its own for [`Form::on`].
119    pub fn list(&mut self, key: &str, items: Vec<ListItem>, empty: impl Into<String>) -> &mut Self {
120        self.dialog.push(
121            key,
122            Control::List {
123                items,
124                empty: empty.into(),
125            },
126        );
127        self
128    }
129
130    /// Things to do or choose; pressing one runs `run` with its value.
131    pub fn actions(
132        &mut self,
133        key: &str,
134        actions: Vec<Action>,
135        run: impl Fn(Edit<'_, A, T>, &str) + 'a,
136    ) -> &mut Self {
137        self.field(key, Control::Actions { actions }, move |edit, value| {
138            if let Value::Choice(choice) = value {
139                run(edit, &choice);
140            }
141        })
142    }
143
144    pub fn checkbox(
145        &mut self,
146        key: &str,
147        label: impl Into<String>,
148        value: bool,
149        set: impl Fn(&mut A, bool) + 'a,
150    ) -> &mut Self {
151        let control = Control::Checkbox {
152            label: label.into(),
153            value,
154        };
155        self.field(key, control, move |edit, value| {
156            if let Value::Bool(b) = value {
157                set(edit.args, b);
158            }
159        })
160    }
161
162    pub fn number(
163        &mut self,
164        key: &str,
165        number: Number<S>,
166        set: impl Fn(&mut A, f64) + 'a,
167    ) -> &mut Self {
168        self.field(key, Control::Number(number), move |edit, value| {
169            if let Value::Number(v) = value {
170                set(edit.args, v);
171            }
172        })
173    }
174
175    /// One of `options`, by value.
176    pub fn select(
177        &mut self,
178        key: &str,
179        label: impl Into<String>,
180        value: impl Into<String>,
181        options: Vec<Choice>,
182        set: impl Fn(&mut A, &str) + 'a,
183    ) -> &mut Self {
184        let control = Control::Select {
185            label: label.into(),
186            value: value.into(),
187            options,
188        };
189        self.field(key, control, move |edit, value| {
190            if let Value::Choice(choice) = value {
191                set(edit.args, &choice);
192            }
193        })
194    }
195
196    /// Entities that can fill one of `roles` — and, with a `scope`, are part
197    /// of it (see [`Reference`]); `set` gets what the field holds now.
198    #[allow(clippy::too_many_arguments)]
199    pub fn reference(
200        &mut self,
201        key: &str,
202        label: impl Into<String>,
203        value: Vec<EntityRef>,
204        roles: &[Role],
205        scope: Option<EntityRef>,
206        multiple: bool,
207        set: impl Fn(Edit<'_, A, T>, Vec<EntityRef>) + 'a,
208    ) -> &mut Self {
209        let control = Control::Reference(Reference {
210            label: label.into(),
211            roles: roles.to_vec(),
212            scope,
213            value: value
214                .into_iter()
215                .map(|entity| Picked {
216                    entity,
217                    detail: None,
218                    tone: Tone::Normal,
219                })
220                .collect(),
221            multiple,
222            armed: false,
223        });
224        self.field(key, control, move |edit, value| {
225            if let Value::Entities(entities) = value {
226                set(edit, entities);
227            }
228        })
229    }
230}