use kahlo::prelude::*; use crate::{ component::Component, input::MouseButton, layout::{LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy}, ui::UIHandle, }; use super::{Label, Widget}; enum ButtonState { Idle, Hovered, Clicked, } pub struct Button<C: Component> { layout: LayoutNode, label: Label<C>, state: ButtonState, hook: Option<Box<dyn Fn() -> Option<C::Msg>>>, } impl<C: Component> Button<C> { pub fn new(uih: &UIHandle) -> Self { let mut layout = uih.new_layout_node(); layout .set_width_policy(SizePolicy::expands(1)) .set_height_policy(SizePolicy { minimum: uih.theme().ui_font_size as usize, desired: (uih.theme().ui_font_size * 1.5) as usize, slack_weight: 0, }); Self { layout, label: Label::new(uih), state: ButtonState::Idle, hook: None, } } pub fn set_label(&mut self, label: &str) { self.label.set_text(label); } pub fn set_hook(&mut self, to: Box<dyn Fn() -> Option<C::Msg>>) { self.hook = Some(to); } } impl<C: Component> LayoutNodeContainer for Button<C> { fn layout_node(&self) -> &LayoutNode { &self.layout } fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> { if ndx == 0 { Some(self.label.layout_node()) } else { None } } fn layout_child_count(&self) -> usize { 1 } } impl<C: Component> Widget<C> for Button<C> { fn poll( &mut self, uih: &mut UIHandle, input_state: Option<&crate::input::InputState>, ) -> Vec<<C as Component>::Msg> { let mut result: Vec<<C as Component>::Msg> = vec![]; if let (Some(istate), Some(area)) = (input_state, self.layout.render_area()) { if area.contains_inclusive(istate.mouse.pos) { println!("cursor on button"); if istate.mouse.buttons.active(MouseButton::Left) { self.state = ButtonState::Clicked; } else if istate.mouse.released().active(MouseButton::Left) { self.state = ButtonState::Idle; result.extend(self.hook.as_ref().map(|v| v()).flatten().into_iter()); } else { self.state = ButtonState::Hovered; } } else { self.state = ButtonState::Idle; } } result.extend(self.label.poll(uih, input_state).into_iter()); result } fn layout_node(&self) -> LayoutNodeAccess { LayoutNodeAccess::new(self) } fn layout_node_mut(&mut self) -> &mut LayoutNode { &mut self.layout } fn render(&self, theme: &crate::theme::Theme, target: &mut kahlo::RgbaBitmap) { let colour = match self.state { ButtonState::Idle => theme.background, ButtonState::Hovered => theme.panel, ButtonState::Clicked => theme.foreground, }; target.fill_region(self.layout.render_area().unwrap(), colour); self.label.render(theme, target); } }