alpha
Login
or
Join now
arthomnix.dev
/
eepy
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
fw16-epd-gui: add slider element
author
arthomnix
date
1 year ago
(Jan 14, 2025, 11:48 AM UTC)
commit
35d7a43d
35d7a43dcd209fdddccfbbd02f4db8131b6c8e0d
parent
b8e7758e
b8e7758e733a9ddd8d47af530de892d984f1b4b8
+171
-27
4 changed files
Expand all
Collapse all
Unified
Split
fw16-epd-gui
src
element
button.rs
mod.rs
slider.rs
fw16-epd-main
src
gui.rs
+10
-10
fw16-epd-gui/src/element/button.rs
Reviewed
···
22
22
23
23
#[derive(Debug, defmt::Format)]
24
24
pub struct Button<'a> {
25
25
-
rect: RoundedRectangle,
26
26
-
label: &'a str,
27
27
-
rect_style: PrimitiveStyle<BinaryColor>,
28
28
-
char_style: MonoTextStyle<'a, BinaryColor>,
29
29
-
touch_feedback: bool,
30
30
-
touch_feedback_immediate_release: bool,
25
25
+
pub rect: RoundedRectangle,
26
26
+
pub label: &'a str,
27
27
+
pub rect_style: PrimitiveStyle<BinaryColor>,
28
28
+
pub char_style: MonoTextStyle<'a, BinaryColor>,
29
29
+
pub touch_feedback: bool,
30
30
+
pub touch_feedback_immediate_release: bool,
31
31
32
32
began_click: bool,
33
33
inverted: bool,
···
88
88
true,
89
89
touch_feedback_immediate_release,
90
90
)
91
91
-
}
92
92
-
93
93
-
pub fn rect(&self) -> Rectangle {
94
94
-
self.rect.bounding_box()
95
91
}
96
92
97
93
fn invert(&mut self) {
···
171
167
}
172
168
173
169
ret
170
170
+
}
171
171
+
172
172
+
fn bounding_box(&self) -> Rectangle {
173
173
+
self.rect.into_styled(self.rect_style).bounding_box()
174
174
}
175
175
}
+6
-11
fw16-epd-gui/src/element/mod.rs
Reviewed
···
1
1
pub mod button;
2
2
+
pub mod slider;
2
3
3
4
use embedded_graphics::mono_font::ascii::FONT_10X20;
4
5
use embedded_graphics::mono_font::MonoTextStyle;
5
6
use embedded_graphics::pixelcolor::BinaryColor;
6
7
use embedded_graphics::prelude::*;
7
7
-
use embedded_graphics::primitives::{PrimitiveStyle, PrimitiveStyleBuilder};
8
8
+
use embedded_graphics::primitives::{PrimitiveStyle, PrimitiveStyleBuilder, Rectangle};
8
9
use fw16_epd_program_interface::{Event, TouchEvent};
10
10
+
use tp370pgh01::{DIM_X, DIM_Y};
9
11
use crate::draw_target::EpdDrawTarget;
10
12
11
13
pub const DEFAULT_PRIMITIVE_STYLE: PrimitiveStyle<BinaryColor> = PrimitiveStyleBuilder::new()
···
21
23
fn draw_init(&self, target: &mut EpdDrawTarget);
22
24
23
25
fn tick(&mut self, target: &mut EpdDrawTarget, ev: Event) -> Self::Output;
24
24
-
}
25
26
26
26
-
impl<T: Drawable<Color = BinaryColor>> Gui for T {
27
27
-
type Output = ();
28
28
-
29
29
-
fn draw_init(&self, target: &mut EpdDrawTarget) {
30
30
-
self.draw(target).unwrap();
31
31
-
}
32
32
-
33
33
-
fn tick(&mut self, _target: &mut EpdDrawTarget, _ev: Event) {
34
34
-
// no-op
27
27
+
// By default, assume element fills the display
28
28
+
fn bounding_box(&self) -> Rectangle {
29
29
+
Rectangle::new(Point::zero(), Size::new(DIM_X as u32, DIM_Y as u32))
35
30
}
36
31
}
+114
fw16-epd-gui/src/element/slider.rs
Reviewed
···
1
1
+
use embedded_graphics::prelude::*;
2
2
+
use embedded_graphics::geometry::Point;
3
3
+
use embedded_graphics::pixelcolor::BinaryColor;
4
4
+
use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle};
5
5
+
use fw16_epd_program_interface::{Event, TouchEventType};
6
6
+
use crate::draw_target::EpdDrawTarget;
7
7
+
use crate::element::{Gui, DEFAULT_PRIMITIVE_STYLE};
8
8
+
9
9
+
pub struct Slider {
10
10
+
pub start_point: Point,
11
11
+
pub length: i32,
12
12
+
pub min: i32,
13
13
+
pub max: i32,
14
14
+
pub value: i32,
15
15
+
16
16
+
pub line_style: PrimitiveStyle<BinaryColor>,
17
17
+
pub marker_style: PrimitiveStyle<BinaryColor>,
18
18
+
pub marker_radius: i32,
19
19
+
20
20
+
sliding: bool,
21
21
+
}
22
22
+
23
23
+
impl Slider {
24
24
+
pub fn new(start_point: Point, length: i32, min: i32, max: i32, starting_value: i32, line_style: PrimitiveStyle<BinaryColor>, marker_style: PrimitiveStyle<BinaryColor>, marker_radius: i32) -> Self {
25
25
+
Self {
26
26
+
start_point,
27
27
+
length,
28
28
+
min,
29
29
+
max,
30
30
+
value: starting_value,
31
31
+
32
32
+
line_style,
33
33
+
marker_style,
34
34
+
marker_radius,
35
35
+
36
36
+
sliding: false,
37
37
+
}
38
38
+
}
39
39
+
40
40
+
pub fn with_default_style(start_point: Point, length: i32, min: i32, max: i32, starting_value: i32) -> Self {
41
41
+
Self {
42
42
+
start_point,
43
43
+
length,
44
44
+
min,
45
45
+
max,
46
46
+
value: starting_value,
47
47
+
48
48
+
line_style: DEFAULT_PRIMITIVE_STYLE,
49
49
+
marker_style: DEFAULT_PRIMITIVE_STYLE,
50
50
+
marker_radius: 9,
51
51
+
52
52
+
sliding: false,
53
53
+
}
54
54
+
}
55
55
+
}
56
56
+
57
57
+
impl Gui for Slider {
58
58
+
type Output = bool;
59
59
+
60
60
+
fn draw_init(&self, target: &mut EpdDrawTarget) {
61
61
+
target.fill_solid(&self.bounding_box(), BinaryColor::Off).unwrap();
62
62
+
63
63
+
Line::new(self.start_point, self.start_point + Point::new(self.length, 0))
64
64
+
.into_styled(self.line_style)
65
65
+
.draw(target)
66
66
+
.unwrap();
67
67
+
68
68
+
let x = self.start_point.x + (self.length * (self.value - self.min)) / (self.max - self.min);
69
69
+
let marker_point = Point::new(x, self.start_point.y);
70
70
+
Circle::with_center(marker_point, self.marker_radius as u32 * 2)
71
71
+
.into_styled(self.marker_style)
72
72
+
.draw(target)
73
73
+
.unwrap();
74
74
+
}
75
75
+
76
76
+
fn tick(&mut self, target: &mut EpdDrawTarget, ev: Event) -> bool {
77
77
+
if let Event::Touch(ev) = ev {
78
78
+
let p = ev.eg_point();
79
79
+
80
80
+
if self.bounding_box().contains(p) && ev.ev_type == TouchEventType::Down {
81
81
+
self.sliding = true;
82
82
+
} else if ev.ev_type == TouchEventType::Up {
83
83
+
self.sliding = false;
84
84
+
}
85
85
+
86
86
+
if self.sliding {
87
87
+
if p.x >= self.start_point.x && p.x <= (self.start_point.x + self.length) {
88
88
+
let xmin = self.start_point.x;
89
89
+
let xmax = xmin + self.length;
90
90
+
91
91
+
self.value = self.min + ((self.max - self.min) * (ev.eg_point().x - xmin)) / (xmax - xmin);
92
92
+
self.draw_init(target);
93
93
+
} else if p.x < self.start_point.x {
94
94
+
self.value = self.min;
95
95
+
self.draw_init(target);
96
96
+
} else {
97
97
+
self.value = self.max;
98
98
+
self.draw_init(target);
99
99
+
}
100
100
+
101
101
+
return true;
102
102
+
}
103
103
+
}
104
104
+
105
105
+
false
106
106
+
}
107
107
+
108
108
+
fn bounding_box(&self) -> Rectangle {
109
109
+
let real_radius = self.marker_radius + self.marker_style.stroke_width as i32;
110
110
+
let top_left = self.start_point + Point::new(-real_radius, -real_radius);
111
111
+
Rectangle::new(top_left, Size::new((self.length + 2 * real_radius) as u32, 2 * real_radius as u32))
112
112
+
}
113
113
+
114
114
+
}
+41
-6
fw16-epd-main/src/gui.rs
Reviewed
···
2
2
use embedded_graphics::geometry::AnchorPoint;
3
3
use embedded_graphics::pixelcolor::BinaryColor;
4
4
use embedded_graphics::prelude::*;
5
5
-
use embedded_graphics::primitives::{Line, Rectangle};
5
5
+
use embedded_graphics::primitives::{Line, PrimitiveStyle, Rectangle};
6
6
use fw16_epd_gui::draw_target::EpdDrawTarget;
7
7
use fw16_epd_gui::element::button::Button;
8
8
use fw16_epd_gui::element::{Gui, DEFAULT_PRIMITIVE_STYLE};
9
9
+
use fw16_epd_gui::element::slider::Slider;
9
10
use fw16_epd_program_interface::{RefreshBlockMode, SafeOption, Event, TouchEventType};
10
11
use crate::{next_event, set_touch_enabled};
11
12
···
84
85
struct ScratchpadPage {
85
86
exit_button: Button<'static>,
86
87
clear_button: Button<'static>,
88
88
+
toggle_button: Button<'static>,
89
89
+
slider: Slider,
90
90
+
eraser: bool,
87
91
prev_pos: Option<Point>,
88
92
}
89
93
90
94
impl ScratchpadPage {
91
95
fn new() -> Self {
92
96
let exit_button = Button::with_default_style_auto_sized(Point::new(10, 416 - 10 - 20), "Exit", true);
97
97
+
93
98
let next_pos = exit_button
94
94
-
.rect()
99
99
+
.bounding_box()
95
100
.translate(Point::new(10, 0))
96
101
.anchor_point(AnchorPoint::TopRight);
97
102
let clear_button = Button::with_default_style_auto_sized(next_pos, "Clear", true);
98
103
104
104
+
let next_pos = clear_button
105
105
+
.bounding_box()
106
106
+
.translate(Point::new(10, 0))
107
107
+
.anchor_point(AnchorPoint::TopRight);
108
108
+
let toggle_button = Button::with_default_style_auto_sized(next_pos, "Eraser", false);
109
109
+
110
110
+
let slider = Slider::with_default_style(Point::new(20, 20), 100, 1, 10, 2);
111
111
+
99
112
Self {
100
113
exit_button,
101
114
clear_button,
115
115
+
toggle_button,
116
116
+
slider,
117
117
+
eraser: false,
102
118
prev_pos: None,
103
119
}
104
120
}
···
110
126
fn draw_init(&self, draw_target: &mut EpdDrawTarget) {
111
127
self.exit_button.draw_init(draw_target);
112
128
self.clear_button.draw_init(draw_target);
129
129
+
self.toggle_button.draw_init(draw_target);
130
130
+
self.slider.draw_init(draw_target);
113
131
}
114
132
115
133
fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output {
116
134
let mut refresh: Option<RefreshBlockMode> = None;
135
135
+
let mut handle_drawing: bool = true;
117
136
118
137
let e = self.exit_button.tick(draw_target, ev);
119
138
if e.clicked {
···
134
153
refresh = Some(RefreshBlockMode::BlockAcknowledge);
135
154
}
136
155
156
156
+
let t = self.toggle_button.tick(draw_target, ev);
157
157
+
if t.clicked {
158
158
+
self.eraser = !self.eraser;
159
159
+
self.toggle_button.label = if self.eraser { "Pen" } else { "Eraser" };
160
160
+
refresh = Some(RefreshBlockMode::NonBlocking);
161
161
+
}
162
162
+
if t.needs_refresh {
163
163
+
refresh = Some(RefreshBlockMode::BlockAcknowledge);
164
164
+
}
165
165
+
166
166
+
if self.slider.tick(draw_target, ev) {
167
167
+
refresh = Some(RefreshBlockMode::NonBlocking);
168
168
+
debug!("stroke width = {}", self.slider.value);
169
169
+
handle_drawing = false;
170
170
+
}
171
171
+
137
172
if let Event::Touch(ev) = ev {
138
138
-
if matches!(ev.ev_type, TouchEventType::Move | TouchEventType::Up) {
173
173
+
if handle_drawing && matches!(ev.ev_type, TouchEventType::Move | TouchEventType::Up) {
139
174
if let Some(prev) = self.prev_pos {
175
175
+
let style = PrimitiveStyle::with_stroke(BinaryColor::from(!self.eraser), self.slider.value as u32);
140
176
Line::new(prev, ev.eg_point())
141
141
-
.into_styled(DEFAULT_PRIMITIVE_STYLE)
177
177
+
.into_styled(style)
142
178
.draw(draw_target)
143
179
.unwrap();
144
180
145
145
-
self.exit_button.draw_init(draw_target);
146
146
-
self.clear_button.draw_init(draw_target);
181
181
+
self.draw_init(draw_target);
147
182
148
183
if refresh.is_none() {
149
184
refresh = Some(RefreshBlockMode::NonBlocking);