git.lucas.co / cce-display-manager
login greeter
git clone https://git.lucas.co/cce-display-manager.git

commit32fafc2865026cf1507502198bd822a0831123f3
parent19b200b667
authorLucas Galante <[email protected]>
date2026-07-12 07:07
refactor: root Container dissolved — card + session list are the dispatch roots (Phase 6ax)

The root Container was transparent, at origin, and childless of its
own: relink_tree now registers the card and session list directly
(link_parent_child's registration side effect, minus the dead root
links), the three propagate sites fan out to the two roots in the
legacy z-order (session list first — the reversed child order), and the
text walk visits card then session list (the root's children() order).
Local coords are unaffected: no dm handler reads local_x/local_y.

A/B vs baseline: static diff = background animation phase only
(B-vs-B control differs frame-to-frame across the whole window);
session-list click behavior identical (highlight does not move on
either build — pre-existing); 3/3 interaction rounds alive on both.
One unreproduced bogus-alloc was seen once (dm's known latent
stale-pointer signature) — 0/3 repro afterwards on either binary.

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

 src/main.rs | 47 +++++++++++++++++++++++++++--------------------
 1 file changed, 27 insertions(+), 20 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 359751d..a114b3f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
 use cce_ui::widget::{
     Button, ContentBg, TextLabel, Element, ElementState, MouseButton, Key, NamedKey, KeyEvent, TextBox,
-    Widget, Container, focus, MouseScrollDelta
+    Widget, focus, MouseScrollDelta
 };
 use cce_ui::context::UiContext;
 use wayland_client::QueueHandle;
@@ -282,7 +282,6 @@ struct State {
     status_lbl: StatusLabel,
     session_list: SessionList,
     ui_context: cce_ui::context::UiContext,
-    root_container: Container,
 
 
     cursor_x: f32,
@@ -323,8 +322,11 @@ impl State {
     /// propagate_event, the all_* child aggregation) then reads live widgets.
     fn relink_tree(&mut self) {
         let ctx = &mut self.ui_context;
-        focus::link_parent_child(&mut self.root_container, &mut self.card, ctx);
-        focus::link_parent_child(&mut self.root_container, &mut self.session_list, ctx);
+        // Root Container DISSOLVED (Phase 6ax): the card and the session list are the two
+        // dispatch/walk roots; register them directly (link_parent_child used to do it as a
+        // side effect of the root links).
+        ctx.register_widget(self.card.base.id(), self.card.as_ptr_mut());
+        ctx.register_widget(self.session_list.base.id(), self.session_list.as_ptr_mut());
         focus::link_parent_child(&mut self.card, &mut self.username_box, ctx);
         focus::link_parent_child(&mut self.card, &mut self.password_box, ctx);
         focus::link_parent_child(&mut self.card, &mut self.login_btn, ctx);
@@ -361,9 +363,6 @@ impl State {
         // Background spans the whole screen
         self.bg.set_rect(0.0, 0.0, sw, sh);
 
-        // Root container spans the whole screen
-        self.root_container.set_rect(0.0, 0.0, sw, sh);
-
         // Center card configuration
         let card_w = 360.0;
         let card_h = 280.0;
@@ -405,8 +404,12 @@ impl State {
             local_x: cx,
             local_y: cy,
         };
-        let root_ptr = self.root_container.as_ptr_mut();
-        if self.ui_context.propagate_event(&event, root_ptr) {
+        let sl_ptr = self.session_list.as_ptr_mut();
+        let card_ptr = self.card.as_ptr_mut();
+        if self.ui_context.propagate_event(&event, sl_ptr) {
+            changed = true;
+        }
+        if self.ui_context.propagate_event(&event, card_ptr) {
             changed = true;
         }
         changed
@@ -425,8 +428,10 @@ impl State {
             local_x: cx,
             local_y: cy,
         };
-        let root_ptr = self.root_container.as_ptr_mut();
-        let handled = self.ui_context.propagate_event(&event, root_ptr);
+        let sl_ptr = self.session_list.as_ptr_mut();
+        let card_ptr = self.card.as_ptr_mut();
+        let handled = self.ui_context.propagate_event(&event, sl_ptr)
+            || self.ui_context.propagate_event(&event, card_ptr);
         if button == MouseButton::Left && state == ElementState::Pressed {
             if !handled {
                 self.ui_context.clear_focus();
@@ -447,8 +452,11 @@ impl State {
             changed = true;
         }
         let ui_event = cce_ui::widget::Event::KeyInput(event.clone());
-        let root_ptr = self.root_container.as_ptr_mut();
-        if self.ui_context.propagate_event(&ui_event, root_ptr) {
+        let sl_ptr = self.session_list.as_ptr_mut();
+        let card_ptr = self.card.as_ptr_mut();
+        if self.ui_context.propagate_event(&ui_event, sl_ptr) {
+            changed = true;
+        } else if self.ui_context.propagate_event(&ui_event, card_ptr) {
             changed = true;
         }
         changed
@@ -543,7 +551,6 @@ impl cce_ui::engine::Application for State {
             status_lbl,
             session_list,
             ui_context: cce_ui::context::UiContext::new(),
-            root_container: Container::new(),
             cursor_x: 0.0,
             cursor_y: 0.0,
             width: 1024.0,
@@ -663,13 +670,13 @@ impl cce_ui::engine::Application for State {
             None,
             None,
         );
-        // Widget text via the paint walk, over the TRUE roots: root_container descends into
-        // the card (whose paint_self override carries its header labels) and its input
-        // children, plus the session list; bg is a standalone leaf. Walking the flat
-        // widgets_iter would emit the card's children twice (once via descent, once as
-        // standalone roots).
+        // Widget text via the paint walk, over the TRUE roots (root Container dissolved,
+        // Phase 6ax): the card descends into its input children via the walk; the session
+        // list and bg are standalone leaves. Walking the flat widgets_iter would emit the
+        // card's children twice (once via descent, once as standalone roots).
         cce_ui::scene::painter::append_widget_text(&self.ui_context, &self.bg, &mut pc);
-        cce_ui::scene::painter::append_widget_text(&self.ui_context, &self.root_container, &mut pc);
+        cce_ui::scene::painter::append_widget_text(&self.ui_context, &self.card, &mut pc);
+        cce_ui::scene::painter::append_widget_text(&self.ui_context, &self.session_list, &mut pc);
 
         Some(pc.finish())
     }