use ratatui::widgets::Paragraph;
use super::{Component, EventContext, RenderContext};
use crate::input::{Event, Key};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CheckboxMessage {
Toggle,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CheckboxOutput {
Toggled(bool),
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(
feature = "serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct CheckboxState {
label: String,
checked: bool,
}
impl CheckboxState {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
checked: false,
}
}
pub fn checked(label: impl Into<String>) -> Self {
Self {
label: label.into(),
checked: true,
}
}
pub fn label(&self) -> &str {
&self.label
}
pub fn set_label(&mut self, label: impl Into<String>) {
self.label = label.into();
}
pub fn is_checked(&self) -> bool {
self.checked
}
pub fn set_checked(&mut self, checked: bool) {
self.checked = checked;
}
pub fn with_label(mut self, label: impl Into<String>) -> Self {
self.label = label.into();
self
}
pub fn with_checked(mut self, checked: bool) -> Self {
self.checked = checked;
self
}
pub fn update(&mut self, msg: CheckboxMessage) -> Option<CheckboxOutput> {
Checkbox::update(self, msg)
}
}
pub struct Checkbox;
impl Component for Checkbox {
type State = CheckboxState;
type Message = CheckboxMessage;
type Output = CheckboxOutput;
fn init() -> Self::State {
CheckboxState::default()
}
fn update(state: &mut Self::State, msg: Self::Message) -> Option<Self::Output> {
match msg {
CheckboxMessage::Toggle => {
state.checked = !state.checked;
Some(CheckboxOutput::Toggled(state.checked))
}
}
}
fn handle_event(
_state: &Self::State,
event: &Event,
ctx: &EventContext,
) -> Option<Self::Message> {
if !ctx.focused || ctx.disabled {
return None;
}
if let Some(key) = event.as_key() {
match key.code {
Key::Enter | Key::Char(' ') => Some(CheckboxMessage::Toggle),
_ => None,
}
} else {
None
}
}
fn view(state: &Self::State, ctx: &mut RenderContext<'_, '_>) {
let check_mark = if state.checked { "x" } else { " " };
let text = format!("[{}] {}", check_mark, state.label);
let style = if ctx.disabled {
ctx.theme.disabled_style()
} else if ctx.focused {
ctx.theme.focused_style()
} else {
ctx.theme.normal_style()
};
let paragraph = Paragraph::new(text).style(style);
let annotation = crate::annotation::Annotation::checkbox("checkbox")
.with_label(state.label.as_str())
.with_selected(state.checked);
let annotated = crate::annotation::Annotate::new(paragraph, annotation)
.focused(ctx.focused)
.disabled(ctx.disabled);
ctx.frame.render_widget(annotated, ctx.area);
}
}
#[cfg(test)]
mod tests;