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