1use crate::geom::{Bounds, Point};
2use crate::gfx::DrawingContext;
3use crate::view::{View, ViewId};
4use crate::{Action, Callback, DrawEvent, EventType, GuiEvent, LayoutEvent, LayoutFn, Theme};
5use alloc::vec::Vec;
6use alloc::{format, vec};
7use hashbrown::HashMap;
8use log::{info, warn};
9
10#[derive(Debug)]
11pub struct Scene {
12 pub(crate) keys: HashMap<ViewId, View>,
13 children: HashMap<ViewId, Vec<ViewId>>,
14 parents: HashMap<ViewId, ViewId>,
15 pub(crate) dirty: bool,
16 pub bounds: Bounds,
17 pub dirty_rect: Bounds,
18 pub root_id: ViewId,
19 pub(crate) focused: Option<ViewId>,
20 pub layout_dirty: bool,
21}
22
23impl Scene {
24 pub fn dump(&self) {
25 info!("scene");
26 info!(
27 " dirty {} {}, focused {:?}",
28 self.dirty, self.dirty_rect, self.focused
29 );
30 self.dump_view(&self.root_id.clone(), "");
31 }
32 fn dump_view(&self, id: &ViewId, indent: &str) {
33 if let Some(view) = self.get_view(&id) {
34 info!("{indent}{id} ---");
35 info!("{indent} bounds {}", view.bounds);
37 info!("{indent} h = {:?} {:?}", view.h_flex, view.h_align);
38 info!("{indent} v = {:?} {:?}", view.v_flex, view.v_align);
39 }
40 let kids = self.get_children_ids(id);
41 for kid in kids {
42 self.dump_view(&kid, &format!("{indent} "));
43 }
44 }
45}
46
47impl Scene {
48 pub fn root_id(&self) -> ViewId {
49 self.root_id
50 }
51 pub fn set_focused(&mut self, name: &ViewId) {
52 if self.focused.is_some() {
53 let fo = self.focused.as_ref().unwrap().clone();
54 self.mark_dirty_view(&fo);
55 }
56 self.focused = Some(name.clone());
57 self.mark_dirty_view(name);
58 }
59 pub fn get_focused(&self) -> Option<ViewId> {
60 self.focused.clone()
61 }
62 pub fn is_focused(&self, name: &ViewId) -> bool {
63 self.focused.as_ref().is_some_and(|focused| focused == name)
64 }
65 pub fn is_visible(&self, name: &ViewId) -> bool {
66 if let Some(view) = self.get_view(name) {
67 view.visible
68 } else {
69 false
70 }
71 }
72 pub fn show_view(&mut self, name: &ViewId) {
73 if let Some(view) = self.get_view_mut(name) {
74 view.visible = true;
75 }
76 self.mark_dirty_view(name);
77 }
78 pub fn hide_view(&mut self, name: &ViewId) {
79 if let Some(view) = self.get_view_mut(name) {
80 view.visible = false;
81 }
82 self.mark_dirty_view(name);
83 }
84 pub fn mark_dirty_all(&mut self) {
85 self.dirty_rect = self.bounds;
86 self.dirty = true;
87 }
88 pub fn mark_dirty_view(&mut self, name: &ViewId) {
89 if let Some(view) = self.get_view(name) {
90 let global_bounds = self.get_view_global_bounds(view);
91 self.dirty_rect = self.dirty_rect.union(global_bounds);
92 self.dirty = true;
93 }
94 }
95 pub fn mark_layout_dirty(&mut self) {
96 self.layout_dirty = true;
97 self.mark_dirty_all();
98 }
99
100 pub fn get_children_ids(&self, name: &ViewId) -> Vec<ViewId> {
101 if let Some(children) = self.children.get(name) {
102 children.clone()
103 } else {
104 Vec::new()
105 }
106 }
107 pub fn get_children_ids_filtered(&self, id: &ViewId, cb: fn(&View) -> bool) -> Vec<ViewId> {
108 self.get_children_ids(id)
109 .iter()
110 .map(|kid| self.get_view(kid))
111 .flatten()
112 .filter(|v| cb(v)) .map(|v| v.name.clone())
115 .collect()
116 }
117
118 pub(crate) fn has_view(&self, name: &ViewId) -> bool {
119 self.keys.contains_key(name)
120 }
121 pub fn get_view(&self, name: &ViewId) -> Option<&View> {
122 self.keys.get(name)
123 }
124 pub fn get_view_mut(&mut self, name: &ViewId) -> Option<&mut View> {
125 self.keys.get_mut(name)
126 }
127 pub fn get_view_state<T: 'static>(&mut self, name: &ViewId) -> Option<&mut T> {
128 if let Some(view) = self.get_view_mut(name) {
129 if let Some(view) = &mut view.state {
130 return view.downcast_mut::<T>();
131 }
132 }
133 None
134 }
135 pub fn get_view_layout(&mut self, name: &ViewId) -> Option<LayoutFn> {
136 if let Some(view) = self.get_view_mut(name) {
137 return view.layout;
138 }
139 None
140 }
141 pub(crate) fn get_view_bounds(&self, name: &ViewId) -> Option<Bounds> {
142 if let Some(view) = self.get_view(name) {
143 return Some(view.bounds.clone());
144 }
145 None
146 }
147 pub(crate) fn viewcount(&self) -> usize {
148 self.keys.len()
149 }
150 pub fn remove_view(&mut self, name: &ViewId) -> Option<View> {
151 self.mark_dirty_view(name);
152 self.keys.remove(name)
153 }
154 pub fn get_parent_for_view(&self, name: &ViewId) -> Option<&ViewId> {
155 self.parents.get(name)
156 }
157 pub fn remove_view_from_parent(&mut self, parent: &ViewId, child: &ViewId) {
158 if let Some(children) = self.children.get_mut(parent) {
159 if let Some(n) = children.iter().position(|name| name == child) {
160 children.remove(n);
161 }
162 }
163 if self.parents.contains_key(child) {
164 self.parents.remove(child);
165 } else {
166 warn!("parent {parent} does not contain child {child}");
167 }
168 }
169 pub fn new_with_bounds(bounds: Bounds) -> Scene {
170 let root_id = ViewId::new("root");
171 let root = View {
172 name: root_id.clone(),
173 title: root_id.as_str().into(),
174 bounds,
175 visible: true,
176 input: None,
177 state: None,
178 layout: Some(layout_root_panel),
179 draw: Some(|e| e.ctx.fill_rect(&e.view.bounds, &e.theme.panel_bg)),
180 ..Default::default()
181 };
182 let mut keys: HashMap<ViewId, View> = HashMap::new();
183 keys.insert(root_id.clone(), root);
184 Scene {
185 bounds,
186 keys,
187 dirty: true,
188 layout_dirty: true,
189 root_id,
190 focused: None,
191 dirty_rect: bounds,
192 children: HashMap::new(),
193 parents: HashMap::new(),
194 }
195 }
196 pub fn new() -> Scene {
197 let bounds = Bounds::new(0, 0, 200, 200);
198 Self::new_with_bounds(bounds)
199 }
200 pub fn add_view(&mut self, view: View) {
201 let name = view.name.clone();
202 if self.keys.contains_key(&name) {
203 warn!("might be adding duplicate view key {name}");
204 }
205 self.keys.insert(name.clone(), view);
206 self.mark_dirty_view(&name);
207 }
208 pub fn add_view_to_root(&mut self, view: View) {
209 self.add_view_to_parent(view, &self.root_id.clone());
210 }
211 pub fn add_view_to_parent(&mut self, view: View, parent: &ViewId) {
212 if !self.children.contains_key(parent) {
213 self.children.insert(parent.clone(), vec![]);
214 }
215 self.parents.insert(view.name.clone(), parent.clone());
216 if let Some(children) = self.children.get_mut(parent) {
217 children.push(view.name.clone());
218 }
219 self.add_view(view);
220 }
221 pub fn move_view_to_parent(&mut self, child: &ViewId, parent: &ViewId) {
222 if !self.children.contains_key(parent) {
223 self.children.insert(parent.clone(), vec![]);
224 }
225 if let Some(children) = self.children.get_mut(parent) {
226 children.push(child.clone());
227 }
228 }
229 pub fn remove_parent_and_children(&mut self, name: &ViewId) {
230 let kids = self.get_children_ids(name);
231 for kid in kids {
232 self.remove_view(&kid);
233 self.remove_view_from_parent(name, &kid);
234 }
235 self.remove_view(name);
236 }
237
238 fn get_view_global_bounds(&self, view: &View) -> Bounds {
239 let mut current = &view.name;
240 let mut offset = Point::zero();
241 while let Some(parent) = self.parents.get(current) {
242 if let Some(bounds) = self.get_view_bounds(parent) {
243 offset = offset + bounds.position;
244 }
245 current = parent;
246 }
247 view.bounds + offset
248 }
249}
250
251fn layout_root_panel(pass: &mut LayoutEvent) {
252 if let Some(view) = pass.scene.get_view_mut(&pass.target) {
253 view.bounds.size.w = pass.space.w;
254 view.bounds.size.h = pass.space.h;
255 }
256 for kid in &pass.scene.get_children_ids(&pass.target) {
257 pass.layout_child(kid, pass.space);
258 }
259}
260
261pub type EventResult = (ViewId, Action);
262
263pub fn click_at(scene: &mut Scene, handlers: &Vec<Callback>, pt: Point) -> Option<EventResult> {
264 let targets = pick_at(scene, &pt);
265 if let Some((target, pt)) = targets.last() {
266 let mut event: GuiEvent = GuiEvent {
267 scene,
268 target,
269 event_type: EventType::Tap(pt.clone()),
270 action: None,
271 };
272 if let Some(view) = event.scene.get_view(target) {
273 if let Some(input) = view.input {
274 event.action = input(&mut event);
275 }
276 }
277 for cb in handlers {
278 cb(&mut event);
279 }
280 if let Some(action) = event.action {
281 return Some((target.clone(), action));
282 }
283 }
284 None
285}
286
287pub fn event_at_focused(scene: &mut Scene, event_type: &EventType) -> Option<EventResult> {
288 if scene.focused.is_some() {
289 let focused = scene.focused.as_ref().unwrap().clone();
290 let mut event: GuiEvent = GuiEvent {
291 scene,
292 target: &focused,
293 event_type: event_type.clone(),
294 action: None,
295 };
296 if let Some(view) = event.scene.get_view(&focused) {
297 if let Some(input) = view.input {
298 event.action = input(&mut event);
299 }
300 if let Some(action) = event.action {
301 return Some((focused, action));
302 }
303 }
304 }
305 None
306}
307
308type Pick = (ViewId, Point);
309
310pub fn pick_at(scene: &mut Scene, pt: &Point) -> Vec<Pick> {
311 pick_at_view(scene, pt, &scene.root_id)
312}
313
314fn pick_at_view(scene: &Scene, pt: &Point, name: &ViewId) -> Vec<Pick> {
315 let mut coll: Vec<Pick> = vec![];
316 if let Some(view) = scene.keys.get(name) {
317 if view.bounds.contains(pt) && view.visible {
318 coll.push((view.name.clone(), pt.clone()));
319 let pt2 = pt.subtract(&view.bounds.position);
320 for kid in scene.get_children_ids(&view.name) {
321 let mut coll2 = pick_at_view(scene, &pt2, &kid);
322 coll.append(&mut coll2);
323 }
324 }
325 }
326 coll
327}
328
329pub fn draw_scene(scene: &mut Scene, ctx: &mut dyn DrawingContext, theme: &Theme) {
330 if scene.dirty {
331 ctx.fill_rect(&scene.bounds, &theme.panel_bg);
332 let name = scene.root_id.clone();
333 draw_view(scene, ctx, theme, &name);
334 scene.dirty = false;
335 scene.dirty_rect = Bounds::new_empty();
336 }
337}
338
339fn draw_view(scene: &mut Scene, ctx: &mut dyn DrawingContext, theme: &Theme, name: &ViewId) {
340 let focused = &scene.focused.clone();
341 let bounds = &scene.bounds.clone();
342 if let Some(view) = scene.get_view_mut(name)
343 && view.visible
344 {
345 if let Some(draw) = view.draw {
346 let mut de: DrawEvent = DrawEvent {
347 theme,
348 view,
349 ctx,
350 focused,
351 bounds,
352 };
353 draw(&mut de);
354 }
355 }
356 if let Some(view) = scene.get_view(name) {
357 if view.visible {
359 let bounds = view.bounds;
360 ctx.translate(&bounds.position);
361 for kid in scene.get_children_ids(&view.name) {
362 draw_view(scene, ctx, theme, &kid);
363 }
364 ctx.translate(&bounds.position.negate());
365 }
366 }
367}
368
369pub fn layout_scene(scene: &mut Scene, theme: &Theme) {
370 if scene.layout_dirty {
371 let mut pass = LayoutEvent {
372 target: &scene.root_id(),
373 space: scene.bounds.size,
374 scene,
375 theme,
376 };
377 if let Some(layout) = pass.scene.get_view_layout(&pass.scene.root_id()) {
378 layout(&mut pass);
379 }
380 scene.layout_dirty = false;
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use crate::geom::Bounds;
387 use crate::scene::Scene;
388 use crate::view::ViewId;
389
390 #[test]
391 fn basic_add_remove() {
392 let mut scene: Scene = Scene::new_with_bounds(Bounds::new(0, 0, 100, 30));
393 assert_eq!(scene.viewcount(), 1);
394 let view = crate::tests::make_simple_view(&"foo".into());
395 assert_eq!(scene.viewcount(), 1);
396 scene.add_view(view);
397 assert_eq!(scene.viewcount(), 2);
398 assert!(scene.get_view(&"foo".into()).is_some());
399 let res = scene.remove_view(&"foo".into());
400 assert_eq!(res.is_some(), true);
401 assert_eq!(scene.viewcount(), 1);
402 let res2 = scene.remove_view(&"bar".into());
403 assert_eq!(res2.is_some(), false);
404 }
405 #[test]
406 fn parent_child() {
407 let mut scene: Scene = Scene::new();
408 let parent_id: ViewId = "parent".into();
409 let child_id: ViewId = "child".into();
410 let parent_view = crate::tests::make_simple_view(&parent_id);
411 scene.add_view(parent_view);
412
413 let child_view = crate::tests::make_simple_view(&child_id);
414 assert_eq!(scene.get_children_ids(&parent_id).len(), 0);
415 assert_eq!(scene.viewcount(), 2);
416 scene.add_view_to_parent(child_view, &parent_id);
417 assert_eq!(scene.get_children_ids(&parent_id).len(), 1);
418 assert_eq!(scene.get_parent_for_view(&child_id).unwrap(), &parent_id);
419 scene.remove_view_from_parent(&parent_id, &child_id);
420 assert_eq!(scene.get_children_ids(&parent_id).len(), 0);
421 assert!(scene.get_parent_for_view(&child_id).is_none());
422
423 scene.move_view_to_parent(&child_id, &parent_id);
424 assert_eq!(scene.get_children_ids(&parent_id).len(), 1);
425 let child2 = crate::tests::make_simple_view(&"child2".into());
426 scene.add_view_to_parent(child2, &parent_id);
427 assert_eq!(scene.get_children_ids(&parent_id).len(), 2);
428 assert_eq!(scene.viewcount(), 4);
429
430 scene.remove_parent_and_children(&parent_id);
431 assert_eq!(scene.get_children_ids(&parent_id).len(), 0);
432 assert_eq!(scene.viewcount(), 1);
433 }
434}