1use crate::{
22 core::{
23 num_traits, pool::Handle, reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*,
24 },
25 define_constructor,
26 grid::{Column, GridBuilder, Row},
27 message::{MessageDirection, UiMessage},
28 numeric::{NumericType, NumericUpDownBuilder, NumericUpDownMessage},
29 widget::WidgetBuilder,
30 BuildContext, Control, Thickness, UiNode, UserInterface, Widget,
31};
32use fyrox_core::algebra::SMatrix;
33use std::ops::{Deref, DerefMut};
34
35fn make_numeric_input<T: NumericType>(
36 ctx: &mut BuildContext,
37 column: usize,
38 row: usize,
39 value: T,
40 min: T,
41 max: T,
42 step: T,
43 editable: bool,
44 precision: usize,
45) -> Handle<UiNode> {
46 NumericUpDownBuilder::new(
47 WidgetBuilder::new()
48 .on_row(row)
49 .on_column(column)
50 .with_margin(Thickness {
51 left: 1.0,
52 top: 0.0,
53 right: 1.0,
54 bottom: 0.0,
55 }),
56 )
57 .with_precision(precision)
58 .with_value(value)
59 .with_min_value(min)
60 .with_max_value(max)
61 .with_step(step)
62 .with_editable(editable)
63 .build(ctx)
64}
65
66#[derive(Debug, Clone, PartialEq)]
67pub enum MatrixEditorMessage<const R: usize, const C: usize, T>
68where
69 T: NumericType,
70{
71 Value(SMatrix<T, R, C>),
72}
73
74impl<const R: usize, const C: usize, T> MatrixEditorMessage<R, C, T>
75where
76 T: NumericType,
77{
78 define_constructor!(MatrixEditorMessage:Value => fn value(SMatrix<T, R, C>), layout: false);
79}
80
81#[derive(Clone, Visit, Reflect, Debug, ComponentProvider)]
82pub struct MatrixEditor<const R: usize, const C: usize, T>
83where
84 T: NumericType,
85{
86 pub widget: Widget,
87 pub fields: Vec<Handle<UiNode>>,
88 #[reflect(hidden)]
89 #[visit(skip)]
90 pub value: SMatrix<T, R, C>,
91 #[reflect(hidden)]
92 #[visit(skip)]
93 pub min: SMatrix<T, R, C>,
94 #[reflect(hidden)]
95 #[visit(skip)]
96 pub max: SMatrix<T, R, C>,
97 #[reflect(hidden)]
98 #[visit(skip)]
99 pub step: SMatrix<T, R, C>,
100}
101
102impl<const R: usize, const C: usize, T> Default for MatrixEditor<R, C, T>
103where
104 T: NumericType,
105{
106 fn default() -> Self {
107 Self {
108 widget: Default::default(),
109 fields: Default::default(),
110 value: SMatrix::repeat(T::zero()),
111 min: SMatrix::repeat(T::min_value()),
112 max: SMatrix::repeat(T::max_value()),
113 step: SMatrix::repeat(T::one()),
114 }
115 }
116}
117
118impl<const R: usize, const C: usize, T> Deref for MatrixEditor<R, C, T>
119where
120 T: NumericType,
121{
122 type Target = Widget;
123
124 fn deref(&self) -> &Self::Target {
125 &self.widget
126 }
127}
128
129impl<const R: usize, const C: usize, T> DerefMut for MatrixEditor<R, C, T>
130where
131 T: NumericType,
132{
133 fn deref_mut(&mut self) -> &mut Self::Target {
134 &mut self.widget
135 }
136}
137
138impl<const R: usize, const C: usize, T: NumericType> TypeUuidProvider for MatrixEditor<R, C, T> {
139 fn type_uuid() -> Uuid {
140 let r_id = Uuid::from_u64_pair(R as u64, R as u64);
141 let c_id = Uuid::from_u64_pair(C as u64, C as u64);
142 combine_uuids(
143 c_id,
144 combine_uuids(
145 r_id,
146 combine_uuids(
147 uuid!("9f05427a-5862-4574-bb21-ebaf52aa8c72"),
148 T::type_uuid(),
149 ),
150 ),
151 )
152 }
153}
154
155impl<const R: usize, const C: usize, T> Control for MatrixEditor<R, C, T>
156where
157 T: NumericType,
158{
159 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
160 self.widget.handle_routed_message(ui, message);
161
162 if let Some(&NumericUpDownMessage::Value(value)) = message.data::<NumericUpDownMessage<T>>()
163 {
164 if message.direction() == MessageDirection::FromWidget {
165 for (i, field) in self.fields.iter().enumerate() {
166 if message.destination() == *field {
167 let mut new_value = self.value;
168 new_value[i] = value;
169 ui.send_message(MatrixEditorMessage::value(
170 self.handle(),
171 MessageDirection::ToWidget,
172 new_value,
173 ));
174 }
175 }
176 }
177 } else if let Some(&MatrixEditorMessage::Value(new_value)) =
178 message.data::<MatrixEditorMessage<R, C, T>>()
179 {
180 if message.direction() == MessageDirection::ToWidget {
181 let mut changed = false;
182
183 for i in 0..self.fields.len() {
184 let editor = self.fields[i];
185 let current = &mut self.value[i];
186 let min = self.min[i];
187 let max = self.max[i];
188 let new = num_traits::clamp(new_value[i], min, max);
189
190 if *current != new {
191 *current = new;
192 ui.send_message(NumericUpDownMessage::value(
193 editor,
194 MessageDirection::ToWidget,
195 new,
196 ));
197 changed = true;
198 }
199 }
200
201 if changed {
202 ui.send_message(message.reverse());
203 }
204 }
205 }
206 }
207}
208
209pub struct MatrixEditorBuilder<const R: usize, const C: usize, T>
210where
211 T: NumericType,
212{
213 widget_builder: WidgetBuilder,
214 value: SMatrix<T, R, C>,
215 editable: bool,
216 min: SMatrix<T, R, C>,
217 max: SMatrix<T, R, C>,
218 step: SMatrix<T, R, C>,
219 precision: usize,
220}
221
222impl<const R: usize, const C: usize, T> MatrixEditorBuilder<R, C, T>
223where
224 T: NumericType,
225{
226 pub fn new(widget_builder: WidgetBuilder) -> Self {
227 Self {
228 widget_builder,
229 value: SMatrix::identity(),
230 editable: true,
231 min: SMatrix::repeat(T::min_value()),
232 max: SMatrix::repeat(T::max_value()),
233 step: SMatrix::repeat(T::one()),
234 precision: 3,
235 }
236 }
237
238 pub fn with_value(mut self, value: SMatrix<T, R, C>) -> Self {
239 self.value = value;
240 self
241 }
242
243 pub fn with_editable(mut self, editable: bool) -> Self {
244 self.editable = editable;
245 self
246 }
247
248 pub fn with_min(mut self, min: SMatrix<T, R, C>) -> Self {
249 self.min = min;
250 self
251 }
252
253 pub fn with_max(mut self, max: SMatrix<T, R, C>) -> Self {
254 self.max = max;
255 self
256 }
257
258 pub fn with_step(mut self, step: SMatrix<T, R, C>) -> Self {
259 self.step = step;
260 self
261 }
262
263 pub fn with_precision(mut self, precision: usize) -> Self {
264 self.precision = precision;
265 self
266 }
267
268 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
269 let mut fields = Vec::new();
270 let mut children = Vec::new();
271
272 for row in 0..R {
273 for column in 0..C {
274 let field = make_numeric_input(
275 ctx,
276 column,
277 row,
278 self.value[(row, column)],
279 self.min[(row, column)],
280 self.max[(row, column)],
281 self.step[(row, column)],
282 self.editable,
283 self.precision,
284 );
285 children.push(field);
286 fields.push(field);
287 }
288 }
289
290 let grid = GridBuilder::new(WidgetBuilder::new().with_children(children))
291 .add_rows(vec![Row::stretch(); R])
292 .add_columns(vec![Column::stretch(); C])
293 .build(ctx);
294
295 let node = MatrixEditor {
296 widget: self.widget_builder.with_child(grid).build(ctx),
297 fields,
298 value: self.value,
299 min: self.min,
300 max: self.max,
301 step: self.step,
302 };
303
304 ctx.add_node(UiNode::new(node))
305 }
306}
307
308#[cfg(test)]
309mod test {
310 use crate::matrix::MatrixEditorBuilder;
311 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
312
313 #[test]
314 fn test_deletion() {
315 test_widget_deletion(|ctx| {
316 MatrixEditorBuilder::<2, 2, f32>::new(WidgetBuilder::new()).build(ctx)
317 });
318 }
319}