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

commit29e85ca97e077c19678cedfc3c6aeb7d0388434a
parent03fd626c7e
authorLucas Galante <[email protected]>
date2026-07-09 08:37
docs(rfc): record Phase 5r — Paginator migrated

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

 docs/rfc-core-rebuild.md | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/docs/rfc-core-rebuild.md b/docs/rfc-core-rebuild.md
index abd9069..6b487b4 100644
--- a/docs/rfc-core-rebuild.md
+++ b/docs/rfc-core-rebuild.md
@@ -635,9 +635,45 @@ Constraint respected: **each crate still builds standalone** — the new core is
     (authenticator, data-editor, display-manager, email, files, fonts, layout-interface,
     system-settings, text-editor) + 5 in-crate embedders (treelist, scrolling_list,
     keybinds_control, multi_control's `InstancedWidget` variant, parameters_bg).
+  - **5r — `Paginator` (value-embedded container: ButtonStrip + Vec<Page>). DONE (live-A/B
+    verified, cce-layout-interface byte-identical across four states).** The model owns both
+    embedded legacy widgets by value and proxies events to them (strip first, draining its
+    click into the selection; then the selected page while not hidden); the container
+    concern serves them via `container_children`/`child_visible` (strip always, selected
+    page only), and `arrange_children` is the legacy `set_rect` body. New adapter surface:
+    **`Layout::register_embedded_children(host_id, ctx)`** — legacy `tick`/`layout`
+    re-registered the strip + pages into the ctx registry every frame, and that registration
+    is load-bearing (the spatial grid is rebuilt from registered widgets; the registered
+    strip is what blocks backplate drags over the sidebar —
+    `backplate::tests::test_paginator_blocks_backplate_drag`); the adapter calls it from
+    `Element::tick` and `Element::layout`, the legacy cadence. **`Paint::
+    aggregates_child_extra_quads`** — legacy container `extra_quads` served the CHILDREN's
+    chrome only, while the widget's own background quad lived in `all_quads` alone;
+    cce-email and cce-layout-interface render the tab column through `extra_quads` over
+    their own backgrounds (emitting the bg there would double-blend), and cce-test-interface
+    renders through `all_quads` (dropping the bg there would blank it). The adapter's
+    `all_quads` now draws own prims from a shared `own_plain_quads()` helper instead of
+    `extra_quads()` (identical for every prior migration) so the two views never
+    double-serve. **`Paint::forwarded_highlight(ctx)`** — legacy `highlight_quad` forwarded
+    to the strip's (the hovered-tab tint cce-layout-interface draws by calling
+    `highlight_quad` directly); served only through that getter, kept out of
+    `all_quads`/`paint_self` (gated on `legacy_focus_highlight` now — where the strip's own
+    aggregation already carries it, as legacy container `all_quads` overrides did).
+    `PageSelector` + `MenuController` ride the existing `Input` capability hooks
+    (cce-test-interface reaches `sidebar_w` through an `as_page_selector()` downcast on
+    `dyn Element`). Ported bug-for-bug though unused workspace-wide: the `pages` container
+    surface (`add_widget_to_page`/`set_pages`/…) — every app manages page content itself
+    keyed on `selected_page()`. Verified: 171 tests, all four consumer apps build,
+    cce-layout-interface live A/B **byte-identical (AE=0)** on idle, File-tab hover,
+    Page-tab click, and Page-selected hover (exercises extra_quads aggregation, highlight
+    forwarding, labels, and the click→selection→page-switch path);
+    cce-test-interface A/B equals its launch-to-launch noise exactly (same 7.5k-px bbox —
+    animated waveform phase; the `all_quads` gallery path contributes zero residual).
+    Sweep: 3 app repos re-typed to `Adapted<Paginator>` (email, files, layout-interface);
+    test-interface's `Box<dyn Element>` gallery needed no change.
   - **Still to do:** migrate remaining widgets
     off `impl Element` onto the narrow traits (per-widget, Phase 6 flavour). Remaining:
-    Paginator (embeds ButtonStrip + Vec<Page>), ParametersBg (real container, 1.8k lines),
+    ParametersBg (real container, 1.8k lines),
     and the deferred leaves (StatusBar, Float3, LayoutPreview,
     PreviewState); the embedded-base containers
     (Layer/Container/Page/Plate/Backplate/ScrollBox/List/…) dissolve via Phase 6 scene