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