use std::cell::Cell; use frui::{ api::events::{Event, WidgetEvent}, prelude::*, }; const WIDTH: f64 = 60.0; const HEIGHT: f64 = 60.0; const COLOR: Color = Color::rgb8(255, 144, 54); #[derive(SingleChildWidget)] pub struct Button { pub label: L, pub on_click: F, } impl SingleChildWidget for Button { fn build<'w>(&'w self, _: BuildContext<'w, Self>) -> Self::Widget<'w> { Center { child: &self.label } } fn layout(&self, ctx: RenderContext, _: Constraints) -> Size { ctx.child().layout(Constraints { min_width: 0., max_width: WIDTH, min_height: 0., max_height: HEIGHT, }); Size { width: WIDTH, height: HEIGHT, } } fn paint(&self, ctx: RenderContext, canvas: &mut PaintContext, offset: &Offset) { let color; let hover_shade = 0.2; if ctx.rstate().is_hovered.get() { let (mut r, mut g, mut b, a) = COLOR.as_rgba8(); // User-chosen color darkened. r = (r as f32 * (1. - hover_shade)) as u8; g = (g as f32 * (1. - hover_shade)) as u8; b = (b as f32 * (1. - hover_shade)) as u8; color = Color::rgba8(r, g, b, a); } else { color = COLOR.clone(); } let brush = &canvas.solid_brush(color); PietRenderContext::fill( canvas, RoundedRect::new(offset.x, offset.y, offset.x + WIDTH, offset.y + HEIGHT, 15.), brush, ); ctx.child().paint(canvas, offset) } } pub struct ButtonRenderState { pub(crate) is_pressed: Cell, pub(crate) is_hovered: Cell, } impl RenderState for Button { type State = ButtonRenderState; fn create_state(&self) -> Self::State { ButtonRenderState { is_pressed: Cell::new(false), is_hovered: Cell::new(false), } } } impl WidgetEvent for Button { fn handle_event(&self, ctx: RenderContext, event: &Event) -> bool { match event { Event::MouseDown(e) => { if let MouseButton::Left = e.button { if ctx.point_in_layout_bounds(e.pos) { ctx.rstate().is_pressed.set(true); } } } Event::MouseUp(e) => { if let MouseButton::Left = e.button { if ctx.rstate().is_pressed.replace(false) { // Call user-defined callback. (self.on_click)(); } } } Event::MouseMove(e) => { if ctx.point_in_layout_bounds(e.pos) { if !ctx.rstate().is_hovered.replace(true) { // Repaint only if the hover state changed. ctx.schedule_layout(); } } else { if ctx.rstate().is_hovered.replace(false) { // Repaint only if the hover state changed. ctx.schedule_layout(); } } } _ => {} }; if let Event::MouseDown(_) | Event::MouseUp(_) | Event::MouseMove(_) = event { true } else { false } } }