pelican_ui 2.2.0

Standard UI tools and components built off Pelican UI of the RAMP stack.
Documentation
use prism::event::{OnEvent, KeyboardState, KeyboardEvent, NamedKey, Key};
use prism::layout::{Stack, Column, Row, Offset};
use prism::drawable::{Drawable, Component};
use prism::canvas::Align;
use prism::{Context, Request};

use ptsd::interactions;

use crate::theme::{self, Theme, Icons};
use crate::components::Icon;
use crate::components::text::{Text, TextStyle};
use crate::components::button::{ButtonWidth, ButtonSize, Button};

#[derive(Debug, Component, Clone)]
pub struct Keypad(Column, Vec<GhostButtonRow>);
impl OnEvent for Keypad {}

impl Keypad {
    pub fn new(theme: &Theme) -> Self {
        Keypad(Column::center(16.0), vec![
            GhostButtonRow::new(theme, vec![KeypadButton::char('1'), KeypadButton::char('2'), KeypadButton::char('3')]),
            GhostButtonRow::new(theme, vec![KeypadButton::char('4'), KeypadButton::char('5'), KeypadButton::char('6')]),
            GhostButtonRow::new(theme, vec![KeypadButton::char('7'), KeypadButton::char('8'), KeypadButton::char('9')]),
            GhostButtonRow::new(theme, vec![KeypadButton::char('.'), KeypadButton::char('0'), KeypadButton::delete()]),
        ])
    }
}


struct KeypadButton;

impl KeypadButton {
    pub fn char(c: char) -> (Option<char>, Option<Icons>, Key) {
        (Some(c), None, Key::Character(c.to_string().as_str().into()))
    }

    pub fn delete() -> (Option<char>, Option<Icons>, Key) {
        (None, Some(Icons::Back), Key::Named(NamedKey::Delete))
    }
}

#[derive(Debug, Component, Clone)]
pub struct GhostButtonRow(Row, Vec<GhostButton>);
impl OnEvent for GhostButtonRow {}

impl GhostButtonRow {
    pub fn new(theme: &Theme, data: Vec<(Option<char>, Option<Icons>, Key)>) -> Self {
        GhostButtonRow(Row::center(16.0), data.into_iter().map(|(c, i, key)| {
            let label = c.map(|character| character.to_string());
            GhostButton::new(theme, label.as_deref(), i, move |ctx: &mut Context, _: &Theme| {
                ctx.send(Request::Event(Box::new(KeyboardEvent{state: KeyboardState::Pressed, key: key.clone()})));
            })
        }).collect::<Vec<GhostButton>>())
    }
}

#[derive(Debug, Component, Clone)]
struct GhostButton(Stack, pub interactions::Button);
impl OnEvent for GhostButton {}
impl GhostButton {
    fn new(theme: &Theme, label: Option<&str>, icon: Option<Icons>, mut on_click: impl FnMut(&mut Context, &Theme) + Clone + 'static) -> Self {
        let colors = theme::Button::get(theme.colors(), theme::Variant::Ghost);
        let default =  {
            let font_size = ButtonSize::Large.font();
            let icon_size = ButtonSize::Large.icon();
            let mut drawables: Vec<Box<dyn Drawable>> = Vec::new();
            if let Some(l) = label { drawables.push(Box::new(Text::new(theme, l, font_size, TextStyle::Label(colors.default.label), Align::Left, None))); }
            if let Some(i) = icon { drawables.push(Box::new(Icon::new(theme, i, Some(colors.default.label), icon_size))); }
            Button::new(drawables, ButtonSize::Large, ButtonWidth::Fill, Offset::Center, colors.default.background, colors.default.outline)
        };
        
        let theme = theme.clone();
        let callback = Box::new(move |ctx: &mut Context| (on_click)(ctx, &theme));
        GhostButton(Stack::default(), interactions::Button::new(default, None::<Button>, None::<Button>, None::<Button>, callback, false))
    }
}