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}