#![warn(missing_docs)]
use crate::{
core::{
algebra::Vector2, math::Rect, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
visitor::prelude::*,
},
define_constructor,
grid::{Column, GridBuilder, Row},
message::{MessageDirection, UiMessage},
numeric::NumericType,
text::TextBuilder,
vec::{VecEditorBuilder, VecEditorMessage},
widget::{Widget, WidgetBuilder},
BuildContext, Control, Thickness, UiNode, UserInterface, VerticalAlignment,
};
use fyrox_core::variable::InheritableVariable;
use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
#[derive(Debug, Clone, PartialEq)]
pub enum RectEditorMessage<T>
where
T: NumericType,
{
Value(Rect<T>),
}
impl<T: NumericType> RectEditorMessage<T> {
define_constructor!(
RectEditorMessage:Value => fn value(Rect<T>), layout: false
);
}
#[derive(Default, Debug, Clone, Visit, Reflect, ComponentProvider)]
pub struct RectEditor<T>
where
T: NumericType,
{
pub widget: Widget,
pub position: InheritableVariable<Handle<UiNode>>,
pub size: InheritableVariable<Handle<UiNode>>,
pub value: InheritableVariable<Rect<T>>,
}
impl<T: NumericType> ConstructorProvider<UiNode, UserInterface> for RectEditor<T> {
fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
GraphNodeConstructor::new::<Self>()
.with_variant(
format!("Rect Editor<{}>", std::any::type_name::<T>()),
|ui| {
RectEditorBuilder::<T>::new(WidgetBuilder::new())
.build(&mut ui.build_ctx())
.into()
},
)
.with_group("Rect")
}
}
impl<T> Deref for RectEditor<T>
where
T: NumericType,
{
type Target = Widget;
fn deref(&self) -> &Self::Target {
&self.widget
}
}
impl<T> DerefMut for RectEditor<T>
where
T: NumericType,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.widget
}
}
impl<T> TypeUuidProvider for RectEditor<T>
where
T: NumericType,
{
fn type_uuid() -> Uuid {
combine_uuids(
uuid!("5a3daf9d-f33b-494b-b111-eb55721dc7ac"),
T::type_uuid(),
)
}
}
impl<T> Control for RectEditor<T>
where
T: NumericType,
{
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.widget.handle_routed_message(ui, message);
if let Some(RectEditorMessage::Value(value)) = message.data::<RectEditorMessage<T>>() {
if message.destination() == self.handle
&& message.direction() == MessageDirection::ToWidget
&& *value != *self.value
{
self.value.set_value_and_mark_modified(*value);
ui.send_message(message.reverse());
}
} else if let Some(VecEditorMessage::Value(value)) =
message.data::<VecEditorMessage<T, 2>>()
{
if message.direction() == MessageDirection::FromWidget {
if message.destination() == *self.position {
if self.value.position != *value {
ui.send_message(RectEditorMessage::value(
self.handle,
MessageDirection::ToWidget,
Rect::new(value.x, value.y, self.value.size.x, self.value.size.y),
));
}
} else if message.destination() == *self.size && self.value.size != *value {
ui.send_message(RectEditorMessage::value(
self.handle,
MessageDirection::ToWidget,
Rect::new(
self.value.position.x,
self.value.position.y,
value.x,
value.y,
),
));
}
}
}
}
}
pub struct RectEditorBuilder<T>
where
T: NumericType,
{
widget_builder: WidgetBuilder,
value: Rect<T>,
}
fn create_field<T: NumericType>(
ctx: &mut BuildContext,
name: &str,
value: Vector2<T>,
row: usize,
) -> (Handle<UiNode>, Handle<UiNode>) {
let editor;
let grid = GridBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::left(10.0))
.on_row(row)
.with_child(
TextBuilder::new(WidgetBuilder::new())
.with_text(name)
.with_vertical_text_alignment(VerticalAlignment::Center)
.build(ctx),
)
.with_child({
editor = VecEditorBuilder::new(WidgetBuilder::new().on_column(1))
.with_value(value)
.build(ctx);
editor
}),
)
.add_column(Column::strict(70.0))
.add_column(Column::stretch())
.add_row(Row::stretch())
.build(ctx);
(grid, editor)
}
impl<T> RectEditorBuilder<T>
where
T: NumericType,
{
pub fn new(widget_builder: WidgetBuilder) -> Self {
Self {
widget_builder,
value: Default::default(),
}
}
pub fn with_value(mut self, value: Rect<T>) -> Self {
self.value = value;
self
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
let (position_grid, position) = create_field(ctx, "Position", self.value.position, 0);
let (size_grid, size) = create_field(ctx, "Size", self.value.size, 1);
let node = RectEditor {
widget: self
.widget_builder
.with_child(
GridBuilder::new(
WidgetBuilder::new()
.with_child(position_grid)
.with_child(size_grid),
)
.add_row(Row::stretch())
.add_row(Row::stretch())
.add_column(Column::stretch())
.build(ctx),
)
.build(ctx),
value: self.value.into(),
position: position.into(),
size: size.into(),
};
ctx.add_node(UiNode::new(node))
}
}
#[cfg(test)]
mod test {
use crate::rect::RectEditorBuilder;
use crate::{test::test_widget_deletion, widget::WidgetBuilder};
#[test]
fn test_deletion() {
test_widget_deletion(|ctx| RectEditorBuilder::<f32>::new(WidgetBuilder::new()).build(ctx));
}
}