use ratatui::prelude::*;
use ratatui::widgets::{Block, Borders, Paragraph};
use super::{Component, EventContext, RenderContext};
use crate::input::{Event, Key};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ButtonMessage {
Press,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ButtonOutput {
Pressed,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(
feature = "serialization",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct ButtonState {
label: String,
}
impl ButtonState {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
}
}
pub fn label(&self) -> &str {
&self.label
}
pub fn set_label(&mut self, label: impl Into<String>) {
self.label = label.into();
}
pub fn with_label(mut self, label: impl Into<String>) -> Self {
self.label = label.into();
self
}
pub fn update(&mut self, msg: ButtonMessage) -> Option<ButtonOutput> {
Button::update(self, msg)
}
}
pub struct Button;
impl Component for Button {
type State = ButtonState;
type Message = ButtonMessage;
type Output = ButtonOutput;
fn init() -> Self::State {
ButtonState::default()
}
fn update(_state: &mut Self::State, msg: Self::Message) -> Option<Self::Output> {
match msg {
ButtonMessage::Press => Some(ButtonOutput::Pressed),
}
}
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(ButtonMessage::Press),
_ => None,
}
} else {
None
}
}
fn view(state: &Self::State, ctx: &mut RenderContext<'_, '_>) {
let style = if ctx.disabled {
ctx.theme.disabled_style()
} else if ctx.focused {
ctx.theme.focused_style()
} else {
ctx.theme.normal_style()
};
let border_style = if ctx.focused && !ctx.disabled {
ctx.theme.focused_border_style()
} else {
ctx.theme.border_style()
};
let paragraph = Paragraph::new(state.label.as_str())
.style(style)
.alignment(Alignment::Center)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(border_style),
);
let annotation =
crate::annotation::Annotation::button("button").with_label(state.label.as_str());
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;