use crate::input::{Action, FocusEvent, ModKeys, MouseEvent};
use crate::layout::{Cells, Dim, LengthBound, Pos};
use crate::text::{IntoGlyph, StyleGroup, Theme, WidgetGroup};
use crate::{Result, Widget};
use std::cell::Cell;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum State {
Disabled,
Enabled,
Hovered,
Focused,
Pressed,
}
pub struct Button<W: Widget> {
wrapped: W,
state: Cell<State>,
}
impl<W: Widget> Button<W> {
pub fn new(wrapped: W) -> Self {
let state = Cell::new(State::Enabled);
Self { wrapped, state }
}
pub fn disable(&self) {
self.state.set(State::Disabled);
}
pub fn enable(&self) {
if self.state.get() == State::Disabled {
self.state.set(State::Enabled);
}
}
}
impl<W: Widget> Widget for Button<W> {
fn widget_group(&self) -> WidgetGroup {
WidgetGroup::Button
}
fn style_group(&self) -> StyleGroup {
match self.state.get() {
State::Disabled => StyleGroup::Disabled,
State::Enabled => StyleGroup::Enabled,
State::Focused => StyleGroup::Focused,
State::Hovered => StyleGroup::Hovered,
State::Pressed => StyleGroup::Interacted,
}
}
fn width_bounds(&self, theme: &Theme) -> LengthBound {
self.wrapped.width_bounds(theme)
}
fn height_bounds(&self, theme: &Theme, width: u16) -> LengthBound {
self.wrapped.height_bounds(theme, width)
}
fn draw(&self, cells: &mut Cells, offset: Pos) -> Result<()> {
cells.fill(&' '.into_glyph()?)?;
self.wrapped.draw(cells, offset)
}
fn focus(&self, fev: FocusEvent) -> Option<Action> {
use FocusEvent::*;
use State::*;
let state = self.state.get();
match (fev, state) {
(_, Disabled) => Some(Disabled),
(Offer, _) => Some(Focused),
(Take, _) => Some(Enabled),
(HoverInside, Enabled) => Some(Hovered),
(HoverInside, Pressed) => Some(Focused),
(HoverOutside, Hovered) => Some(Enabled),
(HoverOutside, Pressed) => Some(Focused),
_ => None,
}
.and_then(|st| {
if st != self.state.get() {
self.state.set(st);
Some(Action::Redraw())
} else {
None
}
})
}
fn mouse_event(
&self,
mev: MouseEvent,
_mods: ModKeys,
_dim: Dim,
_pos: Pos,
) -> Option<Action> {
let state = self.state.get();
match (mev, state) {
(_, State::Disabled) => None,
(MouseEvent::ButtonDown(_), _) => Some(State::Pressed),
_ => None,
}
.and_then(|s| {
if s != state {
self.state.set(s);
Some(Action::Redraw())
} else {
None
}
})
}
}