1#![cfg_attr(not(test), no_std)]
7
8extern crate alloc;
9
10#[cfg(any(
11 test,
12 feature = "gif",
13 all(feature = "jpeg", not(target_os = "none")),
14 all(feature = "png", not(target_os = "none")),
15 all(feature = "qrcode", not(target_os = "none"))
16))]
17extern crate std;
18
19use alloc::boxed::Box;
20use alloc::{rc::Rc, vec::Vec};
21use core::cell::RefCell;
22
23#[cfg(feature = "gif")]
24use rlvgl_core::gif;
25#[cfg(all(feature = "jpeg", not(target_os = "none")))]
26use rlvgl_core::jpeg;
27#[cfg(all(feature = "png", not(target_os = "none")))]
28use rlvgl_core::png;
29#[cfg(all(feature = "qrcode", not(target_os = "none")))]
30use rlvgl_core::qrcode;
31
32#[cfg(any(
33 all(feature = "png", not(target_os = "none")),
34 all(feature = "jpeg", not(target_os = "none")),
35 feature = "gif"
36))]
37use rlvgl_core::widget::Color;
38use rlvgl_core::{
39 WidgetNode,
40 application::{AppInfo, Application},
41 event::Event,
42 widget::{Rect, Widget},
43};
44#[cfg(any(
45 all(feature = "png", not(target_os = "none")),
46 all(feature = "jpeg", not(target_os = "none")),
47 feature = "gif"
48))]
49use rlvgl_widgets::image::Image;
50use rlvgl_widgets::{button::Button, container::Container, label::Label};
51
52use rlvgl_i18n::t;
53
54type WidgetHandle = Rc<RefCell<dyn Widget>>;
55type WidgetSlot = Rc<RefCell<Option<WidgetHandle>>>;
56
57#[cfg(all(feature = "png", not(target_os = "none")))]
60const PNG_LOGO: &[u8] = &[
61 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
62 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,
63 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xf8, 0xcf, 0xc0, 0x00,
64 0x00, 0x03, 0x01, 0x01, 0x00, 0xc9, 0xfe, 0x92, 0xef, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
65 0x44, 0xae, 0x42, 0x60, 0x82,
66];
67
68#[cfg(all(feature = "jpeg", not(target_os = "none")))]
69const JPEG_LOGO: &[u8] = &[
70 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01,
71 0x00, 0x01, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08,
72 0x07, 0x07, 0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0d, 0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12,
73 0x13, 0x0f, 0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c, 0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20,
74 0x22, 0x2c, 0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30, 0x31, 0x34, 0x34, 0x34, 0x1f, 0x27,
75 0x39, 0x3d, 0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x09, 0x09,
76 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0d, 0x0d, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32, 0x32, 0x32,
77 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
78 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
79 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
80 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01,
81 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
82 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
83 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04,
84 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05,
85 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1,
86 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a,
87 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38,
88 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
89 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
90 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
91 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
92 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
93 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
94 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01,
95 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5,
97 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02,
98 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61,
99 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52,
100 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a,
101 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47,
102 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
103 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86,
104 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
105 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
106 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
107 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
108 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f,
109 0x00, 0xe2, 0xe8, 0xa2, 0x8a, 0xf9, 0x93, 0xf7, 0x13, 0xff, 0xd9,
110];
111
112#[cfg(feature = "gif")]
113const GIF_LOGO: &[u8] = &[
114 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0xf0, 0x00, 0x00, 0xff, 0x00, 0x00,
115 0xff, 0xff, 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
116 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b,
117];
118
119pub struct DemoApp {
121 pending: Rc<RefCell<Vec<WidgetNode>>>,
122 to_remove: Rc<RefCell<Vec<WidgetHandle>>>,
123 counter: Rc<RefCell<u32>>,
124}
125
126impl DemoApp {
127 pub fn new() -> Self {
129 Self {
130 pending: Rc::new(RefCell::new(Vec::new())),
131 to_remove: Rc::new(RefCell::new(Vec::new())),
132 counter: Rc::new(RefCell::new(0)),
133 }
134 }
135}
136
137impl Default for DemoApp {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl Application for DemoApp {
144 fn info(&self) -> AppInfo {
145 AppInfo {
146 name: "rlvgl-demo",
147 version: "0.1.0",
148 preferred_width: 320,
149 preferred_height: 240,
150 }
151 }
152
153 fn build(&mut self, width: u32, height: u32) -> WidgetNode {
154 let w = width as i32;
155 let h = height as i32;
156 let root_w = width;
157 let root_h = height;
158
159 let button = Rc::new(RefCell::new(Button::new(
160 t!("demo.clicks_zero"),
161 Rect {
162 x: 10,
163 y: 40,
164 width: 80,
165 height: 20,
166 },
167 )));
168
169 {
170 let counter = self.counter.clone();
171 button.borrow_mut().set_on_click(move |btn: &mut Button| {
172 let mut count = counter.borrow_mut();
173 *count += 1;
174 btn.set_text(t!("demo.clicks", count = *count));
175 });
176 }
177
178 let label = Label::new(
179 t!("demo.title", version = "0.1.9"),
180 Rect {
181 x: 10,
182 y: 10,
183 width: 200,
184 height: 20,
185 },
186 );
187
188 let plugins = Rc::new(RefCell::new(Button::new(
189 t!("demo.plugins"),
190 Rect {
191 x: 100,
192 y: 40,
193 width: 80,
194 height: 20,
195 },
196 )));
197 let menu_widget: WidgetSlot = Rc::new(RefCell::new(None));
198 #[cfg(all(feature = "qrcode", not(target_os = "none")))]
199 let qr_demo: WidgetSlot = Rc::new(RefCell::new(None));
200 #[cfg(all(feature = "png", not(target_os = "none")))]
201 let png_demo: WidgetSlot = Rc::new(RefCell::new(None));
202 #[cfg(feature = "gif")]
203 let gif_demo: WidgetSlot = Rc::new(RefCell::new(None));
204 #[cfg(all(feature = "jpeg", not(target_os = "none")))]
205 let jpeg_demo: WidgetSlot = Rc::new(RefCell::new(None));
206 {
207 let pending_add = self.pending.clone();
208 let pending_rm = self.to_remove.clone();
209 let menu_ref = menu_widget.clone();
210 #[cfg(all(feature = "qrcode", not(target_os = "none")))]
211 let qr_ref = qr_demo.clone();
212 #[cfg(all(feature = "png", not(target_os = "none")))]
213 let png_ref = png_demo.clone();
214 #[cfg(feature = "gif")]
215 let gif_ref = gif_demo.clone();
216 #[cfg(all(feature = "jpeg", not(target_os = "none")))]
217 let jpeg_ref = jpeg_demo.clone();
218 let _root_w = root_w;
219 let _root_h = root_h;
220 plugins.borrow_mut().set_on_click(move |_btn: &mut Button| {
221 if let Some(menu_w) = menu_ref.borrow_mut().take() {
222 pending_rm.borrow_mut().push(menu_w);
223 } else {
224 let menu_w: WidgetHandle = Rc::new(RefCell::new(Container::new(Rect {
225 x: 10,
226 y: 70,
227 width: 100,
228 height: 170,
229 })));
230 #[allow(unused_mut)]
231 let mut children = Vec::new();
232
233 #[cfg(all(feature = "qrcode", not(target_os = "none")))]
234 {
235 let qr_button = Rc::new(RefCell::new(Button::new(
236 t!("demo.qr_code"),
237 Rect {
238 x: 20,
239 y: 80,
240 width: 80,
241 height: 20,
242 },
243 )));
244 {
245 let pending_add = pending_add.clone();
246 let pending_rm = pending_rm.clone();
247 let qr_demo = qr_ref.clone();
248 let w = root_w;
249 let h = root_h;
250 qr_button.borrow_mut().set_on_click(move |_b: &mut Button| {
251 if let Some(qr_w) = qr_demo.borrow_mut().take() {
252 pending_rm.borrow_mut().push(qr_w);
253 } else {
254 let demo = build_plugin_demo(w, h);
255 let handle = demo.widget.clone();
256 qr_demo.borrow_mut().replace(handle.clone());
257 pending_add.borrow_mut().push(demo);
258 }
259 });
260 }
261 children.push(WidgetNode {
262 widget: qr_button,
263 children: Vec::new(),
264 });
265 }
266
267 #[cfg(all(feature = "png", not(target_os = "none")))]
268 {
269 let png_button = Rc::new(RefCell::new(Button::new(
270 t!("demo.png"),
271 Rect {
272 x: 20,
273 y: 110,
274 width: 80,
275 height: 20,
276 },
277 )));
278 {
279 let pending_add = pending_add.clone();
280 let pending_rm = pending_rm.clone();
281 let png_demo = png_ref.clone();
282 let w = root_w;
283 let h = root_h;
284 png_button
285 .borrow_mut()
286 .set_on_click(move |_b: &mut Button| {
287 if let Some(png_w) = png_demo.borrow_mut().take() {
288 pending_rm.borrow_mut().push(png_w);
289 } else {
290 let demo = build_png_demo(w, h);
291 let handle = demo.widget.clone();
292 png_demo.borrow_mut().replace(handle.clone());
293 pending_add.borrow_mut().push(demo);
294 }
295 });
296 }
297 children.push(WidgetNode {
298 widget: png_button,
299 children: Vec::new(),
300 });
301 }
302
303 #[cfg(feature = "gif")]
304 {
305 let gif_button = Rc::new(RefCell::new(Button::new(
306 t!("demo.gif"),
307 Rect {
308 x: 20,
309 y: 140,
310 width: 80,
311 height: 20,
312 },
313 )));
314 {
315 let pending_add = pending_add.clone();
316 let pending_rm = pending_rm.clone();
317 let gif_demo = gif_ref.clone();
318 let w = root_w;
319 let h = root_h;
320 gif_button
321 .borrow_mut()
322 .set_on_click(move |_b: &mut Button| {
323 if let Some(gif_w) = gif_demo.borrow_mut().take() {
324 pending_rm.borrow_mut().push(gif_w);
325 } else {
326 let demo = build_gif_demo(w, h);
327 let handle = demo.widget.clone();
328 gif_demo.borrow_mut().replace(handle.clone());
329 pending_add.borrow_mut().push(demo);
330 }
331 });
332 }
333 children.push(WidgetNode {
334 widget: gif_button,
335 children: Vec::new(),
336 });
337 }
338
339 #[cfg(all(feature = "jpeg", not(target_os = "none")))]
340 {
341 let jpeg_button = Rc::new(RefCell::new(Button::new(
342 t!("demo.jpeg"),
343 Rect {
344 x: 20,
345 y: 170,
346 width: 80,
347 height: 20,
348 },
349 )));
350 {
351 let pending_add = pending_add.clone();
352 let pending_rm = pending_rm.clone();
353 let jpeg_demo = jpeg_ref.clone();
354 let w = root_w;
355 let h = root_h;
356 jpeg_button
357 .borrow_mut()
358 .set_on_click(move |_b: &mut Button| {
359 if let Some(jpeg_w) = jpeg_demo.borrow_mut().take() {
360 pending_rm.borrow_mut().push(jpeg_w);
361 } else {
362 let demo = build_jpeg_demo(w, h);
363 let handle = demo.widget.clone();
364 jpeg_demo.borrow_mut().replace(handle.clone());
365 pending_add.borrow_mut().push(demo);
366 }
367 });
368 }
369 children.push(WidgetNode {
370 widget: jpeg_button,
371 children: Vec::new(),
372 });
373 }
374
375 let menu = WidgetNode {
376 widget: menu_w.clone(),
377 children,
378 };
379 menu_ref.borrow_mut().replace(menu_w);
380 pending_add.borrow_mut().push(menu);
381 }
382 });
383 }
384
385 WidgetNode {
386 widget: Rc::new(RefCell::new(Container::new(Rect {
387 x: 0,
388 y: 0,
389 width: w,
390 height: h,
391 }))),
392 children: alloc::vec![
393 WidgetNode {
394 widget: Rc::new(RefCell::new(label)),
395 children: Vec::new(),
396 },
397 WidgetNode {
398 widget: button.clone(),
399 children: Vec::new(),
400 },
401 WidgetNode {
402 widget: plugins.clone(),
403 children: Vec::new(),
404 },
405 ],
406 }
407 }
408
409 fn after_event(&mut self, root: &Rc<RefCell<WidgetNode>>, _event: &Event) {
410 flush_pending(root, &self.pending, &self.to_remove);
411 }
412
413 fn tick(&mut self, _root: &Rc<RefCell<WidgetNode>>) {}
414
415 fn destroy(&mut self) {}
416}
417
418pub fn create_app() -> Box<dyn Application> {
420 Box::new(DemoApp::new())
421}
422
423#[unsafe(no_mangle)]
435#[allow(improper_ctypes_definitions)]
436pub extern "C" fn rlvgl_create_app() -> *mut dyn Application {
437 Box::into_raw(create_app())
438}
439
440#[unsafe(no_mangle)]
447#[allow(improper_ctypes_definitions)]
448pub unsafe extern "C" fn rlvgl_destroy_app(ptr: *mut dyn Application) {
449 if !ptr.is_null() {
450 unsafe {
451 drop(Box::from_raw(ptr));
452 }
453 }
454}
455
456#[cfg(all(feature = "qrcode", not(target_os = "none")))]
461pub fn build_plugin_demo(root_w: u32, root_h: u32) -> WidgetNode {
463 let (pixels_vec, width, _) = qrcode::generate(b"https://github.com/SoftOboros/rlvgl").unwrap();
464 let target = root_h * 2 / 3;
465 let scale = target as f32 / width as f32;
466 let new_w = target;
467 let new_h = target;
468 let mut scaled = Vec::with_capacity((new_w * new_h) as usize);
469 for y in 0..new_h {
470 for x in 0..new_w {
471 let src_x = (x as f32 / scale).floor() as usize;
472 let src_y = (y as f32 / scale).floor() as usize;
473 let idx = src_y * width as usize + src_x;
474 let color = pixels_vec
475 .get(idx)
476 .copied()
477 .unwrap_or(Color(255, 255, 255, 255));
478 scaled.push(color);
479 }
480 }
481 let pixels: &'static [Color] = Box::leak(scaled.into_boxed_slice());
482 let x_pos = (root_w - new_w) as i32;
483 let y_pos = (root_h - new_h) as i32;
484 WidgetNode {
485 widget: Rc::new(RefCell::new(Image::new(
486 Rect {
487 x: x_pos,
488 y: y_pos,
489 width: new_w as i32,
490 height: new_h as i32,
491 },
492 new_w as i32,
493 new_h as i32,
494 pixels,
495 ))),
496 children: Vec::new(),
497 }
498}
499
500#[cfg(all(feature = "png", not(target_os = "none")))]
501pub fn build_png_demo_scaled(scale: f32, root_w: u32, root_h: u32) -> WidgetNode {
503 let (pixels_vec, width, height) =
504 png::decode(PNG_LOGO).expect("failed to decode built-in PNG asset");
505
506 let mut scale = if scale.is_finite() && scale > 0.0 {
507 scale
508 } else {
509 1.0
510 };
511 scale = scale
512 .min(root_w as f32 / width as f32)
513 .min(root_h as f32 / height as f32);
514
515 let new_w = (width as f32 * scale).max(1.0).round() as u32;
516 let new_h = (height as f32 * scale).max(1.0).round() as u32;
517
518 let mut scaled = Vec::with_capacity((new_w * new_h) as usize);
519 for y in 0..new_h {
520 for x in 0..new_w {
521 let src_x = (x as f32 / scale).floor() as usize;
522 let src_y = (y as f32 / scale).floor() as usize;
523 let idx = src_y * width as usize + src_x;
524 let color = pixels_vec.get(idx).copied().unwrap_or(Color(0, 0, 0, 255));
525 scaled.push(color);
526 }
527 }
528 let pixels: &'static [Color] = Box::leak(scaled.into_boxed_slice());
529
530 let x_pos = (root_w - new_w) as i32;
531 let y_pos = (root_h - new_h) as i32;
532 WidgetNode {
533 widget: Rc::new(RefCell::new(Image::new(
534 Rect {
535 x: x_pos,
536 y: y_pos,
537 width: new_w as i32,
538 height: new_h as i32,
539 },
540 new_w as i32,
541 new_h as i32,
542 pixels,
543 ))),
544 children: Vec::new(),
545 }
546}
547
548#[cfg(all(feature = "png", not(target_os = "none")))]
549pub fn build_png_demo(root_w: u32, root_h: u32) -> WidgetNode {
551 build_png_demo_scaled(0.5, root_w, root_h)
552}
553
554#[cfg(feature = "gif")]
555pub fn build_gif_demo_scaled(scale: f32, root_w: u32, root_h: u32) -> WidgetNode {
557 let (frames, width, height) =
558 gif::decode(GIF_LOGO).expect("failed to decode built-in GIF asset");
559 let frame = &frames[0];
560 let width = width as u32;
561 let height = height as u32;
562 let mut scale = if scale.is_finite() && scale > 0.0 {
563 scale
564 } else {
565 1.0
566 };
567 scale = scale
568 .min(root_w as f32 / width as f32)
569 .min(root_h as f32 / height as f32);
570
571 let new_w = (width as f32 * scale).max(1.0).round() as u32;
572 let new_h = (height as f32 * scale).max(1.0).round() as u32;
573
574 let mut scaled = Vec::with_capacity((new_w * new_h) as usize);
575 for y in 0..new_h {
576 for x in 0..new_w {
577 let src_x = (x as f32 / scale).floor() as usize;
578 let src_y = (y as f32 / scale).floor() as usize;
579 let idx = src_y * width as usize + src_x;
580 let color = frame
581 .pixels
582 .get(idx)
583 .copied()
584 .unwrap_or(Color(0, 0, 0, 255));
585 scaled.push(color);
586 }
587 }
588 let pixels: &'static [Color] = Box::leak(scaled.into_boxed_slice());
589
590 let x_pos = (root_w - new_w) as i32;
591 let y_pos = (root_h - new_h) as i32;
592 WidgetNode {
593 widget: Rc::new(RefCell::new(Image::new(
594 Rect {
595 x: x_pos,
596 y: y_pos,
597 width: new_w as i32,
598 height: new_h as i32,
599 },
600 new_w as i32,
601 new_h as i32,
602 pixels,
603 ))),
604 children: Vec::new(),
605 }
606}
607
608#[cfg(feature = "gif")]
609pub fn build_gif_demo(root_w: u32, root_h: u32) -> WidgetNode {
611 build_gif_demo_scaled(0.5, root_w, root_h)
612}
613
614#[cfg(all(feature = "jpeg", not(target_os = "none")))]
615pub fn build_jpeg_demo_scaled(scale: f32, root_w: u32, root_h: u32) -> WidgetNode {
617 let (pixels_vec, width, height) =
618 jpeg::decode(JPEG_LOGO).expect("failed to decode built-in JPEG asset");
619
620 let width = width as u32;
621 let height = height as u32;
622 let mut scale = if scale.is_finite() && scale > 0.0 {
623 scale
624 } else {
625 1.0
626 };
627 scale = scale
628 .min(root_w as f32 / width as f32)
629 .min(root_h as f32 / height as f32);
630
631 let new_w = (width as f32 * scale).max(1.0).round() as u32;
632 let new_h = (height as f32 * scale).max(1.0).round() as u32;
633
634 let mut scaled = Vec::with_capacity((new_w * new_h) as usize);
635 for y in 0..new_h {
636 for x in 0..new_w {
637 let src_x = (x as f32 / scale).floor() as usize;
638 let src_y = (y as f32 / scale).floor() as usize;
639 let idx = src_y * width as usize + src_x;
640 let color = pixels_vec.get(idx).copied().unwrap_or(Color(0, 0, 0, 255));
641 scaled.push(color);
642 }
643 }
644 let pixels: &'static [Color] = Box::leak(scaled.into_boxed_slice());
645
646 let x_pos = (root_w - new_w) as i32;
647 let y_pos = (root_h - new_h) as i32;
648 WidgetNode {
649 widget: Rc::new(RefCell::new(Image::new(
650 Rect {
651 x: x_pos,
652 y: y_pos,
653 width: new_w as i32,
654 height: new_h as i32,
655 },
656 new_w as i32,
657 new_h as i32,
658 pixels,
659 ))),
660 children: Vec::new(),
661 }
662}
663
664#[cfg(all(feature = "jpeg", not(target_os = "none")))]
665pub fn build_jpeg_demo(root_w: u32, root_h: u32) -> WidgetNode {
667 build_jpeg_demo_scaled(0.5, root_w, root_h)
668}
669
670pub fn flush_pending(
675 root: &Rc<RefCell<WidgetNode>>,
676 pending: &Rc<RefCell<Vec<WidgetNode>>>,
677 to_remove: &Rc<RefCell<Vec<WidgetHandle>>>,
678) {
679 let mut root_ref = root.borrow_mut();
680 for handle in to_remove.borrow_mut().drain(..) {
681 root_ref
682 .children
683 .retain(|n| !Rc::ptr_eq(&n.widget, &handle));
684 }
685 let mut pending_nodes = pending.borrow_mut();
686 root_ref.children.extend(pending_nodes.drain(..));
687}