firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
1use embedded_graphics::prelude::*;
2use embedded_graphics::mono_font::MonoTextStyle;
3use embedded_graphics::pixelcolor::BinaryColor;
4use embedded_graphics::primitives::{CornerRadii, PrimitiveStyle, Rectangle, RoundedRectangle};
5use embedded_graphics::text::{Alignment, Baseline, Text, TextStyle, TextStyleBuilder};
6use embedded_graphics::text::renderer::TextRenderer;
7use eepy_sys::input::{Event, TouchEventType};
8use crate::draw_target::EpdDrawTarget;
9use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE, DEFAULT_TEXT_STYLE};
10
11const CENTRE_STYLE: TextStyle = TextStyleBuilder::new()
12 .alignment(Alignment::Center)
13 .baseline(Baseline::Middle)
14 .build();
15
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
18pub struct ButtonOutput {
19 pub clicked: bool,
20 pub needs_refresh: bool,
21}
22
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24#[derive(Debug)]
25pub struct Button<'a> {
26 pub rect: RoundedRectangle,
27 pub label: &'a str,
28 pub rect_style: PrimitiveStyle<BinaryColor>,
29 pub char_style: MonoTextStyle<'a, BinaryColor>,
30 pub touch_feedback: bool,
31 pub touch_feedback_immediate_release: bool,
32
33 began_click: bool,
34 inverted: bool,
35 should_uninvert: bool,
36}
37
38impl<'a> Button<'a> {
39 pub fn new(rect: RoundedRectangle, label: &'a str, rect_style: PrimitiveStyle<BinaryColor>, char_style: MonoTextStyle<'a, BinaryColor>, touch_feedback: bool, touch_feedback_immediate_release: bool) -> Self {
40 Self {
41 rect,
42 label,
43 rect_style,
44 char_style,
45 touch_feedback,
46 touch_feedback_immediate_release,
47 began_click: false,
48 inverted: false,
49 should_uninvert: false,
50 }
51 }
52
53 pub fn auto_sized(top_left: Point, corner_radii: CornerRadii, label: &'a str, rect_style: PrimitiveStyle<BinaryColor>, char_style: MonoTextStyle<'a, BinaryColor>, touch_feedback: bool, touch_feedback_immediate_release: bool) -> Self {
54 let size = Size::new((char_style.font.character_size.width + char_style.font.character_spacing) * (label.len() as u32 + 1), char_style.line_height());
55 Self {
56 rect: RoundedRectangle::new(Rectangle::new(top_left, size), corner_radii),
57 label,
58 rect_style,
59 char_style,
60 touch_feedback,
61 touch_feedback_immediate_release,
62 began_click: false,
63 inverted: false,
64 should_uninvert: false,
65 }
66 }
67
68 pub fn with_default_style(rect: Rectangle, label: &'a str, touch_feedback_immediate_release: bool) -> Self {
69 Self {
70 rect: RoundedRectangle::new(rect, CornerRadii::new(Size::new(3, 3))),
71 label,
72 rect_style: DEFAULT_PRIMITIVE_STYLE,
73 char_style: DEFAULT_TEXT_STYLE,
74 touch_feedback: true,
75 touch_feedback_immediate_release,
76 began_click: false,
77 inverted: false,
78 should_uninvert: false,
79 }
80 }
81
82 pub fn with_default_style_auto_sized(top_left: Point, label: &'a str, touch_feedback_immediate_release: bool) -> Self {
83 Self::auto_sized(
84 top_left,
85 CornerRadii::new(Size::new(3, 3)),
86 label,
87 DEFAULT_PRIMITIVE_STYLE,
88 DEFAULT_TEXT_STYLE,
89 true,
90 touch_feedback_immediate_release,
91 )
92 }
93
94 fn invert(&mut self) {
95 self.rect_style.fill_color = self.rect_style.fill_color.map(|c| c.invert());
96 self.char_style.text_color = self.char_style.text_color.map(|c| c.invert());
97 self.inverted = !self.inverted;
98 }
99}
100
101impl<'a> Gui for Button<'a> {
102 type Output = ButtonOutput;
103
104 fn draw_init(&self, target: &mut EpdDrawTarget) {
105 self.rect
106 .into_styled(self.rect_style)
107 .draw(target)
108 .unwrap();
109
110 Text::with_text_style(
111 self.label,
112 self.rect.bounding_box().center(),
113 self.char_style,
114 CENTRE_STYLE
115 )
116 .draw(target)
117 .unwrap();
118 }
119
120 fn tick(&mut self, target: &mut EpdDrawTarget, ev: Event) -> Self::Output {
121 let mut ret = ButtonOutput::default();
122
123 if let Event::Touch(ev) = ev {
124 if self.rect.contains(ev.eg_point()) {
125 match (self.began_click, ev.ev_type) {
126 (false, TouchEventType::Down) => {
127 self.began_click = true;
128 if self.touch_feedback {
129 self.invert();
130 self.draw_init(target);
131 ret.needs_refresh = true;
132 }
133 },
134 (true, TouchEventType::Up) => {
135 self.began_click = false;
136 if self.inverted {
137 if self.touch_feedback_immediate_release {
138 self.invert();
139 self.draw_init(target);
140 ret.needs_refresh = true;
141 } else {
142 self.should_uninvert = true;
143 }
144 }
145 ret.clicked = true;
146 },
147 _ => {},
148 }
149 } else {
150 self.began_click = false;
151 if self.inverted {
152 if self.touch_feedback_immediate_release {
153 self.invert();
154 self.draw_init(target);
155 ret.needs_refresh = true;
156 } else {
157 self.should_uninvert = true;
158 }
159 }
160 }
161 }
162
163 if ev == Event::RefreshFinished && self.should_uninvert {
164 self.should_uninvert = false;
165 self.invert();
166 self.draw_init(target);
167 ret.needs_refresh = true;
168 }
169
170 ret
171 }
172
173 fn bounding_box(&self) -> Rectangle {
174 self.rect.into_styled(self.rect_style).bounding_box()
175 }
176}