1use 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
19pub 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
27type Setter<'a, A, T> = Box<dyn Fn(Edit<'_, A, T>, Value) + 'a>;
29
30pub struct Form<'a, S: Scalar, A = (), T = ()> {
34 pub dialog: Dialog<S>,
35 pub visuals: Vec<Visual<S>>,
36 pub focus: Option<CoordinateSystem<S>>,
40 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 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 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 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 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 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 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 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 #[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}