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

commit03a2729a8a2ac254596120b16f15ba3792980ff0
parent1063a74c30
authorLucas Galante <[email protected]>
date2026-07-08 12:38
docs(rfc): record Phase 5k — controller capabilities + Breadcrumb/Node

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

 docs/rfc-core-rebuild.md | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/docs/rfc-core-rebuild.md b/docs/rfc-core-rebuild.md
index f5395cd..a3aa63e 100644
--- a/docs/rfc-core-rebuild.md
+++ b/docs/rfc-core-rebuild.md
@@ -482,11 +482,36 @@ Constraint respected: **each crate still builds standalone** — the new core is
     `parent`/`set_parent`) — that belongs with the container/children design, not the leaf
     recipe. Also still pending from the leaf tier: Float3, LayoutPreview (time), PreviewState
     (needs per-label fonts on `Prim::Text`).
+  - **5k — Controller capabilities + first controller widgets: `Breadcrumb`, `Node`. DONE.**
+    The controller tier was blocked because `Element` is implemented exactly once (for
+    `Adapted<W>`), so a migrated widget couldn't re-expose its `as_*_controller` downcasts.
+    Resolution: transitional **capability hooks on `Input`** (`menu/graph/spreadsheet/path/
+    param/geom _controller[_mut]`, default `None`) that the adapter forwards the `Element`
+    downcast pairs to — a controller widget returns `Some(self)`. This is the pragmatic half of
+    the §3.5 "typed messages" bullet: the controller *traits* are already the typed surface;
+    what dies with `Element` is reaching them through the god-trait (end state: hold the
+    concrete `Adapted<W>` or a `&dyn XController` directly). Also added:
+    `EventCtx::open_context_menu` (Breadcrumb records the right-clicked segment *before* the
+    shared menu opens — `opens_context_menu` can't express work-before-menu), an
+    `Input::copy_path` forward (context menu "Copy Path"), and an `Adapted::on_cursor_moved`
+    override — another direct-dispatch entry (cce-files drives breadcrumb hover through it)
+    routing the raw move to `on_event` with the base hover bookkeeping as fallback.
+    `ScrollController` was deleted outright: zero implementors and zero live callers
+    workspace-wide (only a dead cce-designer helper, removed there). **Breadcrumb** (first
+    controller widget, live in cce-files + cce-designer) verified on the live compositor:
+    idle/hover captures pixel-identical to the stashed legacy build at the widget, and a
+    breadcrumb-segment click navigates correctly through the direct-dispatch
+    `mouse_input → on_event → path_click` chain. **Node** (ParamController + GeomController,
+    self-moving drag with grid snap via `drag_reposition`) has no live constructors in any app
+    repo — compile + router-level tests only. 160 tests pass; all 19 client crates build.
   - **Still to do:** migrate remaining widgets
-    off `impl Element` onto the narrow traits (per-widget, Phase 6 flavour); replace the 8 live
-    `as_*_controller` downcast pairs (called by `cce-designer`, `cce-test-interface`, and ~10
-    cce-ui widgets) with a typed message/command channel; delete `Element` + `Adapted` once the
-    last widget is across.
+    off `impl Element` onto the narrow traits (per-widget, Phase 6 flavour). Remaining
+    controller-tier widgets ride the 5k hooks when they migrate: Spreadsheet (needs
+    `tick`/`wants_tick`/`is_scrollable` forwards for its inertial scroll), Graph, and the
+    container-coupled ones (Menu/MenuBar, Paginator, Switcher, ContentBg, ParametersBg — these
+    also need the children/tree concern). Then delete `Element` + `Adapted` once the last
+    widget is across, at which point the `as_*_controller` pairs and the `Input` capability
+    hooks die together (callers hold concrete types or `&dyn XController`).
 - **Phase 6 — Per-app migration.** Move each `cce-*` app onto the new core; delete legacy paths
   once the last app is across.