git.lucas.co / cce-fonts
font browser

commita7ad2886c4a4ee4880661b557297b44a0a8312d4
parent7f90bb03ce
authorLucas Galante <[email protected]>
date2026-07-12 19:54
refactor: cce-fonts on routed events (6bd shrink — direct dispatch retired)

The 6p dispatch_widgets roster stays as the root list, but every per-method
call in it is one Event through ui_context.propagate_event: the pointer-move
is_dragging -> drag_update pass collapses into the router's drag forwarding;
the two-pass press routing (popover-first, then reverse order with the
Plates' unfocus-on-missed-press rule) keeps its structure with propagate per
root; wheel keeps the no-break all-roots loop; KeyInput short-circuits on
the first handler (the 6ac focused-first trap). App-owned ScrollRegions,
focus policy, app shortcuts, and the take_click/take_change drains stay.

Verified live A/B (identical pointer sequence per build): family-row click,
style-dropdown popover open, and 3x list wheel all byte-identical (AE=0);
static frames differ only by cursor sprites.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018u7qTwzX95dd5ysAkaSCLk

 src/main.rs | 73 ++++++++++++++++++++++++++++---------------------------------
 1 file changed, 34 insertions(+), 39 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 4e688b6..fad455f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,7 @@ use wayland_client::QueueHandle;
 use glyphon::FontSystem;
 use cce_ui::engine::{Application, EngineState, LogicalPosition, LogicalSize, WindowSettings};
 use cce_ui::widget::{
-    MouseButton, ElementState, MouseScrollDelta, KeyEvent, WidgetHost,
+    MouseButton, ElementState, MouseScrollDelta, KeyEvent, WidgetHost, Event,
     TextBox, Button, Key, NamedKey, Dropdown, Spinbox,
 };
 
@@ -1115,18 +1115,13 @@ impl Application for TypefaceApp {
         self.list_region.hovered = self.list_region.hit(px, py);
         self.mid_region.hovered = self.mid_region.hit(px, py);
 
-        // The dissolved panels' cursor forwarding: dragging children get drag_update,
-        // everyone else cursor_moved (Plate::on_cursor_moved's inner loop, flattened).
+        // Routed dispatch (6bd shrink): one PointerMove through the router per roster
+        // root — hover bookkeeping plus the router's drag forwarding (replaces the
+        // is_dragging -> drag_update pass; DragUpdate reaches the drag target off-rect).
+        let ev = Event::PointerMove { x: px, y: py, local_x: px, local_y: py };
         for w_ptr in self.dispatch_widgets(true) {
-            unsafe {
-                let w = &mut *w_ptr;
-                if w.is_dragging() {
-                    if w.drag_update(px, py) {
-                        changed = true;
-                    }
-                } else if w.cursor_moved(px, py, &mut self.ui_context) {
-                    changed = true;
-                }
+            if self.ui_context.propagate_event(&ev, w_ptr) {
+                changed = true;
             }
         }
 
@@ -1180,36 +1175,34 @@ impl Application for TypefaceApp {
             }
         }
 
-        // The dissolved panels' press routing, flattened: popover-first (an open dropdown
-        // must see the click before anything beneath), then reverse order with the
-        // Plates' unfocus-on-missed-press rule, stopping at the first handler.
+        // The dissolved panels' press routing, flattened and ROUTED (6bd shrink):
+        // popover-first (an open dropdown must see the click before anything beneath),
+        // then reverse order with the Plates' unfocus-on-missed-press rule, stopping at
+        // the first handler. The router hit-gates presses and records the drag target.
+        let ev = Event::MouseButton { button, state, x: px, y: py, local_x: px, local_y: py };
         let widgets = self.dispatch_widgets(false);
         let mut input_handled = region_handled;
         for &w_ptr in &widgets {
             if input_handled {
                 break;
             }
-            unsafe {
-                let w = &mut *w_ptr;
-                if w.popover_rect().is_some() {
-                    if w.mouse_input(button, state, px, py, &mut self.ui_context) {
-                        changed = true;
-                        input_handled = true;
-                        break;
-                    }
+            if unsafe { (*w_ptr).popover_rect() }.is_some() {
+                if self.ui_context.propagate_event(&ev, w_ptr) {
+                    changed = true;
+                    input_handled = true;
+                    break;
                 }
             }
         }
         if !input_handled {
             for &w_ptr in &widgets {
+                if self.ui_context.propagate_event(&ev, w_ptr) {
+                    changed = true;
+                    break;
+                }
                 unsafe {
-                    let w = &mut *w_ptr;
-                    if w.mouse_input(button, state, px, py, &mut self.ui_context) {
-                        changed = true;
-                        break;
-                    }
-                    if state == ElementState::Pressed && !w.hit_test(px, py, &self.ui_context) {
-                        w.unfocus();
+                    if state == ElementState::Pressed && !(*w_ptr).hit_test(px, py, &self.ui_context) {
+                        (*w_ptr).unfocus();
                     }
                 }
             }
@@ -1281,11 +1274,12 @@ impl Application for TypefaceApp {
         if self.selected_family.is_some() && self.mid_region.wheel(delta, px, py) {
             changed = true;
         }
+        // Routed (6bd shrink): every roster root sees the wheel, as the legacy
+        // no-break loop did; each widget hit-gates internally.
+        let ev = Event::MouseWheel { delta: *delta, x: px, y: py, local_x: px, local_y: py };
         for w_ptr in self.dispatch_widgets(true) {
-            unsafe {
-                if (*w_ptr).mouse_wheel(delta, px, py, &mut self.ui_context) {
-                    changed = true;
-                }
+            if self.ui_context.propagate_event(&ev, w_ptr) {
+                changed = true;
             }
         }
 
@@ -1389,12 +1383,13 @@ impl Application for TypefaceApp {
         if !handled {
             let old_search_text = if self.search_box.editing { self.search_box.edit_buffer.clone() } else { self.search_box.text.clone() };
             
+            // Routed (6bd shrink); short-circuits on the first handler — the router
+            // delivers KeyInput to the focused widget first on EVERY call (the 6ac trap).
+            let ev = Event::KeyInput(event.clone());
             for w_ptr in self.dispatch_widgets(true) {
-                unsafe {
-                    if (*w_ptr).keyboard_input(event, &mut self.ui_context) {
-                        handled = true;
-                        break;
-                    }
+                if self.ui_context.propagate_event(&ev, w_ptr) {
+                    handled = true;
+                    break;
                 }
             }
             if !handled && self.list_region.keyboard(event) {