pelican_ui 2.0.4

Standard UI tools and components built off Pelican UI of the RAMP stack.
Documentation
use roost::{Component, Context};
use roost::events::OnEvent;
use roost::drawable::{Color, Drawable, Image};
use roost::layouts::{Bin, Column, Offset, Opt, Padding, Row, Size, Stack};

use crate::components::{Rectangle, AspectRatioImage};
use crate::components::interface::general::InterfaceTrait;
use crate::components::interface::navigation::{AppPage, NavigatorEvent, RootInfo, NavigatorSelectable};
use crate::plugin::PelicanUI;

#[derive(Component, Debug)]
pub struct DesktopInterface(Row, Option<Opt<Box<dyn Drawable>>>, Bin<Stack, Rectangle>, Option<Box<dyn AppPage>>);
impl OnEvent for DesktopInterface {}

impl InterfaceTrait for DesktopInterface {
    fn app_page(&mut self) -> &mut Option<Box<dyn AppPage>> {&mut self.3}
    fn navigator(&mut self) -> &mut Option<Opt<Box<dyn Drawable>>> {&mut self.1}
}

impl DesktopInterface {
    pub fn new(
        ctx: &mut Context, 
        start_page: Box<dyn AppPage>,
        navigation: Option<(Vec<RootInfo>, Option<Vec<RootInfo>>)>,
    ) -> Self {
        let color = ctx.get::<PelicanUI>().get().0.theme().colors.outline.secondary;
        let navigator = navigation.map(|n| Opt::new(Box::new(DesktopNavigator::new(ctx, n)) as Box<dyn Drawable>, true));
        let line_layout = Stack(Offset::default(), Offset::default(), Size::Static(1.0), Size::Fill, Padding::default());
        let separator = Bin(line_layout, Rectangle::new(color, 0.0, None));
        DesktopInterface(Row::start(0.0), navigator, separator, Some(start_page))
    }
}

#[derive(Debug, Component)]
pub struct DesktopNavigator(Column, Image, ButtonColumn, Option<Bin<Stack, Rectangle>>, Option<ButtonColumn>);
impl OnEvent for DesktopNavigator {}

impl DesktopNavigator {
    pub fn new(ctx: &mut Context, navigation: (Vec<RootInfo>, Option<Vec<RootInfo>>)) -> Self {
        let group_id = uuid::Uuid::new_v4();
        let (mut top_col, mut bot_col) = (Vec::new(), Vec::new());
        let mut i = 0;

        let spacer = navigation.1.as_ref().map(|_| {
            let width = Size::custom(move |widths: Vec<(f32, f32)>|(widths[1].0, widths[1].1));
            let spacer = Stack(Offset::Center, Offset::Center, width, Size::Fill, Padding::default());
            Bin(spacer, Rectangle::new(Color::TRANSPARENT, 0.0, None))
        });

        navigation.0.into_iter().for_each(|info| {
            let closure = move |ctx: &mut Context| ctx.trigger_event(NavigatorEvent(i));

            top_col.push(match info.avatar {
                Some(a) => NavigatorSelectable::desktop_avatar(ctx, a, &info.label, closure, 0 == i, group_id),
                None => NavigatorSelectable::desktop_icon(ctx, info.icon, &info.label, closure, 0 == i, group_id)
            });
            i += 1;
        });

        if let Some(n) = navigation.1 {
            n.into_iter().for_each(|info| {
                let closure = move |ctx: &mut Context| ctx.trigger_event(NavigatorEvent(i));

                bot_col.push(match info.avatar {
                    Some(a) => NavigatorSelectable::desktop_avatar(ctx, a, &info.label, closure, 0 == i, group_id),
                    None => NavigatorSelectable::desktop_icon(ctx, info.icon, &info.label, closure, 0 == i, group_id)
                });
                i += 1;
            });
        }

        let top_col = ButtonColumn::new(top_col);
        let bot_col = (!bot_col.is_empty()).then_some(ButtonColumn::new(bot_col));
        let wordmark = ctx.get::<PelicanUI>().get().0.theme().brand.wordmark.clone();
        let width = Size::custom(move |widths: Vec<(f32, f32)>|(widths[1].0, 200.0));

        DesktopNavigator(
            Column::new(32.0, Offset::Center, width, Padding(16.0, 32.0, 16.0, 32.0)),
            AspectRatioImage::new(wordmark, (100.0, 25.0)), top_col, spacer, bot_col
        )
    }

}

#[derive(Debug, Component)]
struct ButtonColumn(Column, Vec<NavigatorSelectable>);
impl OnEvent for ButtonColumn {}

impl ButtonColumn {
    fn new(buttons: Vec<NavigatorSelectable>) -> Self {
        ButtonColumn(Column::center(8.0), buttons)
    }
}