git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit529222bcb0381374ec0077865477cccc8ba47c66
parent86bddc3aef
authorLucas Galante <[email protected]>
date2026-08-14 13:45
fix: TreeList unlinks its rename editor, which was eating row clicks forever after

`mouse_body` registers AND `link_ids`-attaches `edit_box` when a double-click starts
a rename, but nothing ever undid it. Both the paint and the `set_rect` are gated on
`editing_key_idx.is_some()`, so once editing ended the box kept its last rect, stayed
`visible()` with the default `gates_presses`, and remained a tree child the router
descends into — an invisible 170x24 dead zone parked at the last-edited row's screen
coordinates, swallowing the press before `TreeList::mouse_body` ever ran. Each rename
also minted a fresh TextBox id into the same field, so the child list grew forever.

The other three field widgets (`search_box`, `add_key_btn`, `add_key_popover_box`)
follow the rule this file documents at its `register_embedded_children` — registered
but deliberately NOT tree-linked, because the router's children-first descent both
double-delivers and starves the tree-level logic. The rename editor was the one that
broke it; teardown now matches setup rather than dropping the link, so in-box typing
keeps routing exactly as before.

Live-reproduced in cce-data-editor (its only consumer, and it routes presses through
`propagate_event(tree_list.id())`): double-click a row to rename, Escape, click a
different row, then click the renamed row — before, the selection would not move; the
click vanished. After, the row selects and its value editor opens. Renaming itself
still commits (verified "bell" -> "zz" with the value preserved).

Co-Authored-By: Claude Opus 5 <[email protected]>

 src/widget/container/treelist.rs | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/widget/container/treelist.rs b/src/widget/container/treelist.rs
index 3462103..bc6022e 100644
--- a/src/widget/container/treelist.rs
+++ b/src/widget/container/treelist.rs
@@ -1106,6 +1106,7 @@ impl Input for TreeList {
     /// adapter on commit), and runs the scrollbar activity fade.
     fn tick_ctx(&mut self, dt: f32, ectx: &mut EventCtx) -> bool {
         let host = ectx.host_ptr();
+        let host_id = ectx.id;
         let Some(ui) = ectx.ui.as_deref_mut() else {
             return false;
         };
@@ -1167,6 +1168,17 @@ impl Input for TreeList {
                     }
                 }
                 self.editing_key_idx = None;
+                // Undo the register+link done when editing began (see `mouse_body`). The paint
+                // (`if self.editing_key_idx.is_some()`) and the `set_rect` beside it are both
+                // gated on editing, but the tree link was not — so leaving it attached parked a
+                // 170x24 child at the last-edited row's screen coordinates that kept its stale
+                // rect, kept hit-testing (visible, gates_presses default true) and swallowed the
+                // press before `mouse_body` ever ran: an invisible dead zone that ate row clicks
+                // and silently re-entered editing on an unpainted box. Each rename also minted a
+                // fresh TextBox id into the same field, so the child list grew monotonically.
+                let eb_id = self.edit_box.base().id();
+                ui.unlink_child(host_id, eb_id);
+                ui.unregister_widget(eb_id);
                 if let Some(h) = host { ui.set_focused_ptr(h); }
                 changed = true;
             }