Skip to main content

fyrox_ui/
matrix.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use 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}