domi-egui 0.1.0

DOMicile 15-primitive widget library for egui (Phase 3c).
use crate::theme::Theme;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ButtonVariant {
    Primary,
    Ghost,
    Danger,
}

impl Default for ButtonVariant {
    fn default() -> Self { Self::Primary }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ButtonSize {
    Sm,
    Lg,
}

impl Default for ButtonSize {
    fn default() -> Self { Self::Lg }
}

pub struct ButtonProps<'a> {
    pub label: &'a str,
    pub variant: ButtonVariant,
    pub size: ButtonSize,
    pub on_click: Option<Box<dyn FnMut()>>,
    pub disabled: bool,
}

impl<'a> ButtonProps<'a> {
    pub fn new(label: &'a str) -> Self {
        Self {
            label,
            variant: ButtonVariant::default(),
            size: ButtonSize::default(),
            on_click: None,
            disabled: false,
        }
    }
}

pub fn domi_button(ui: &mut egui::Ui, props: ButtonProps) -> egui::Response {
    let ButtonProps { label, variant, size, on_click, disabled } = props;
    let theme = Theme::default();

    let fill = match variant {
        ButtonVariant::Primary => {
            theme.primary.gradient_stops.first().copied()
                .unwrap_or(theme.text_default)
        }
        ButtonVariant::Ghost => theme.surface_glass,
        ButtonVariant::Danger => egui::Color32::from_rgb(0xf4, 0x97, 0x8e),
    };

    let pad_y = match size {
        ButtonSize::Sm => theme.space.0,
        ButtonSize::Lg => theme.space.1,
    };
    let desired_h = ui.spacing().interact_size.y + pad_y * 2.0;
    let (rect, response) = ui.allocate_exact_size(
        egui::vec2((label.len() as f32 * 8.0).max(80.0), desired_h),
        egui::Sense::click(),
    );
    if ui.is_rect_visible(rect) {
        let painter = ui.painter_at(rect);
        painter.rect_filled(rect, theme.radius.1, fill);
        painter.rect_stroke(
            rect,
            theme.radius.1,
            egui::Stroke::new(1.0, theme.text_default),
            egui::StrokeKind::Inside,
        );
        let text_color = match variant {
            ButtonVariant::Primary | ButtonVariant::Danger => theme.text_inverse,
            ButtonVariant::Ghost => theme.text_default,
        };
        painter.text(
            rect.center(),
            egui::Align2::CENTER_CENTER,
            label,
            egui::FontId::new(theme.space.1.max(10.0), theme.body_font),
            text_color,
        );
    }
    if response.clicked() {
        if let Some(mut cb) = on_click {
            cb();
        }
    }
    if disabled {
        ui.add_enabled_ui(false, |_| {});
    }
    response
}