1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Canvas: an owned [`CanvasBuffer`] the host draws into, blitted to the
//! screen by the passive [`Canvas`] widget.
//!
//! Because widgets are rebuilt every frame, the mutable pixel state lives on
//! the host as a [`CanvasBuffer`] field. The host mutates it in `update`
//! (here a button cycles through a few drawings) and lends it to the `Canvas`
//! widget by reference in `view`.
extern crate alloc;
use zest::prelude::*;
use zest::zest_theme::theme::dark;
const CANVAS_W: u32 = 220;
const CANVAS_H: u32 = 160;
#[derive(Clone)]
enum Msg {
Next,
}
struct Screen {
theme: Theme<'static, Rgb565>,
buffer: CanvasBuffer<Rgb565>,
pattern: u8,
}
impl Screen {
fn new() -> Self {
let mut s = Self {
theme: convert_theme(&dark::THEME),
buffer: CanvasBuffer::new(CANVAS_W, CANVAS_H, Rgb565::new(0, 0, 0)),
pattern: 0,
};
s.redraw();
s
}
/// Paint the current pattern into the owned buffer.
fn redraw(&mut self) {
let p = &self.theme.palette;
self.buffer.clear(p.neutral_10);
match self.pattern % 3 {
0 => {
// Concentric rectangle frames.
let mut i = 0i32;
while i < (CANVAS_W.min(CANVAS_H) as i32) / 2 {
let color = if (i / 8) % 2 == 0 {
p.accent_blue
} else {
p.accent_green
};
self.buffer.fill_rect(
i,
i,
CANVAS_W.saturating_sub(2 * i as u32),
CANVAS_H.saturating_sub(2 * i as u32),
color,
);
i += 8;
}
}
1 => {
// A starburst of lines from the center.
let cx = CANVAS_W as i32 / 2;
let cy = CANVAS_H as i32 / 2;
let mut x = 0i32;
while x < CANVAS_W as i32 {
self.buffer.line(cx, cy, x, 0, p.accent_red);
self.buffer
.line(cx, cy, x, CANVAS_H as i32 - 1, p.accent_yellow);
x += 12;
}
}
_ => {
// A diagonal RGB565 gradient drawn pixel-by-pixel.
let mut y = 0i32;
while y < CANVAS_H as i32 {
let mut x = 0i32;
while x < CANVAS_W as i32 {
let r = (x as u32 * 31 / CANVAS_W) as u8;
let g = (y as u32 * 63 / CANVAS_H) as u8;
self.buffer.set_pixel(x, y, Rgb565::new(r, g, 12));
x += 1;
}
y += 1;
}
}
}
}
}
impl ScreenView<Rgb565, Msg> for Screen {
fn name(&self) -> &'static str {
"Canvas"
}
fn theme(&self) -> &Theme<'_, Rgb565> {
&self.theme
}
fn view(&self) -> Element<'_, Rgb565, Msg> {
let heading = Text::new("Canvas drawing")
.align_x(Horizontal::Center)
.font(self.theme.typography.heading)
.color(self.theme.background.on_base);
Column::new()
.spacing(8)
.push(heading)
.push(horizontal_divider())
.push(Canvas::new(&self.buffer))
.push(
Button::new("Next pattern")
.class(ButtonClass::Suggested)
.on_press(Msg::Next),
)
.into_element()
}
}
struct App {
screen: Screen,
}
impl Application for App {
type Message = Msg;
type Color = Rgb565;
type Screen = Screen;
fn init() -> (Self, Task<Msg>) {
(
Self {
screen: Screen::new(),
},
Task::none(),
)
}
fn update(&mut self, m: Msg) -> Task<Msg> {
match m {
Msg::Next => {
self.screen.pattern = self.screen.pattern.wrapping_add(1);
self.screen.redraw();
}
}
Task::none()
}
fn view(&self) -> &Screen {
&self.screen
}
}
#[embassy_executor::main]
async fn main(_spawner: embassy_executor::Spawner) {
zest::run::<App>("zest - Canvas").await;
}