fyrox_ui/
vec.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    border::BorderBuilder,
23    brush::Brush,
24    core::{
25        algebra::SVector, color::Color, num_traits, pool::Handle, reflect::prelude::*,
26        type_traits::prelude::*, visitor::prelude::*,
27    },
28    define_constructor,
29    grid::{Column, GridBuilder, Row},
30    message::{MessageDirection, UiMessage},
31    numeric::{NumericType, NumericUpDownBuilder, NumericUpDownMessage},
32    widget::WidgetBuilder,
33    BuildContext, Control, Thickness, UiNode, UserInterface, Widget,
34};
35
36use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
37use std::ops::{Deref, DerefMut};
38
39fn make_numeric_input<T: NumericType>(
40    ctx: &mut BuildContext,
41    column: usize,
42    value: T,
43    min: T,
44    max: T,
45    step: T,
46    editable: bool,
47    precision: usize,
48) -> Handle<UiNode> {
49    NumericUpDownBuilder::new(
50        WidgetBuilder::new()
51            .on_row(0)
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
69pub fn make_mark(ctx: &mut BuildContext, column: usize, color: Color) -> Handle<UiNode> {
70    BorderBuilder::new(
71        WidgetBuilder::new()
72            .on_row(0)
73            .on_column(column)
74            .with_background(Brush::Solid(color).into())
75            .with_foreground(Brush::Solid(Color::TRANSPARENT).into())
76            .with_width(4.0),
77    )
78    .with_corner_radius(2.0f32.into())
79    .with_pad_by_corner_radius(false)
80    .build(ctx)
81}
82
83#[derive(Debug, Clone, PartialEq)]
84pub enum VecEditorMessage<T, const D: usize>
85where
86    T: NumericType,
87{
88    Value(SVector<T, D>),
89}
90
91impl<T, const D: usize> VecEditorMessage<T, D>
92where
93    T: NumericType,
94{
95    define_constructor!(VecEditorMessage:Value => fn value(SVector<T, D>), layout: false);
96}
97
98#[derive(Clone, Visit, Reflect, Debug, ComponentProvider)]
99#[reflect(derived_type = "UiNode")]
100pub struct VecEditor<T, const D: usize>
101where
102    T: NumericType,
103{
104    pub widget: Widget,
105    pub fields: Vec<Handle<UiNode>>,
106    #[reflect(hidden)]
107    #[visit(skip)]
108    pub value: SVector<T, D>,
109    #[reflect(hidden)]
110    #[visit(skip)]
111    pub min: SVector<T, D>,
112    #[reflect(hidden)]
113    #[visit(skip)]
114    pub max: SVector<T, D>,
115    #[reflect(hidden)]
116    #[visit(skip)]
117    pub step: SVector<T, D>,
118}
119
120impl<T: NumericType, const D: usize> ConstructorProvider<UiNode, UserInterface>
121    for VecEditor<T, D>
122{
123    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
124        GraphNodeConstructor::new::<Self>()
125            .with_variant(
126                format!("Vec Editor<{}, {}>", std::any::type_name::<T>(), D),
127                |ui| {
128                    VecEditorBuilder::<T, D>::new(WidgetBuilder::new().with_name("Vec Editor"))
129                        .build(&mut ui.build_ctx())
130                        .into()
131                },
132            )
133            .with_group("Vector")
134    }
135}
136
137impl<T, const D: usize> Default for VecEditor<T, D>
138where
139    T: NumericType,
140{
141    fn default() -> Self {
142        Self {
143            widget: Default::default(),
144            fields: Default::default(),
145            value: SVector::from([T::default(); D]),
146            min: SVector::from([T::default(); D]),
147            max: SVector::from([T::default(); D]),
148            step: SVector::from([T::default(); D]),
149        }
150    }
151}
152
153impl<T, const D: usize> Deref for VecEditor<T, D>
154where
155    T: NumericType,
156{
157    type Target = Widget;
158
159    fn deref(&self) -> &Self::Target {
160        &self.widget
161    }
162}
163
164impl<T, const D: usize> DerefMut for VecEditor<T, D>
165where
166    T: NumericType,
167{
168    fn deref_mut(&mut self) -> &mut Self::Target {
169        &mut self.widget
170    }
171}
172
173// TODO: Is 16 enough?
174const DIM_UUIDS: [Uuid; 16] = [
175    uuid!("11ec6ec2-9780-4dbe-827a-935cb9ec5bb0"),
176    uuid!("af532488-8833-443a-8ece-d8380e5ad148"),
177    uuid!("6738154a-9663-4628-bb9d-f61d453eafcd"),
178    uuid!("448dab8c-b4e6-478e-a704-ea0b0db628aa"),
179    uuid!("67246977-8802-4e72-a19f-6e4f60b6eced"),
180    uuid!("f711a9f8-288a-4a28-b30e-e7bcfdf26ab0"),
181    uuid!("c92ac3ad-5dc5-41dd-abbd-7fb9aacb9a5f"),
182    uuid!("88d9a035-4424-40d2-af62-f701025bd767"),
183    uuid!("dda09036-18d8-40bc-ae9d-1a69f45e2ba0"),
184    uuid!("b6fe9585-6ebc-4b4d-be66-484b4d7b3d5b"),
185    uuid!("03c41033-e8fe-420d-b246-e7c9dcd7c01b"),
186    uuid!("14ea7e95-0f94-4b15-a53c-97d7d2e58d4e"),
187    uuid!("0149f666-33cf-4e39-b4bd-58502994b162"),
188    uuid!("abb9f691-0958-464b-a37d-a3336b4d33f9"),
189    uuid!("6f37cfd5-9bec-40ec-9dbc-e532d43b81b7"),
190    uuid!("fa786077-95b9-4e7c-9268-7d0314c005ba"),
191];
192
193impl<T: NumericType, const D: usize> TypeUuidProvider for VecEditor<T, D> {
194    fn type_uuid() -> Uuid {
195        combine_uuids(
196            combine_uuids(
197                uuid!("0332144f-c70e-456a-812b-f9b89980d2ba"),
198                T::type_uuid(),
199            ),
200            DIM_UUIDS[D],
201        )
202    }
203}
204
205impl<T, const D: usize> Control for VecEditor<T, D>
206where
207    T: NumericType,
208{
209    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
210        self.widget.handle_routed_message(ui, message);
211
212        if let Some(&NumericUpDownMessage::Value(value)) = message.data::<NumericUpDownMessage<T>>()
213        {
214            if message.direction() == MessageDirection::FromWidget {
215                for (i, field) in self.fields.iter().enumerate() {
216                    if message.destination() == *field {
217                        let mut new_value = self.value;
218                        new_value[i] = value;
219                        ui.send_message(VecEditorMessage::value(
220                            self.handle(),
221                            MessageDirection::ToWidget,
222                            new_value,
223                        ));
224                    }
225                }
226            }
227        } else if let Some(&VecEditorMessage::Value(new_value)) =
228            message.data::<VecEditorMessage<T, D>>()
229        {
230            if message.direction() == MessageDirection::ToWidget {
231                let mut changed = false;
232
233                for i in 0..D {
234                    let editor = self.fields[i];
235                    let current = &mut self.value[i];
236                    let min = self.min[i];
237                    let max = self.max[i];
238                    let new = num_traits::clamp(new_value[i], min, max);
239
240                    if *current != new {
241                        *current = new;
242                        ui.send_message(NumericUpDownMessage::value(
243                            editor,
244                            MessageDirection::ToWidget,
245                            new,
246                        ));
247                        changed = true;
248                    }
249                }
250
251                if changed {
252                    ui.send_message(message.reverse());
253                }
254            }
255        }
256    }
257}
258
259pub struct VecEditorBuilder<T, const D: usize>
260where
261    T: NumericType,
262{
263    widget_builder: WidgetBuilder,
264    value: SVector<T, D>,
265    editable: bool,
266    min: SVector<T, D>,
267    max: SVector<T, D>,
268    step: SVector<T, D>,
269    precision: usize,
270}
271
272impl<T, const D: usize> VecEditorBuilder<T, D>
273where
274    T: NumericType,
275{
276    pub fn new(widget_builder: WidgetBuilder) -> Self {
277        Self {
278            widget_builder,
279            value: SVector::repeat(Default::default()),
280            editable: true,
281            min: SVector::repeat(T::min_value()),
282            max: SVector::repeat(T::max_value()),
283            step: SVector::repeat(T::one()),
284            precision: 3,
285        }
286    }
287
288    pub fn with_value(mut self, value: SVector<T, D>) -> Self {
289        self.value = value;
290        self
291    }
292
293    pub fn with_editable(mut self, editable: bool) -> Self {
294        self.editable = editable;
295        self
296    }
297
298    pub fn with_min(mut self, min: SVector<T, D>) -> Self {
299        self.min = min;
300        self
301    }
302
303    pub fn with_max(mut self, max: SVector<T, D>) -> Self {
304        self.max = max;
305        self
306    }
307
308    pub fn with_step(mut self, step: SVector<T, D>) -> Self {
309        self.step = step;
310        self
311    }
312
313    pub fn with_precision(mut self, precision: usize) -> Self {
314        self.precision = precision;
315        self
316    }
317
318    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
319        let mut fields = Vec::new();
320        let mut children = Vec::new();
321        let mut columns = Vec::new();
322
323        let colors = [
324            Color::opaque(120, 0, 0),
325            Color::opaque(0, 120, 0),
326            Color::opaque(0, 0, 120),
327            Color::opaque(120, 0, 120),
328            Color::opaque(0, 120, 120),
329            Color::opaque(120, 120, 0),
330        ];
331
332        for i in 0..D {
333            children.push(make_mark(
334                ctx,
335                i * 2,
336                colors.get(i).cloned().unwrap_or(Color::ORANGE),
337            ));
338
339            let field = make_numeric_input(
340                ctx,
341                i * 2 + 1,
342                self.value[i],
343                self.min[i],
344                self.max[i],
345                self.step[i],
346                self.editable,
347                self.precision,
348            );
349            children.push(field);
350            fields.push(field);
351
352            columns.push(Column::auto());
353            columns.push(Column::stretch());
354        }
355
356        let grid = GridBuilder::new(WidgetBuilder::new().with_children(children))
357            .add_row(Row::stretch())
358            .add_columns(columns)
359            .build(ctx);
360
361        let node = VecEditor {
362            widget: self.widget_builder.with_child(grid).build(ctx),
363            fields,
364            value: self.value,
365            min: self.min,
366            max: self.max,
367            step: self.step,
368        };
369
370        ctx.add_node(UiNode::new(node))
371    }
372}
373
374pub type Vec2Editor<T> = VecEditor<T, 2>;
375pub type Vec3Editor<T> = VecEditor<T, 3>;
376pub type Vec4Editor<T> = VecEditor<T, 4>;
377pub type Vec5Editor<T> = VecEditor<T, 5>;
378pub type Vec6Editor<T> = VecEditor<T, 6>;
379
380pub type Vec2EditorMessage<T> = VecEditorMessage<T, 2>;
381pub type Vec3EditorMessage<T> = VecEditorMessage<T, 3>;
382pub type Vec4EditorMessage<T> = VecEditorMessage<T, 4>;
383pub type Vec5EditorMessage<T> = VecEditorMessage<T, 5>;
384pub type Vec6EditorMessage<T> = VecEditorMessage<T, 6>;
385
386pub type Vec2EditorBuilder<T> = VecEditorBuilder<T, 2>;
387pub type Vec3EditorBuilder<T> = VecEditorBuilder<T, 3>;
388pub type Vec4EditorBuilder<T> = VecEditorBuilder<T, 4>;
389pub type Vec5EditorBuilder<T> = VecEditorBuilder<T, 5>;
390pub type Vec6EditorBuilder<T> = VecEditorBuilder<T, 6>;
391
392#[cfg(test)]
393mod test {
394    use crate::vec::Vec2EditorBuilder;
395    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
396
397    #[test]
398    fn test_deletion() {
399        test_widget_deletion(|ctx| Vec2EditorBuilder::<f32>::new(WidgetBuilder::new()).build(ctx));
400    }
401}