fyrox_ui/
rect.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
21//! Rect editor widget is used to show and edit [`Rect`] values. It shows four numeric fields: two for top left corner
22//! of a rect, two for its size. See [`RectEditor`] docs for more info and usage examples.
23
24#![warn(missing_docs)]
25
26use crate::{
27    core::{
28        algebra::Vector2, math::Rect, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
29        visitor::prelude::*,
30    },
31    define_constructor,
32    grid::{Column, GridBuilder, Row},
33    message::{MessageDirection, UiMessage},
34    numeric::NumericType,
35    text::TextBuilder,
36    vec::{VecEditorBuilder, VecEditorMessage},
37    widget::{Widget, WidgetBuilder},
38    BuildContext, Control, Thickness, UiNode, UserInterface, VerticalAlignment,
39};
40use fyrox_core::variable::InheritableVariable;
41use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
42use std::{
43    fmt::Debug,
44    ops::{Deref, DerefMut},
45};
46
47/// A set of possible messages, that can be used to either modify or fetch the state of a [`RectEditor`] widget.
48#[derive(Debug, Clone, PartialEq)]
49pub enum RectEditorMessage<T>
50where
51    T: NumericType,
52{
53    /// A message, that can be used to either modify or fetch the current value of a [`RectEditor`] widget.
54    Value(Rect<T>),
55}
56
57impl<T: NumericType> RectEditorMessage<T> {
58    define_constructor!(
59        /// Creates [`RectEditorMessage::Value`] message.
60        RectEditorMessage:Value => fn value(Rect<T>), layout: false
61    );
62}
63
64/// Rect editor widget is used to show and edit [`Rect`] values. It shows four numeric fields: two for top left corner
65/// of a rect, two for its size.
66///
67/// ## Example
68///
69/// Rect editor can be created using [`RectEditorBuilder`], like so:
70///
71/// ```rust
72/// # use fyrox_ui::{
73/// #     core::{math::Rect, pool::Handle},
74/// #     rect::RectEditorBuilder,
75/// #     widget::WidgetBuilder,
76/// #     BuildContext, UiNode,
77/// # };
78/// #
79/// fn create_rect_editor(ctx: &mut BuildContext) -> Handle<UiNode> {
80///     RectEditorBuilder::new(WidgetBuilder::new())
81///         .with_value(Rect::new(0, 0, 10, 20))
82///         .build(ctx)
83/// }
84/// ```
85///
86/// ## Value
87///
88/// To change the value of a rect editor, use [`RectEditorMessage::Value`] message:
89///
90/// ```rust
91/// # use fyrox_ui::{
92/// #     core::{math::Rect, pool::Handle},
93/// #     message::MessageDirection,
94/// #     rect::RectEditorMessage,
95/// #     UiNode, UserInterface,
96/// # };
97/// #
98/// fn change_value(rect_editor: Handle<UiNode>, ui: &UserInterface) {
99///     ui.send_message(RectEditorMessage::value(
100///         rect_editor,
101///         MessageDirection::ToWidget,
102///         Rect::new(20, 20, 60, 80),
103///     ));
104/// }
105/// ```
106///
107/// To "catch" the moment when the value of a rect editor has changed, listen to the same message, but check its direction:
108///
109/// ```rust
110/// # use fyrox_ui::{
111/// #     core::pool::Handle,
112/// #     message::{MessageDirection, UiMessage},
113/// #     rect::RectEditorMessage,
114/// #     UiNode,
115/// # };
116/// #
117/// fn fetch_value(rect_editor: Handle<UiNode>, message: &UiMessage) {
118///     if let Some(RectEditorMessage::Value(value)) = message.data::<RectEditorMessage<u32>>() {
119///         if message.destination() == rect_editor
120///             && message.direction() == MessageDirection::FromWidget
121///         {
122///             println!("The new value is: {:?}", value)
123///         }
124///     }
125/// }
126/// ```
127#[derive(Default, Debug, Clone, Visit, Reflect, ComponentProvider)]
128pub struct RectEditor<T>
129where
130    T: NumericType,
131{
132    /// Base widget of the rect editor.
133    pub widget: Widget,
134    /// A handle to a widget, that is used to show/edit position part of the rect.
135    pub position: InheritableVariable<Handle<UiNode>>,
136    /// A handle to a widget, that is used to show/edit size part of the rect.
137    pub size: InheritableVariable<Handle<UiNode>>,
138    /// Current value of the rect editor.
139    pub value: InheritableVariable<Rect<T>>,
140}
141
142impl<T: NumericType> ConstructorProvider<UiNode, UserInterface> for RectEditor<T> {
143    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
144        GraphNodeConstructor::new::<Self>()
145            .with_variant(
146                format!("Rect Editor<{}>", std::any::type_name::<T>()),
147                |ui| {
148                    RectEditorBuilder::<T>::new(WidgetBuilder::new())
149                        .build(&mut ui.build_ctx())
150                        .into()
151                },
152            )
153            .with_group("Rect")
154    }
155}
156
157impl<T> Deref for RectEditor<T>
158where
159    T: NumericType,
160{
161    type Target = Widget;
162
163    fn deref(&self) -> &Self::Target {
164        &self.widget
165    }
166}
167
168impl<T> DerefMut for RectEditor<T>
169where
170    T: NumericType,
171{
172    fn deref_mut(&mut self) -> &mut Self::Target {
173        &mut self.widget
174    }
175}
176
177impl<T> TypeUuidProvider for RectEditor<T>
178where
179    T: NumericType,
180{
181    fn type_uuid() -> Uuid {
182        combine_uuids(
183            uuid!("5a3daf9d-f33b-494b-b111-eb55721dc7ac"),
184            T::type_uuid(),
185        )
186    }
187}
188
189impl<T> Control for RectEditor<T>
190where
191    T: NumericType,
192{
193    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
194        self.widget.handle_routed_message(ui, message);
195
196        if let Some(RectEditorMessage::Value(value)) = message.data::<RectEditorMessage<T>>() {
197            if message.destination() == self.handle
198                && message.direction() == MessageDirection::ToWidget
199                && *value != *self.value
200            {
201                self.value.set_value_and_mark_modified(*value);
202
203                ui.send_message(message.reverse());
204            }
205        } else if let Some(VecEditorMessage::Value(value)) =
206            message.data::<VecEditorMessage<T, 2>>()
207        {
208            if message.direction() == MessageDirection::FromWidget {
209                if message.destination() == *self.position {
210                    if self.value.position != *value {
211                        ui.send_message(RectEditorMessage::value(
212                            self.handle,
213                            MessageDirection::ToWidget,
214                            Rect::new(value.x, value.y, self.value.size.x, self.value.size.y),
215                        ));
216                    }
217                } else if message.destination() == *self.size && self.value.size != *value {
218                    ui.send_message(RectEditorMessage::value(
219                        self.handle,
220                        MessageDirection::ToWidget,
221                        Rect::new(
222                            self.value.position.x,
223                            self.value.position.y,
224                            value.x,
225                            value.y,
226                        ),
227                    ));
228                }
229            }
230        }
231    }
232}
233
234/// Rect editor builder creates [`RectEditor`] widget instances and adds them to the user interface.
235pub struct RectEditorBuilder<T>
236where
237    T: NumericType,
238{
239    widget_builder: WidgetBuilder,
240    value: Rect<T>,
241}
242
243fn create_field<T: NumericType>(
244    ctx: &mut BuildContext,
245    name: &str,
246    value: Vector2<T>,
247    row: usize,
248) -> (Handle<UiNode>, Handle<UiNode>) {
249    let editor;
250    let grid = GridBuilder::new(
251        WidgetBuilder::new()
252            .with_margin(Thickness::left(10.0))
253            .on_row(row)
254            .with_child(
255                TextBuilder::new(WidgetBuilder::new())
256                    .with_text(name)
257                    .with_vertical_text_alignment(VerticalAlignment::Center)
258                    .build(ctx),
259            )
260            .with_child({
261                editor = VecEditorBuilder::new(WidgetBuilder::new().on_column(1))
262                    .with_value(value)
263                    .build(ctx);
264                editor
265            }),
266    )
267    .add_column(Column::strict(70.0))
268    .add_column(Column::stretch())
269    .add_row(Row::stretch())
270    .build(ctx);
271    (grid, editor)
272}
273
274impl<T> RectEditorBuilder<T>
275where
276    T: NumericType,
277{
278    /// Creates new rect editor builder instance.
279    pub fn new(widget_builder: WidgetBuilder) -> Self {
280        Self {
281            widget_builder,
282            value: Default::default(),
283        }
284    }
285
286    /// Sets the desired value.
287    pub fn with_value(mut self, value: Rect<T>) -> Self {
288        self.value = value;
289        self
290    }
291
292    /// Finished rect editor widget building and adds it to the user interface.
293    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
294        let (position_grid, position) = create_field(ctx, "Position", self.value.position, 0);
295        let (size_grid, size) = create_field(ctx, "Size", self.value.size, 1);
296        let node = RectEditor {
297            widget: self
298                .widget_builder
299                .with_child(
300                    GridBuilder::new(
301                        WidgetBuilder::new()
302                            .with_child(position_grid)
303                            .with_child(size_grid),
304                    )
305                    .add_row(Row::stretch())
306                    .add_row(Row::stretch())
307                    .add_column(Column::stretch())
308                    .build(ctx),
309                )
310                .build(ctx),
311            value: self.value.into(),
312            position: position.into(),
313            size: size.into(),
314        };
315
316        ctx.add_node(UiNode::new(node))
317    }
318}
319
320#[cfg(test)]
321mod test {
322    use crate::rect::RectEditorBuilder;
323    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
324
325    #[test]
326    fn test_deletion() {
327        test_widget_deletion(|ctx| RectEditorBuilder::<f32>::new(WidgetBuilder::new()).build(ctx));
328    }
329}