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}