1use crate::rendering::common::BODY_PX;
2use crate::rendering::layout::word_wrap_bytes;
3use crate::rendering::text_render;
4
5pub fn paint_placeholder_box(
6 buf_bytes: &mut [u8],
7 screen_w: usize,
8 screen_h: usize,
9 x: usize,
10 y: usize,
11 w: usize,
12 h: usize,
13) {
14 for ry in 0..h {
15 for rx in 0..w {
16 let px = x + rx;
17 let py = y + ry;
18 if px >= screen_w || py >= screen_h {
19 continue;
20 }
21 let off = (py * screen_w + px) * 2;
22 if off + 2 > buf_bytes.len() {
23 continue;
24 }
25 let border = rx == 0 || ry == 0 || rx == w - 1 || ry == h - 1;
26 let v = if border { 0x6B4D } else { 0xEF5D };
27 buf_bytes[off] = (v & 0xff) as u8;
28 buf_bytes[off + 1] = (v >> 8) as u8;
29 }
30 }
31}
32
33pub fn fill_rounded_rect(
34 buf_bytes: &mut [u8],
35 screen_w: usize,
36 screen_h: usize,
37 x: usize,
38 y: usize,
39 w: usize,
40 h: usize,
41 fill: u16,
42 border: u16,
43 radius: usize,
44) {
45 let r = radius.min(w / 2).min(h / 2);
46 let put = |buf: &mut [u8], px: usize, py: usize, v: u16| {
47 if px >= screen_w || py >= screen_h {
48 return;
49 }
50 let off = (py * screen_w + px) * 2;
51 if off + 2 > buf.len() {
52 return;
53 }
54 buf[off] = (v & 0xff) as u8;
55 buf[off + 1] = (v >> 8) as u8;
56 };
57 for ry in 0..h {
58 for rx in 0..w {
59 let cx_edge = if rx < r {
60 r - rx
61 } else if rx >= w - r {
62 rx - (w - 1 - r)
63 } else {
64 0
65 };
66 let cy_edge = if ry < r {
67 r - ry
68 } else if ry >= h - r {
69 ry - (h - 1 - r)
70 } else {
71 0
72 };
73 let in_corner = (rx < r || rx >= w - r) && (ry < r || ry >= h - r);
74 let v = if in_corner {
75 let dist2 =
76 (cx_edge as i32) * (cx_edge as i32) + (cy_edge as i32) * (cy_edge as i32);
77 if dist2 > (r as i32) * (r as i32) {
78 continue;
79 } else if dist2 >= ((r - 1) as i32) * ((r - 1) as i32) {
80 border
81 } else {
82 fill
83 }
84 } else {
85 let on_edge = rx == 0 || ry == 0 || rx == w - 1 || ry == h - 1;
86 if on_edge {
87 border
88 } else {
89 fill
90 }
91 };
92 put(buf_bytes, x + rx, y + ry, v);
93 }
94 }
95}
96
97pub fn paint_progress_bar(
98 buf_bytes: &mut [u8],
99 screen_w: usize,
100 screen_h: usize,
101 x: usize,
102 y: usize,
103 w: usize,
104 frac: f32,
105) {
106 let bar_h = 8usize;
107 for ry in 0..bar_h {
108 let py = y + ry;
109 if py >= screen_h {
110 break;
111 }
112 for rx in 0..w {
113 let px = x + rx;
114 if px >= screen_w {
115 break;
116 }
117 let off = (py * screen_w + px) * 2;
118 if off + 2 > buf_bytes.len() {
119 continue;
120 }
121 let filled = (rx as f32 / w as f32) < frac;
122 let v = if filled { 0x1084 } else { 0xC639 };
123 buf_bytes[off] = (v & 0xff) as u8;
124 buf_bytes[off + 1] = (v >> 8) as u8;
125 }
126 }
127}
128
129pub fn measure_text(text: &str, px: f32) -> usize {
130 let space_w = text_render::word_width(" ", px);
131 let mut total = 0.0f32;
132 for (i, word) in text.split(' ').enumerate() {
133 if i > 0 {
134 total += space_w;
135 }
136 if !word.is_empty() {
137 total += text_render::word_width(word, px);
138 }
139 }
140 total as usize
141}
142
143pub const ACTION_BTN_FILL: u16 = 0x0953;
144pub const ACTION_BTN_FG: u16 = 0xFFFF;
145pub const ACTION_BTN_RADIUS: usize = 10;
146
147pub fn paint_action_button(
148 buf_bytes: &mut [u8],
149 screen_w: usize,
150 screen_h: usize,
151 x: usize,
152 y: usize,
153 w: usize,
154 h: usize,
155 label: &str,
156 px: f32,
157) {
158 fill_rounded_rect(
159 buf_bytes,
160 screen_w,
161 screen_h,
162 x,
163 y,
164 w,
165 h,
166 ACTION_BTN_FILL,
167 ACTION_BTN_FILL,
168 ACTION_BTN_RADIUS,
169 );
170 let lw = measure_text(label, px);
171 let lh = text_render::line_height(px);
172 let tx = x + (w.saturating_sub(lw)) / 2;
173 let ty = y + (h.saturating_sub(lh)) / 2;
174 text_render::blit_rgb565_color(
175 buf_bytes,
176 screen_w,
177 label,
178 px,
179 tx,
180 ty,
181 ACTION_BTN_FG,
182 screen_w,
183 screen_h,
184 );
185}
186
187pub fn paint_nav_bar(
188 buf_bytes: &mut [u8],
189 screen_w: usize,
190 screen_h: usize,
191 nav_bar_h: usize,
192 y: usize,
193 center: &str,
194 clock: &str,
195 battery: i32,
196) {
197 let h = nav_bar_h;
198 let top = y;
199 for ry in 0..h {
200 let py = top + ry;
201 if py >= screen_h {
202 break;
203 }
204 let is_edge = ry == 0;
205 for rx in 0..screen_w {
206 let off = (py * screen_w + rx) * 2;
207 if off + 2 > buf_bytes.len() {
208 break;
209 }
210 let v = if is_edge { 0x1084 } else { 0xFFFF };
211 buf_bytes[off] = (v & 0xff) as u8;
212 buf_bytes[off + 1] = (v >> 8) as u8;
213 }
214 }
215 let label_px = BODY_PX * 0.8;
216 let small_px = BODY_PX * 0.66;
217 let lh = text_render::line_height(label_px);
218 let sh = text_render::line_height(small_px);
219 let cy = top + (h.saturating_sub(lh)) / 2;
220 let sy = top + (h.saturating_sub(sh)) / 2;
221
222 let exit_w = 150usize;
223 let exit_h = 56usize.min(h - 8);
224 let exit_x = 12usize;
225 let exit_y = top + (h.saturating_sub(exit_h)) / 2;
226 paint_action_button(
227 buf_bytes, screen_w, screen_h, exit_x, exit_y, exit_w, exit_h, "Exit", label_px,
228 );
229
230 let status = if battery > 0 {
231 format!("{} {}%", clock, battery)
232 } else {
233 clock.to_string()
234 };
235 if !status.is_empty() {
236 let sw = measure_text(&status, small_px);
237 let sx = screen_w.saturating_sub(sw + 24);
238 text_render::blit_rgb565(
239 buf_bytes, screen_w, &status, small_px, sx, sy, screen_w, screen_h,
240 );
241 }
242 if !center.is_empty() {
243 let label_w = measure_text(center, label_px);
244 let cx = (screen_w / 2).saturating_sub(label_w / 2);
245 text_render::blit_rgb565(
246 buf_bytes, screen_w, center, label_px, cx, cy, screen_w, screen_h,
247 );
248 }
249}
250
251pub fn paint_wrapped_text(
252 buf_bytes: &mut [u8],
253 screen_w: usize,
254 screen_h: usize,
255 text: &str,
256 x: usize,
257 y: usize,
258 max_w: usize,
259 px: f32,
260 max_lines: usize,
261) -> usize {
262 let lines = word_wrap_bytes(text, max_w, px);
263 let lh = text_render::line_height(px);
264 let mut cy = y;
265 for (i, line) in lines.iter().enumerate() {
266 if i >= max_lines {
267 break;
268 }
269 text_render::blit_rgb565(
270 buf_bytes, screen_w, &line.text, px, x, cy, screen_w, screen_h,
271 );
272 cy += lh;
273 }
274 cy - y
275}
276
277#[cfg(test)]
278mod tests;