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}