1#![cfg_attr(not(test), no_std)]
13#![deny(missing_docs)]
14#![cfg_attr(all(docsrs, nightly), feature(doc_cfg))]
15
16#[cfg(any(
19 test,
20 feature = "gif",
21 feature = "lottie",
22 feature = "pinyin",
23 feature = "fatfs",
24 feature = "nes",
25 feature = "apng",
26 all(feature = "fontdue", not(target_os = "none")),
27 all(feature = "jpeg", not(target_os = "none")),
28 all(feature = "png", not(target_os = "none")),
29 all(feature = "qrcode", not(target_os = "none"))
30))]
31extern crate std;
32
33extern crate alloc;
34
35pub mod anim;
37pub mod animation;
38pub mod application;
39#[cfg(feature = "fs")]
40pub mod asset;
41pub mod bitmap_font;
42pub mod cmd;
44pub mod draw;
46pub mod edit;
50pub mod event;
51pub mod focus;
53pub mod font;
55#[cfg(feature = "fs")]
56pub mod fs;
57pub mod icon_bitmap;
59pub mod image;
61pub mod interface;
62pub mod invalidation;
64pub mod layout;
66pub mod mask;
68pub mod object;
70pub mod object_anim;
72pub mod observer;
74pub mod packed_font;
76pub mod plugins;
77pub mod property;
79pub mod raster;
82pub mod renderer;
83pub mod scroll;
85pub mod style;
86pub mod style_cascade;
88pub mod theme;
89pub mod timer;
91pub mod widget;
92
93#[cfg(feature = "canvas")]
94#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
95pub use plugins::canvas;
96
97#[cfg(feature = "fatfs")]
98#[cfg_attr(docsrs, doc(cfg(feature = "fatfs")))]
99pub use plugins::fatfs;
100
101#[cfg(all(feature = "fontdue", not(target_os = "none")))]
102#[cfg_attr(docsrs, doc(cfg(feature = "fontdue")))]
103pub use plugins::fontdue;
104
105#[cfg(feature = "gif")]
106#[cfg_attr(docsrs, doc(cfg(feature = "gif")))]
107pub use plugins::gif;
108
109#[cfg(feature = "apng")]
110#[cfg_attr(docsrs, doc(cfg(feature = "apng")))]
111pub use plugins::apng;
112
113#[cfg(all(feature = "jpeg", not(target_os = "none")))]
114#[cfg_attr(docsrs, doc(cfg(feature = "jpeg")))]
115#[cfg_attr(docsrs, doc(cfg(feature = "jpeg")))]
116pub use plugins::jpeg;
117#[cfg(feature = "lottie")]
118#[cfg_attr(docsrs, doc(cfg(feature = "lottie")))]
119pub use plugins::lottie;
120
121#[cfg(feature = "nes")]
122#[cfg_attr(docsrs, doc(cfg(feature = "nes")))]
123pub use plugins::nes;
124
125#[cfg(feature = "pinyin")]
126#[cfg_attr(docsrs, doc(cfg(feature = "pinyin")))]
127pub use plugins::pinyin;
128
129#[cfg(all(feature = "png", not(target_os = "none")))]
130#[cfg_attr(docsrs, doc(cfg(feature = "png")))]
131pub use plugins::png;
132
133#[cfg(all(feature = "qrcode", not(target_os = "none")))]
134#[cfg_attr(docsrs, doc(cfg(feature = "qrcode")))]
135pub use plugins::qrcode;
136
137#[cfg(doctest)]
139doc_comment::doctest!("../../README.md");
140
141use alloc::rc::Rc;
142use alloc::vec::Vec;
143use core::cell::RefCell;
144
145pub struct WidgetNode {
151 pub widget: Rc<RefCell<dyn widget::Widget>>,
153 pub children: Vec<WidgetNode>,
155 pub tag: Option<&'static str>,
160}
161
162impl WidgetNode {
163 pub fn new(widget: Rc<RefCell<dyn widget::Widget>>) -> Self {
165 Self {
166 widget,
167 children: Vec::new(),
168 tag: None,
169 }
170 }
171
172 pub fn with_tag(mut self, tag: &'static str) -> Self {
174 self.tag = Some(tag);
175 self
176 }
177
178 pub fn dispatch_event(&mut self, event: &event::Event) -> bool {
182 if self.widget.borrow_mut().handle_event(event) {
183 return true;
184 }
185 for child in &mut self.children {
186 if child.dispatch_event(event) {
187 return true;
188 }
189 }
190 false
191 }
192
193 pub fn draw(&self, renderer: &mut dyn renderer::Renderer) {
195 self.widget.borrow().draw(renderer);
196 for child in &self.children {
197 child.draw(renderer);
198 }
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use crate::event::Event;
206 use crate::renderer::Renderer;
207 use crate::widget::{Color, Rect, Widget};
208
209 struct TestWidget {
210 name: &'static str,
211 events: alloc::vec::Vec<&'static str>,
212 handle: bool,
213 }
214
215 impl TestWidget {
216 fn new(name: &'static str) -> (Rc<RefCell<Self>>, Rc<RefCell<Self>>) {
217 let w = Rc::new(RefCell::new(Self {
218 name,
219 events: alloc::vec::Vec::new(),
220 handle: false,
221 }));
222 (w.clone(), w)
223 }
224 }
225
226 impl Widget for TestWidget {
227 fn bounds(&self) -> Rect {
228 Rect {
229 x: 0,
230 y: 0,
231 width: 0,
232 height: 0,
233 }
234 }
235 fn draw(&self, renderer: &mut dyn Renderer) {
236 renderer.draw_text((0, 0), self.name, Color(0, 0, 0, 0));
237 }
238 fn handle_event(&mut self, _event: &Event) -> bool {
239 self.events.push(self.name);
240 self.handle
241 }
242 }
243
244 struct TestRenderer(pub alloc::vec::Vec<alloc::string::String>);
245 impl Renderer for TestRenderer {
246 fn fill_rect(&mut self, _rect: Rect, _color: Color) {}
247 fn draw_text(&mut self, _position: (i32, i32), text: &str, _color: Color) {
248 self.0.push(text.to_string());
249 }
250 }
251
252 #[test]
253 fn dispatch_event_bubbles_through_children() {
254 let (root_a, _) = TestWidget::new("A");
255 let (child_b, _) = TestWidget::new("B");
256 let (child_c, _) = TestWidget::new("C");
257
258 let mut tree = WidgetNode {
259 widget: root_a,
260 children: alloc::vec![
261 WidgetNode {
262 widget: child_b.clone(),
263 children: alloc::vec![],
264 tag: None,
265 },
266 WidgetNode {
267 widget: child_c.clone(),
268 children: alloc::vec![],
269 tag: None,
270 },
271 ],
272 tag: None,
273 };
274
275 let consumed = tree.dispatch_event(&Event::Tick);
276 assert!(!consumed, "no widget indicates it handled the event");
277
278 let b = child_b.borrow();
279 let c = child_c.borrow();
280 assert_eq!(b.events, alloc::vec!["B"], "child B saw one event");
281 assert_eq!(c.events, alloc::vec!["C"], "child C saw one event");
282 }
283
284 #[test]
285 fn draw_preorder_parent_before_children() {
286 let (root_a, root_ref) = TestWidget::new("A");
287 let (child_b, _) = TestWidget::new("B");
288 let (child_c, _) = TestWidget::new("C");
289
290 let tree = WidgetNode {
291 widget: root_a,
292 children: alloc::vec![
293 WidgetNode {
294 widget: child_b,
295 children: alloc::vec![],
296 tag: None,
297 },
298 WidgetNode {
299 widget: child_c,
300 children: alloc::vec![],
301 tag: None,
302 },
303 ],
304 tag: None,
305 };
306
307 let mut renderer = TestRenderer(alloc::vec::Vec::new());
308 tree.draw(&mut renderer);
309 assert_eq!(
310 renderer.0,
311 alloc::vec![
312 alloc::string::String::from("A"),
313 alloc::string::String::from("B"),
314 alloc::string::String::from("C"),
315 ],
316 "preorder draw order"
317 );
318
319 assert!(root_ref.borrow().events.is_empty());
321 }
322}