Skip to main content

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