1use super::*;
2
3fn collect_focusable(
6 widget: &dyn Widget,
7 current_path: &mut Vec<usize>,
8 out: &mut Vec<Vec<usize>>,
9) {
10 if widget.is_focusable() {
11 out.push(current_path.clone());
12 }
13 for (i, child) in widget.children().iter().enumerate() {
14 current_path.push(i);
15 collect_focusable(child.as_ref(), current_path, out);
16 current_path.pop();
17 }
18}
19
20fn widget_at_path<'a>(root: &'a mut Box<dyn Widget>, path: &[usize]) -> &'a mut dyn Widget {
22 if path.is_empty() {
23 return root.as_mut();
24 }
25 let idx = path[0];
26 widget_at_path(&mut root.children_mut()[idx], &path[1..])
27}
28
29fn widget_at_path_ref<'a>(root: &'a dyn Widget, path: &[usize]) -> &'a dyn Widget {
30 if path.is_empty() {
31 return root;
32 }
33 let idx = path[0];
34 widget_at_path_ref(root.children()[idx].as_ref(), &path[1..])
35}
36
37pub struct App {
46 root: Box<dyn Widget>,
47 focus: Option<Vec<usize>>,
50 hovered: Option<Vec<usize>>,
52 captured: Option<Vec<usize>>,
56 viewport_height: f64,
58 viewport_size: Size,
60 global_key_handler: Option<Box<dyn FnMut(Key, Modifiers) -> bool>>,
63 touch_state: crate::touch_state::TouchState,
67}
68
69impl App {
70 pub fn new(root: Box<dyn Widget>) -> Self {
72 Self {
73 root,
74 focus: None,
75 hovered: None,
76 captured: None,
77 viewport_height: 1.0,
78 viewport_size: Size::new(1.0, 1.0),
79 global_key_handler: None,
80 touch_state: crate::touch_state::TouchState::new(),
81 }
82 }
83
84 pub fn root(&self) -> &dyn Widget {
89 self.root.as_ref()
90 }
91
92 pub fn root_mut(&mut self) -> &mut dyn Widget {
96 self.root.as_mut()
97 }
98
99 pub fn focused_widget_type_name(&self) -> Option<&'static str> {
101 self.focus
102 .as_deref()
103 .map(|path| widget_at_path_ref(self.root.as_ref(), path).type_name())
104 }
105
106 pub fn set_global_key_handler(
120 &mut self,
121 handler: impl FnMut(Key, Modifiers) -> bool + 'static,
122 ) {
123 self.global_key_handler = Some(Box::new(handler));
124 }
125
126 pub fn layout(&mut self, viewport: Size) {
132 let scale = crate::device_scale::device_scale().max(1e-6);
133 let logical = Size::new(viewport.width / scale, viewport.height / scale);
134 self.viewport_height = logical.height;
135 self.viewport_size = logical;
136 set_current_viewport(logical);
137 self.root
138 .set_bounds(Rect::new(0.0, 0.0, logical.width, logical.height));
139 self.root.layout(logical);
140 }
141
142 pub fn paint(&mut self, ctx: &mut dyn DrawCtx) {
152 crate::animation::clear_draw_request();
153 let viewport = self.viewport_size;
154 crate::widgets::combo_box::begin_combo_popup_frame(viewport);
155 crate::widgets::tooltip::begin_tooltip_frame();
156 self.touch_state.update_gesture();
160 crate::touch_state::set_current(self.touch_state.current());
161 let scale = crate::device_scale::device_scale();
162 if (scale - 1.0).abs() > 1e-6 {
163 ctx.save();
164 ctx.scale(scale, scale);
165 paint_subtree(self.root.as_mut(), ctx);
166 crate::widgets::combo_box::paint_global_combo_popups(ctx);
167 crate::widgets::tooltip::paint_global_tooltips(ctx, viewport);
168 paint_global_overlays(self.root.as_mut(), ctx);
169 crate::widgets::combo_box::paint_global_combo_popups(ctx);
173 ctx.restore();
174 } else {
175 paint_subtree(self.root.as_mut(), ctx);
176 crate::widgets::combo_box::paint_global_combo_popups(ctx);
177 crate::widgets::tooltip::paint_global_tooltips(ctx, viewport);
178 paint_global_overlays(self.root.as_mut(), ctx);
179 crate::widgets::combo_box::paint_global_combo_popups(ctx);
180 }
181 }
182
183 pub fn wants_draw(&self) -> bool {
193 self.root.needs_draw() || crate::animation::wants_draw()
194 }
195
196 pub fn next_draw_deadline(&self) -> Option<web_time::Instant> {
201 self.root.next_draw_deadline()
202 }
203
204 pub fn on_mouse_move(&mut self, screen_x: f64, screen_y: f64) {
213 crate::cursor::reset_cursor_icon();
215 let pos = self.flip_y(screen_x, screen_y);
216 set_current_mouse_world(pos);
217 if let Some(path) = active_modal_path(self.root.as_ref()) {
218 let event = Event::MouseMove { pos };
219 dispatch_event(&mut self.root, &path, &event, pos);
220 self.hovered = Some(path);
221 return;
222 }
223 self.dispatch_mouse_move(pos);
224 }
225
226 pub fn on_mouse_down(
228 &mut self,
229 screen_x: f64,
230 screen_y: f64,
231 button: MouseButton,
232 mods: Modifiers,
233 ) {
234 let pos = self.flip_y(screen_x, screen_y);
235 set_current_mouse_world(pos);
236 let modal_path = active_modal_path(self.root.as_ref());
237 let event = Event::MouseDown {
238 pos,
239 button,
240 modifiers: mods,
241 };
242 if let Some(path) = modal_path {
243 self.set_focus(None);
244 if dispatch_event(&mut self.root, &path, &event, pos) == EventResult::Consumed {
245 self.captured = Some(path);
246 }
247 return;
248 }
249 let hit = self.compute_hit(pos);
250
251 if let Some(ref path) = hit {
253 let w = widget_at_path(&mut self.root, path);
254 if w.is_focusable() {
255 self.set_focus(Some(path.clone()));
256 } else {
257 self.set_focus(None);
258 }
259 } else {
260 self.set_focus(None);
261 }
262
263 if let Some(mut path) = hit {
264 let result = dispatch_event(&mut self.root, &path, &event, pos);
265 if result == EventResult::Consumed {
266 self.maybe_bring_to_front(&mut path);
267 let capture_path = self.compute_hit(pos).unwrap_or(path);
268 self.captured = Some(capture_path);
269 }
270 }
271 }
277
278 pub fn on_mouse_up(
280 &mut self,
281 screen_x: f64,
282 screen_y: f64,
283 button: MouseButton,
284 mods: Modifiers,
285 ) {
286 let pos = self.flip_y(screen_x, screen_y);
287 set_current_mouse_world(pos);
288 let event = Event::MouseUp {
289 pos,
290 button,
291 modifiers: mods,
292 };
293 if let Some(path) = active_modal_path(self.root.as_ref()) {
294 self.captured = None;
295 dispatch_event(&mut self.root, &path, &event, pos);
296 return;
297 }
298 if let Some(path) = self.captured.take() {
300 dispatch_event(&mut self.root, &path, &event, pos);
301 } else {
302 let hit = self.compute_hit(pos);
303 if let Some(path) = hit {
304 dispatch_event(&mut self.root, &path, &event, pos);
305 }
306 }
307 }
308
309 pub fn on_key_down(&mut self, key: Key, mods: Modifiers) {
312 if key == Key::Tab {
313 self.advance_focus(!mods.shift);
314 return;
315 }
316 let event = Event::KeyDown {
317 key: key.clone(),
318 modifiers: mods,
319 };
320 let result = if let Some(path) = active_modal_path(self.root.as_ref()) {
321 dispatch_event(&mut self.root, &path, &event, Point::ORIGIN)
322 } else if let Some(path) = self.focus.clone() {
323 dispatch_event(&mut self.root, &path, &event, Point::ORIGIN)
324 } else {
325 EventResult::Ignored
326 };
327 if result != EventResult::Consumed {
328 let result = dispatch_unconsumed_key(self.root.as_mut(), &key, mods);
329 if result != EventResult::Consumed {
330 if let Some(ref mut handler) = self.global_key_handler {
331 handler(key, mods);
332 }
333 }
334 }
335 }
336
337 pub fn on_key_up(&mut self, key: Key, mods: Modifiers) {
339 let event = Event::KeyUp {
340 key,
341 modifiers: mods,
342 };
343 if let Some(path) = self.focus.clone() {
344 dispatch_event(&mut self.root, &path, &event, Point::ORIGIN);
345 }
346 }
347
348 pub fn on_mouse_wheel(&mut self, screen_x: f64, screen_y: f64, delta_y: f64) {
351 self.on_mouse_wheel_xy_mods(screen_x, screen_y, 0.0, delta_y, Modifiers::default());
352 }
353
354 pub fn on_mouse_wheel_xy(&mut self, screen_x: f64, screen_y: f64, delta_x: f64, delta_y: f64) {
357 self.on_mouse_wheel_xy_mods(screen_x, screen_y, delta_x, delta_y, Modifiers::default());
358 }
359
360 pub fn on_mouse_wheel_xy_mods(
362 &mut self,
363 screen_x: f64,
364 screen_y: f64,
365 delta_x: f64,
366 delta_y: f64,
367 modifiers: Modifiers,
368 ) {
369 let pos = self.flip_y(screen_x, screen_y);
370 set_current_mouse_world(pos);
371 let hit = active_modal_path(self.root.as_ref()).or_else(|| self.compute_hit(pos));
372 let event = Event::MouseWheel {
373 pos,
374 delta_y,
375 delta_x,
376 modifiers,
377 };
378 if let Some(path) = hit {
379 dispatch_event(&mut self.root, &path, &event, pos);
380 }
381 }
382
383 pub fn collect_inspector_nodes(&self) -> Vec<InspectorNode> {
385 let mut out = Vec::new();
386 collect_inspector_nodes(self.root.as_ref(), 0, Point::ORIGIN, &mut out);
387 out
388 }
389
390 pub fn dump_tree_json(&self) -> String {
396 let nodes = self.collect_inspector_nodes();
397 let mut s = String::from("[\n");
398 for (i, n) in nodes.iter().enumerate() {
399 let props_json = n
400 .properties
401 .iter()
402 .map(|(k, v)| format!("{:?}: {:?}", k, v))
403 .collect::<Vec<_>>()
404 .join(", ");
405 s.push_str(&format!(
406 " {{\"type\":{:?},\"depth\":{},\"x\":{:.2},\"y\":{:.2},\"w\":{:.2},\"h\":{:.2},\"props\":{{{}}}}}",
407 n.type_name, n.depth,
408 n.screen_bounds.x, n.screen_bounds.y,
409 n.screen_bounds.width, n.screen_bounds.height,
410 props_json,
411 ));
412 if i + 1 < nodes.len() {
413 s.push(',');
414 }
415 s.push('\n');
416 }
417 s.push(']');
418 s
419 }
420
421 pub fn has_focus(&self) -> bool {
424 self.focus.is_some()
425 }
426
427 pub fn on_mouse_leave(&mut self) {
429 crate::cursor::reset_cursor_icon();
430 self.dispatch_mouse_move(Point::new(-1.0, -1.0));
431 }
432
433 pub fn on_touch_start(
442 &mut self,
443 device: crate::touch_state::TouchDeviceId,
444 id: crate::touch_state::TouchId,
445 screen_x: f64,
446 screen_y: f64,
447 force: Option<f32>,
448 ) {
449 let pos = self.flip_y(screen_x, screen_y);
450 self.touch_state.on_start(device, id, pos, force);
451 }
452 pub fn on_touch_move(
453 &mut self,
454 device: crate::touch_state::TouchDeviceId,
455 id: crate::touch_state::TouchId,
456 screen_x: f64,
457 screen_y: f64,
458 force: Option<f32>,
459 ) {
460 let pos = self.flip_y(screen_x, screen_y);
461 self.touch_state.on_move(device, id, pos, force);
462 }
463 pub fn on_touch_end(
464 &mut self,
465 device: crate::touch_state::TouchDeviceId,
466 id: crate::touch_state::TouchId,
467 ) {
468 self.touch_state.on_end_or_cancel(device, id);
469 }
470 pub fn on_touch_cancel(
471 &mut self,
472 device: crate::touch_state::TouchDeviceId,
473 id: crate::touch_state::TouchId,
474 ) {
475 self.touch_state.on_end_or_cancel(device, id);
476 }
477 pub fn active_touch_count(&self) -> usize {
482 self.touch_state.active_count()
483 }
484
485 fn maybe_bring_to_front(&mut self, clicked_path: &mut Vec<usize>) {
492 let mut node: &dyn Widget = self.root.as_ref();
497 let mut window_info: Option<(Vec<usize>, usize)> = None; for (depth, &idx) in clicked_path.iter().enumerate() {
499 let children = node.children();
500 if idx >= children.len() {
501 break;
502 }
503 node = &*children[idx];
504 if node.type_name() == "Window" {
505 window_info = Some((clicked_path[..depth].to_vec(), idx));
507 }
508 }
509
510 let (parent_path, win_idx) = match window_info {
511 Some(x) => x,
512 None => return,
513 };
514
515 let n = {
517 let parent = widget_at_path(&mut self.root, &parent_path);
518 parent.children().len()
519 };
520 if win_idx >= n - 1 {
521 return;
522 } {
526 let parent = widget_at_path(&mut self.root, &parent_path);
527 let child = parent.children_mut().remove(win_idx);
528 parent.children_mut().push(child);
529 }
530 let new_idx = n - 1;
531 let depth = parent_path.len(); fn shift_path(p: &mut Vec<usize>, depth: usize, old: usize, new: usize) {
535 if p.len() > depth {
536 let i = p[depth];
537 if i == old {
538 p[depth] = new;
539 } else if i > old && i <= new {
540 p[depth] -= 1;
542 }
543 }
544 }
545 shift_path(clicked_path, depth, win_idx, new_idx);
546 if let Some(ref mut p) = self.focus {
547 shift_path(p, depth, win_idx, new_idx);
548 }
549 if let Some(ref mut p) = self.hovered {
550 shift_path(p, depth, win_idx, new_idx);
551 }
552 if let Some(ref mut p) = self.captured {
553 shift_path(p, depth, win_idx, new_idx);
554 }
555 }
556
557 #[inline]
558 fn flip_y(&self, x: f64, y_down: f64) -> Point {
563 let scale = crate::device_scale::device_scale().max(1e-6);
564 let lx = x / scale;
565 let ly_down = y_down / scale;
566 Point::new(lx, self.viewport_height - ly_down)
567 }
568
569 fn compute_hit(&self, pos: Point) -> Option<Vec<usize>> {
570 global_overlay_hit_path(self.root.as_ref(), pos)
571 .or_else(|| hit_test_subtree(self.root.as_ref(), pos))
572 }
573
574 fn dispatch_mouse_move(&mut self, pos: Point) {
575 let new_hit = self.compute_hit(pos);
576
577 if new_hit != self.hovered {
581 if let Some(old_path) = self.hovered.take() {
582 let is_captured = self.captured.as_ref() == Some(&old_path);
583 if !is_captured {
584 let clear = Event::MouseMove {
585 pos: Point::new(-1.0, -1.0),
586 };
587 dispatch_event(&mut self.root, &old_path, &clear, Point::new(-1.0, -1.0));
588 }
589 }
590 self.hovered = new_hit.clone();
591 }
592
593 let event = Event::MouseMove { pos };
594 if let Some(ref cap_path) = self.captured.clone() {
595 dispatch_event(&mut self.root, cap_path, &event, pos);
599 } else if let Some(path) = new_hit {
600 dispatch_event(&mut self.root, &path, &event, pos);
601 }
602 }
603
604 fn set_focus(&mut self, new_path: Option<Vec<usize>>) {
606 if self.focus == new_path {
607 return;
608 }
609 if let Some(old) = self.focus.take() {
610 dispatch_event(&mut self.root, &old, &Event::FocusLost, Point::ORIGIN);
611 }
612 self.focus = new_path.clone();
613 if let Some(new) = new_path {
614 dispatch_event(&mut self.root, &new, &Event::FocusGained, Point::ORIGIN);
615 }
616 }
617
618 fn advance_focus(&mut self, forward: bool) {
620 let mut all: Vec<Vec<usize>> = Vec::new();
621 collect_focusable(self.root.as_ref(), &mut vec![], &mut all);
622 if all.is_empty() {
623 return;
624 }
625 let current_idx = self
626 .focus
627 .as_ref()
628 .and_then(|f| all.iter().position(|p| p == f));
629 let next_idx = match current_idx {
630 None => {
631 if forward {
632 0
633 } else {
634 all.len() - 1
635 }
636 }
637 Some(i) => {
638 if forward {
639 (i + 1) % all.len()
640 } else {
641 if i == 0 {
642 all.len() - 1
643 } else {
644 i - 1
645 }
646 }
647 }
648 };
649 let next_path = all[next_idx].clone();
650 self.set_focus(Some(next_path));
651 }
652}