rg3d-ui 0.15.0

Extendable UI library
Documentation
use crate::{
    core::{algebra::Vector2, math::Rect, pool::Handle},
    define_constructor,
    grid::{Column, GridBuilder, Row},
    message::{MessageDirection, UiMessage},
    numeric::NumericType,
    text::TextBuilder,
    vec::vec2::{Vec2EditorBuilder, Vec2EditorMessage},
    widget::{Widget, WidgetBuilder},
    BuildContext, Control, Thickness, UiNode, UserInterface, VerticalAlignment,
};
use std::{
    any::{Any, TypeId},
    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(Debug, Clone)]
pub struct RectEditor<T>
where
    T: NumericType,
{
    widget: Widget,
    position: Handle<UiNode>,
    size: Handle<UiNode>,
    value: Rect<T>,
}

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> Control for RectEditor<T>
where
    T: NumericType,
{
    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
        if type_id == TypeId::of::<Self>() {
            Some(self)
        } else {
            None
        }
    }

    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 = *value;

                ui.send_message(message.reverse());
            }
        } else if let Some(Vec2EditorMessage::Value(value)) = message.data::<Vec2EditorMessage<T>>()
        {
            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 = Vec2EditorBuilder::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(),
            value: self.value,
            position,
            size,
        };

        ctx.add_node(UiNode::new(node))
    }
}