use std::hash::Hash;
use crate::graphics::{
Color, HorizontalAlignment, Point, Rectangle, VerticalAlignment,
};
use crate::input::{mouse, ButtonState};
use crate::ui::core::{
Align, Element, Event, Hasher, Layout, MouseCursor, Node, Widget,
};
use crate::ui::widget::{text, Column, Row, Text};
pub struct Checkbox<Message> {
is_checked: bool,
on_toggle: Box<dyn Fn(bool) -> Message>,
label: String,
label_color: Color,
}
impl<Message> std::fmt::Debug for Checkbox<Message> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Checkbox")
.field("is_checked", &self.is_checked)
.field("label", &self.label)
.field("label_color", &self.label_color)
.finish()
}
}
impl<Message> Checkbox<Message> {
pub fn new<F>(is_checked: bool, label: &str, f: F) -> Self
where
F: 'static + Fn(bool) -> Message,
{
Checkbox {
is_checked,
on_toggle: Box::new(f),
label: String::from(label),
label_color: Color::WHITE,
}
}
pub fn label_color(mut self, color: Color) -> Self {
self.label_color = color;
self
}
}
impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>
where
Renderer: self::Renderer + text::Renderer,
{
fn node(&self, renderer: &Renderer) -> Node {
Row::<(), Renderer>::new()
.spacing(15)
.align_items(Align::Center)
.push(Column::new().width(28).height(28))
.push(Text::new(&self.label))
.node(renderer)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
match event {
Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left,
state: ButtonState::Pressed,
}) => {
let mouse_over = layout
.children()
.any(|child| child.bounds().contains(cursor_position));
if mouse_over {
messages.push((self.on_toggle)(!self.is_checked));
}
}
_ => {}
}
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
let children: Vec<_> = layout.children().collect();
let text_bounds = children[1].bounds();
text::Renderer::draw(
renderer,
text_bounds,
&self.label,
20.0,
self.label_color,
HorizontalAlignment::Left,
VerticalAlignment::Top,
);
self::Renderer::draw(
renderer,
cursor_position,
children[0].bounds(),
text_bounds,
self.is_checked,
)
}
fn hash(&self, state: &mut Hasher) {
self.label.hash(state);
}
}
pub trait Renderer {
fn draw(
&mut self,
cursor_position: Point,
bounds: Rectangle<f32>,
label_bounds: Rectangle<f32>,
is_checked: bool,
) -> MouseCursor;
}
impl<'a, Message, Renderer> From<Checkbox<Message>>
for Element<'a, Message, Renderer>
where
Renderer: self::Renderer + text::Renderer,
Message: 'static,
{
fn from(checkbox: Checkbox<Message>) -> Element<'a, Message, Renderer> {
Element::new(checkbox)
}
}