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