Skip to main content

agg_gui/widget/
app.rs

1use super::*;
2
3/// Collect all focusable widgets in paint order (DFS root → leaves).
4/// Returns their paths as `Vec<Vec<usize>>`.
5fn 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
20/// Get a mutable reference to the widget at the given path.
21fn 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
37// ---------------------------------------------------------------------------
38// App — top-level owner of the widget tree
39// ---------------------------------------------------------------------------
40
41/// Owns the widget tree, handles focus, and converts OS events to Y-up coords.
42///
43/// Create with [`App::new`], call [`App::layout`] every frame before
44/// [`App::paint`], and feed OS events through the `on_*` methods.
45pub struct App {
46    root: Box<dyn Widget>,
47    /// Current focus path (indices from root into children vec).
48    /// `None` means no widget has focus.
49    focus: Option<Vec<usize>>,
50    /// Path to the widget last seen under the cursor (for hover clearing).
51    hovered: Option<Vec<usize>>,
52    /// Mouse-captured widget path. Set when a widget consumes `MouseDown`;
53    /// cleared on `MouseUp`. While set, `MouseMove` events go to the captured
54    /// widget regardless of cursor position — enabling slider drag-outside-bounds.
55    captured: Option<Vec<usize>>,
56    /// Viewport height in pixels — used for Y-down → Y-up conversion.
57    viewport_height: f64,
58    /// Viewport size in logical pixels from the most recent layout pass.
59    viewport_size: Size,
60    /// Optional legacy key handler called after widget-tree dispatch.
61    /// Returns `true` if the key was handled.
62    global_key_handler: Option<Box<dyn FnMut(Key, Modifiers) -> bool>>,
63    /// Multi-touch gesture recogniser.  Platform shells feed raw touches
64    /// through [`App::on_touch_start/move/end/cancel`]; widgets read the
65    /// per-frame aggregate via [`crate::current_multi_touch`].
66    touch_state: crate::touch_state::TouchState,
67}
68
69impl App {
70    /// Create a new `App` with `root` as the root widget.
71    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    /// Access the root widget — used by tests and inspectors that need to
85    /// introspect the laid-out tree without re-routing events through the
86    /// full dispatch machinery.  Pair with [`find_widget_by_id`] to locate
87    /// a specific widget by its `Widget::id()` (e.g. a Window's title).
88    pub fn root(&self) -> &dyn Widget {
89        self.root.as_ref()
90    }
91
92    /// Mutable counterpart to [`root`].  Required when a test wants to
93    /// drive a specific sub-widget directly (e.g. reading ScrollView
94    /// scroll offset) after the App has routed an event.
95    pub fn root_mut(&mut self) -> &mut dyn Widget {
96        self.root.as_mut()
97    }
98
99    /// Return the type name of the currently focused widget, if any.
100    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    /// Register a legacy global key handler invoked only after the widget tree
107    /// has ignored the key. Prefer widget-owned key handling for new behavior.
108    ///
109    /// # Example
110    /// ```ignore
111    /// app.set_global_key_handler(|key, mods| {
112    ///     if mods.ctrl && mods.shift && key == Key::O {
113    ///         organize_windows();
114    ///         return true;
115    ///     }
116    ///     false
117    /// });
118    /// ```
119    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    /// Lay out the widget tree to fill `viewport`.  `viewport` is in **physical
127    /// pixels** (e.g. `window.inner_size()` on native, `canvas.width/height` on
128    /// wasm); this method divides by the current device scale factor so the
129    /// widget tree lays out in logical (device-independent) units.  Call once
130    /// per frame before [`paint`][Self::paint].
131    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    /// Paint the entire widget tree into `ctx`. Call after [`layout`][Self::layout].
143    ///
144    /// Applies a `ctx.scale(dps, dps)` transform up-front so the whole tree —
145    /// widget dimensions, font sizes, margins — is rendered at physical pixel
146    /// density on HiDPI screens without any widget having to know about DPI.
147    ///
148    /// Also clears the immediate draw flag so widgets can re-request it during
149    /// this paint if they need another frame; hosts read [`wants_draw`]
150    /// after `paint` returns to decide whether to schedule continuous draws.
151    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        // Recompute the multi-touch aggregate once per paint and publish
157        // to the thread-local — widgets read it during `on_event` or
158        // `paint` without an explicit `&App` reference.
159        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            // Modal/global overlays can contain ComboBox widgets. They submit
170            // their popups while `paint_global_overlays` runs, so drain once
171            // more to draw those popups above the modal body.
172            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    /// After a paint pass, returns `true` if any widget requested another frame
184    /// (e.g. an in-progress hover animation).  Hosts should use this to set
185    /// their event-loop control flow to continuous polling while it's `true`.
186    ///
187    /// Combines the visibility-gated tree-walk signal ([`Widget::needs_draw`])
188    /// with the immediate draw request flag ([`crate::animation::wants_draw`]).
189    /// Widgets call `request_draw` for ordinary visual invalidation; scheduled
190    /// draw needs such as cursor blink should use `needs_draw` /
191    /// `next_draw_deadline` so hidden subtrees do not keep the loop awake.
192    pub fn wants_draw(&self) -> bool {
193        self.root.needs_draw() || crate::animation::wants_draw()
194    }
195
196    /// Earliest scheduled draw deadline across the visible widget tree.
197    /// Hosts translate `Some(t)` into `ControlFlow::WaitUntil(t)` so that
198    /// e.g. a text field's cursor blink wakes the loop exactly at the flip
199    /// boundary.  Invisible subtrees contribute nothing.
200    pub fn next_draw_deadline(&self) -> Option<web_time::Instant> {
201        self.root.next_draw_deadline()
202    }
203
204    // --- Platform event ingestion ---
205    //
206    // Hosts pass raw physical-pixel coordinates (e.g. `e.clientX * devicePixelRatio`
207    // in wasm, or `WindowEvent::CursorMoved.position` on native).  These methods
208    // divide by the current device scale factor and flip Y so widget code sees
209    // logical Y-up coordinates matching the layout pass.
210
211    /// Mouse cursor moved. `screen_y` is Y-down physical pixels.
212    pub fn on_mouse_move(&mut self, screen_x: f64, screen_y: f64) {
213        // Reset cursor so the hovered widget can set it; Default if nothing sets it.
214        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    /// Mouse button pressed. `screen_y` is Y-down physical pixels.
227    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        // Click-to-focus: if the hit widget is focusable, give it focus.
252        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        // NO blanket request_draw.  Mouse-down on an inert area must not
272        // cause a repaint.  Each widget that changes visual state in
273        // response to a MouseDown (button press, window raise, focus
274        // indicator on the focus-gained widget, etc.) is responsible for
275        // calling `crate::animation::request_draw` itself.
276    }
277
278    /// Mouse button released. `screen_y` is Y-down.
279    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        // Deliver release to captured widget first (if any), then clear capture.
299        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    /// Key pressed. Delivered to the focused widget first, then to the visible
310    /// widget tree as an unconsumed key if focus ignores it.
311    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    /// Key released. Delivered to the focused widget.
338    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    /// Mouse wheel scrolled. `screen_y` is Y-down. `delta_y` positive = scroll up.
349    /// `delta_x` positive = content moves right.
350    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    /// Mouse wheel with an explicit horizontal component (trackpad pan,
355    /// shift+wheel via the platform harness).
356    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    /// Mouse wheel with explicit horizontal component and modifier state.
361    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    /// Snapshot the entire widget tree for the inspector.
384    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    /// Serialize the widget tree — types, bounds, depth, properties — as JSON.
391    ///
392    /// Produces a flat array of nodes in paint-order DFS.  Suitable for writing
393    /// to a file and diffing between runs to verify layout stability.  Used by
394    /// the demo harness's debug hotkey.
395    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    /// Returns `true` if any widget currently holds keyboard focus.
422    /// Used by the render loop to schedule cursor-blink repaints.
423    pub fn has_focus(&self) -> bool {
424        self.focus.is_some()
425    }
426
427    /// Call when the cursor leaves the window to clear hover state.
428    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    // --- Touch ingestion ---
434    //
435    // Raw touches go into the multi-touch gesture recogniser; widgets
436    // read `current_multi_touch()` each frame.  Platform shells ALSO
437    // route the first finger through the existing `on_mouse_*` entry
438    // points so widgets that only understand mouse input keep working
439    // without changes.  Coordinates are the same physical-pixel Y-down
440    // units the mouse entry points accept.
441    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    /// Current number of fingers down across all devices.  Used by
478    /// widgets that want to know the gesture has *begun* before the
479    /// first frame has had a chance to produce a delta (where
480    /// `current_multi_touch()` may still be `None`).
481    pub fn active_touch_count(&self) -> usize {
482        self.touch_state.active_count()
483    }
484
485    // --- Private helpers ---
486
487    /// If the click path passes through a `Window` widget, move that window to
488    /// the end of its parent's children list so it paints on top of siblings.
489    /// All stored paths (focus, hovered, captured, plus the clicked path itself)
490    /// are updated to reflect the new index.
491    fn maybe_bring_to_front(&mut self, clicked_path: &mut Vec<usize>) {
492        // Walk the clicked path and record the deepest Window encountered.
493        // At each step we descend into children[idx]; after descending, if the
494        // new node is a Window we record (parent_path, win_idx).  We keep
495        // scanning so a nested Window (unlikely but possible) wins.
496        let mut node: &dyn Widget = self.root.as_ref();
497        let mut window_info: Option<(Vec<usize>, usize)> = None; // (parent_path, win_idx)
498        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                // parent_path = clicked_path[..depth], win_idx = idx
506                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        // Check there's actually a sibling to leapfrog.
516        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        } // already at front
523
524        // Move the window to the end of its parent's children (mutable pass).
525        {
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(); // depth at which the window index sits
532
533        // Update any stored path whose element at `depth` was affected by the move.
534        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                    // Siblings that were after the removed window shift left by 1.
541                    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    /// Convert a platform-supplied physical Y-down coordinate into the
559    /// logical Y-up space the widget tree works in.  Divides by the current
560    /// device scale factor (so mouse coords line up with the scaled paint
561    /// transform) and flips Y against the cached logical viewport height.
562    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 the hovered widget changed, clear the old one — but skip the clear
578        // event when the old widget still has mouse capture (it should keep
579        // receiving real positions, not a (-1,-1) sentinel that snaps state).
580        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            // Captured widget always receives the real position, regardless of
596            // whether the cursor is over it — this is what keeps a slider
597            // tracking the cursor when dragged outside its bounds.
598            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    /// Set focus to `new_path`, sending `FocusLost` / `FocusGained` as needed.
605    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    /// Move focus to the next (or previous) focusable widget in paint order.
619    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}