mod palette;
use embedded_graphics::{
mono_font::{
MonoTextStyleBuilder,
ascii::{FONT_7X14, FONT_9X18_BOLD},
},
pixelcolor::Rgb565,
prelude::*,
primitives::{PrimitiveStyleBuilder, Rectangle, RoundedRectangle},
text::{Alignment, Baseline, Text, TextStyleBuilder},
};
use crate::{ButtonTouchResponse, FsTheme, I18n, Localized, TouchEvent, TouchPhase};
use palette::{background, border, foreground};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ButtonKind {
Primary,
Secondary,
Destructive,
Ghost,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ButtonSpec<'a, K> {
pub key: K,
pub icon: Option<&'a str>,
pub label: Localized<'a>,
pub kind: ButtonKind,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Button<'a, K> {
pub frame: Rectangle,
pub spec: ButtonSpec<'a, K>,
touch_active: bool,
highlighted: bool,
}
impl<'a, K: Copy> Button<'a, K> {
pub const fn new(frame: Rectangle, spec: ButtonSpec<'a, K>) -> Self {
Self {
frame,
spec,
touch_active: false,
highlighted: false,
}
}
pub fn set_frame(&mut self, frame: Rectangle) {
self.frame = frame;
}
pub fn clear_touch_state(&mut self) {
self.touch_active = false;
self.highlighted = false;
}
pub const fn is_highlighted(&self) -> bool {
self.highlighted
}
pub fn draw<D>(&self, display: &mut D, theme: &FsTheme, i18n: &I18n<'a>)
where
D: DrawTarget<Color = Rgb565>,
{
self.draw_state(display, theme, i18n, self.is_highlighted());
}
pub fn handle_touch(&mut self, touch: TouchEvent) -> ButtonTouchResponse<K> {
match touch.phase {
TouchPhase::Start => self.start_touch(touch),
TouchPhase::Move => self.move_touch(touch),
TouchPhase::End => self.finish_touch(touch),
TouchPhase::Cancel => self.cancel_touch(),
}
}
pub fn draw_state<D>(
&self,
display: &mut D,
theme: &FsTheme,
i18n: &I18n<'a>,
highlighted: bool,
) where
D: DrawTarget<Color = Rgb565>,
{
let style = PrimitiveStyleBuilder::new()
.fill_color(background(self.spec.kind, theme, highlighted))
.stroke_color(border(self.spec.kind, theme, highlighted))
.stroke_width(if highlighted { 3 } else { 2 })
.build();
RoundedRectangle::with_equal_corners(self.frame, Size::new(18, 18))
.into_styled(style)
.draw(display)
.ok();
let label_style = MonoTextStyleBuilder::new()
.font(&FONT_7X14)
.text_color(foreground(self.spec.kind, theme, highlighted))
.build();
let icon_style = MonoTextStyleBuilder::new()
.font(&FONT_9X18_BOLD)
.text_color(foreground(self.spec.kind, theme, highlighted))
.build();
let text_style = TextStyleBuilder::new()
.alignment(Alignment::Center)
.baseline(Baseline::Middle)
.build();
let mut label_center = self.frame.center();
if let Some(icon) = self.spec.icon {
Text::with_text_style(
icon,
self.frame.center() + Point::new(0, -10),
icon_style,
text_style,
)
.draw(display)
.ok();
label_center += Point::new(0, 14);
}
Text::with_text_style(
i18n.text(self.spec.label),
label_center,
label_style,
text_style,
)
.draw(display)
.ok();
}
fn start_touch(&mut self, touch: TouchEvent) -> ButtonTouchResponse<K> {
let was_highlighted = self.highlighted;
if touch.within(self.frame) {
self.touch_active = true;
self.highlighted = true;
return ButtonTouchResponse {
action: None,
captured: true,
redraw: !was_highlighted,
};
}
self.clear_touch_state();
ButtonTouchResponse {
action: None,
captured: false,
redraw: was_highlighted,
}
}
fn move_touch(&mut self, touch: TouchEvent) -> ButtonTouchResponse<K> {
if !self.touch_active {
return ButtonTouchResponse {
action: None,
captured: false,
redraw: false,
};
}
let highlighted = touch.within(self.frame);
let redraw = highlighted != self.highlighted;
self.highlighted = highlighted;
ButtonTouchResponse {
action: None,
captured: true,
redraw,
}
}
fn finish_touch(&mut self, touch: TouchEvent) -> ButtonTouchResponse<K> {
if !self.touch_active {
return ButtonTouchResponse {
action: None,
captured: false,
redraw: false,
};
}
let action = (self.highlighted && touch.within(self.frame)).then_some(self.spec.key);
let redraw = self.highlighted;
self.clear_touch_state();
ButtonTouchResponse {
action,
captured: true,
redraw,
}
}
fn cancel_touch(&mut self) -> ButtonTouchResponse<K> {
if !self.touch_active {
return ButtonTouchResponse {
action: None,
captured: false,
redraw: false,
};
}
let redraw = self.highlighted;
self.clear_touch_state();
ButtonTouchResponse {
action: None,
captured: true,
redraw,
}
}
}