Skip to main content

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