1use embedded_graphics::{
2 mono_font::{
3 MonoTextStyleBuilder,
4 ascii::{FONT_7X14, FONT_9X18_BOLD},
5 },
6 pixelcolor::Rgb565,
7 prelude::*,
8 primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, RoundedRectangle},
9 text::{Alignment, Baseline, Text, TextStyleBuilder},
10};
11
12use crate::alert_layout::{alert_layout, alert_layout_in_panel, button_frames, message_top};
13use crate::{
14 Button, ButtonKind, ButtonSpec, ButtonTouchResponse, ButtonTouchState, FsTheme, I18n,
15 TouchEvent,
16};
17
18use super::{AlertButtonRole, AlertKind, AlertSpec};
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct AlertView<'a, const N: usize> {
23 pub spec: AlertSpec<'a, N>,
25 touch_state: ButtonTouchState<u8>,
26}
27
28impl<'a, const N: usize> AlertView<'a, N> {
29 pub const fn new(spec: AlertSpec<'a, N>) -> Self {
31 Self {
32 spec,
33 touch_state: ButtonTouchState::new(),
34 }
35 }
36
37 pub fn clear_touch_state(&mut self) {
39 self.touch_state.clear();
40 }
41
42 pub fn panel(&self, bounds: Rectangle, theme: &FsTheme, i18n: &I18n<'a>) -> Rectangle {
44 alert_layout::<N>(bounds, theme, i18n.text(self.spec.message)).panel
45 }
46
47 pub fn handle_touch(&mut self, touch: TouchEvent, panel: Rectangle) -> ButtonTouchResponse<u8> {
49 let buttons = self.buttons(panel);
50 self.touch_state.handle_touch(touch, &buttons)
51 }
52
53 pub fn draw<D>(&self, display: &mut D, panel: Rectangle, theme: &FsTheme, i18n: &I18n<'a>)
55 where
56 D: embedded_graphics::draw_target::DrawTarget<Color = Rgb565>,
57 {
58 self.draw_state(display, panel, theme, i18n);
59 }
60
61 pub fn draw_state<D>(&self, display: &mut D, panel: Rectangle, theme: &FsTheme, i18n: &I18n<'a>)
63 where
64 D: embedded_graphics::draw_target::DrawTarget<Color = Rgb565>,
65 {
66 let layout = alert_layout_in_panel::<N>(panel, i18n.text(self.spec.message));
67 RoundedRectangle::with_equal_corners(
68 Rectangle::new(panel.top_left + Point::new(6, 8), panel.size),
69 Size::new(22, 22),
70 )
71 .into_styled(PrimitiveStyle::with_fill(theme.dim))
72 .draw(display)
73 .ok();
74
75 let panel_style = PrimitiveStyleBuilder::new()
76 .fill_color(theme.surface)
77 .stroke_color(theme.surface_alt)
78 .stroke_width(2)
79 .build();
80 RoundedRectangle::with_equal_corners(panel, Size::new(22, 22))
81 .into_styled(panel_style)
82 .draw(display)
83 .ok();
84
85 let title_style = MonoTextStyleBuilder::new()
86 .font(&FONT_9X18_BOLD)
87 .text_color(kind_color(self.spec.kind, theme))
88 .build();
89 let message_style = MonoTextStyleBuilder::new()
90 .font(&FONT_7X14)
91 .text_color(theme.text_secondary)
92 .build();
93 let text_style = TextStyleBuilder::new()
94 .alignment(Alignment::Center)
95 .baseline(Baseline::Middle)
96 .build();
97
98 Text::with_text_style(
99 i18n.text(self.spec.title),
100 panel.center() + Point::new(0, -40),
101 title_style,
102 text_style,
103 )
104 .draw(display)
105 .ok();
106 for (index, line) in layout.message_lines.iter().enumerate() {
107 Text::with_text_style(
108 line,
109 Point::new(panel.center().x, message_top(panel) + (index as i32 * 18)),
110 message_style,
111 text_style,
112 )
113 .draw(display)
114 .ok();
115 }
116
117 for button in self.buttons(panel) {
118 button.draw_state(
119 display,
120 theme,
121 i18n,
122 self.touch_state.is_highlighted(&button),
123 );
124 }
125 }
126
127 fn buttons(&self, panel: Rectangle) -> [Button<'a, u8>; N] {
128 core::array::from_fn(|index| {
129 let action = self.spec.actions[index];
130 Button::new(
131 button_frames::<N>(panel)[index],
132 ButtonSpec {
133 key: action.id,
134 icon: None,
135 label: action.label,
136 kind: button_kind(action.role),
137 },
138 )
139 })
140 }
141}
142
143fn kind_color(kind: AlertKind, theme: &FsTheme) -> Rgb565 {
144 match kind {
145 AlertKind::Error => theme.danger,
146 AlertKind::Warning => theme.warning,
147 AlertKind::Confirm => theme.accent,
148 }
149}
150
151fn button_kind(role: AlertButtonRole) -> ButtonKind {
152 match role {
153 AlertButtonRole::Primary => ButtonKind::Primary,
154 AlertButtonRole::Secondary => ButtonKind::Secondary,
155 AlertButtonRole::Destructive => ButtonKind::Destructive,
156 }
157}