raui 0.70.17

Renderer Agnostic User Interface
use raui_app::app::{App, AppConfig, declarative::DeclarativeApp};
use raui_core::{
    Managed, make_widget, pre_hooks,
    view_model::{ViewModel, ViewModelValue},
    widget::{
        component::{
            containers::{
                content_box::content_box,
                horizontal_box::horizontal_box,
                vertical_box::{VerticalBoxProps, vertical_box},
            },
            image_box::{ImageBoxProps, image_box},
            interactive::{
                navigation::{NavItemActive, use_nav_container_active},
                slider_view::{SliderViewDirection, SliderViewProps, slider_view},
            },
            text_box::{TextBoxProps, text_box},
        },
        context::WidgetContext,
        node::WidgetNode,
        unit::{
            content::ContentBoxItemLayout,
            flex::FlexBoxItemLayout,
            text::{TextBoxFont, TextBoxHorizontalAlign, TextBoxVerticalAlign},
        },
        utils::{Color, Rect},
    },
};

const DATA: &str = "data";
const FLOAT_INPUT: &str = "float-input";
const INTEGER_INPUT: &str = "integer-input";
const UNSIGNED_INTEGER_INPUT: &str = "unsigned-integer-input";

struct AppData {
    float_input: Managed<ViewModelValue<f32>>,
    integer_input: Managed<ViewModelValue<i32>>,
    unsigned_integer_input: Managed<ViewModelValue<u8>>,
}

fn use_app(ctx: &mut WidgetContext) {
    ctx.life_cycle.mount(|mut ctx| {
        ctx.view_models
            .bindings(DATA, FLOAT_INPUT)
            .unwrap()
            .bind(ctx.id.to_owned());
        ctx.view_models
            .bindings(DATA, INTEGER_INPUT)
            .unwrap()
            .bind(ctx.id.to_owned());
        ctx.view_models
            .bindings(DATA, UNSIGNED_INTEGER_INPUT)
            .unwrap()
            .bind(ctx.id.to_owned());
    });
}

#[pre_hooks(use_nav_container_active, use_app)]
fn app(mut ctx: WidgetContext) -> WidgetNode {
    let mut app_data = ctx
        .view_models
        .view_model_mut(DATA)
        .unwrap()
        .write::<AppData>()
        .unwrap();

    make_widget!(horizontal_box)
        .listed_slot(
            make_widget!(input)
                .with_props(FlexBoxItemLayout {
                    margin: 50.0.into(),
                    ..Default::default()
                })
                .with_props(SliderViewProps {
                    input: Some(app_data.float_input.lazy().into()),
                    from: -10.0,
                    to: 10.0,
                    direction: SliderViewDirection::BottomToTop,
                }),
        )
        .listed_slot(
            make_widget!(vertical_box)
                .with_props(VerticalBoxProps {
                    override_slots_layout: Some(FlexBoxItemLayout {
                        margin: 50.0.into(),
                        ..Default::default()
                    }),
                    ..Default::default()
                })
                .listed_slot(make_widget!(input).with_props(SliderViewProps {
                    input: Some(app_data.integer_input.lazy().into()),
                    from: -2.0,
                    to: 2.0,
                    ..Default::default()
                }))
                .listed_slot(make_widget!(input).with_props(SliderViewProps {
                    input: Some(app_data.unsigned_integer_input.lazy().into()),
                    from: -3.0,
                    to: 7.0,
                    direction: SliderViewDirection::RightToLeft,
                })),
        )
        .into()
}

fn input(ctx: WidgetContext) -> WidgetNode {
    let props = ctx.props.read_cloned_or_default::<SliderViewProps>();
    let percentage = props.get_percentage();
    let value = props.get_value();
    let anchors = match props.direction {
        SliderViewDirection::LeftToRight => Rect {
            left: 0.0,
            right: percentage,
            top: 0.0,
            bottom: 1.0,
        },
        SliderViewDirection::RightToLeft => Rect {
            left: 1.0 - percentage,
            right: 1.0,
            top: 0.0,
            bottom: 1.0,
        },
        SliderViewDirection::TopToBottom => Rect {
            left: 0.0,
            right: 1.0,
            top: 0.0,
            bottom: percentage,
        },
        SliderViewDirection::BottomToTop => Rect {
            left: 0.0,
            right: 1.0,
            top: 1.0 - percentage,
            bottom: 1.0,
        },
    };

    make_widget!(slider_view)
        .with_props(NavItemActive)
        .with_props(props)
        .named_slot(
            "content",
            make_widget!(content_box)
                .listed_slot(
                    make_widget!(image_box).with_props(ImageBoxProps::colored(Color {
                        r: 0.0,
                        g: 0.0,
                        b: 1.0,
                        a: 1.0,
                    })),
                )
                .listed_slot(
                    make_widget!(image_box)
                        .with_props(ContentBoxItemLayout {
                            anchors,
                            ..Default::default()
                        })
                        .with_props(ImageBoxProps::colored(Color {
                            r: 1.0,
                            g: 0.0,
                            b: 0.0,
                            a: 1.0,
                        })),
                )
                .listed_slot(make_widget!(text_box).with_props(TextBoxProps {
                    text: value.to_string(),
                    horizontal_align: TextBoxHorizontalAlign::Center,
                    vertical_align: TextBoxVerticalAlign::Middle,
                    font: TextBoxFont {
                        name: "./demos/hello-world/resources/verdana.ttf".to_owned(),
                        size: 64.0,
                    },
                    color: Color {
                        r: 1.0,
                        g: 1.0,
                        b: 1.0,
                        a: 1.0,
                    },
                    ..Default::default()
                })),
        )
        .into()
}

fn main() {
    let app = DeclarativeApp::default()
        .tree(make_widget!(app))
        .view_model(
            DATA,
            ViewModel::produce(|properties| AppData {
                float_input: Managed::new(ViewModelValue::new(
                    Default::default(),
                    properties.notifier(FLOAT_INPUT),
                )),
                integer_input: Managed::new(ViewModelValue::new(
                    Default::default(),
                    properties.notifier(INTEGER_INPUT),
                )),
                unsigned_integer_input: Managed::new(ViewModelValue::new(
                    Default::default(),
                    properties.notifier(UNSIGNED_INTEGER_INPUT),
                )),
            }),
        );

    App::new(AppConfig::default().title("Slider View")).run(app);
}