git.lucas.co / cce-color-editor
color picker and palette editor
git clone https://git.lucas.co/cce-color-editor.git

commit488587f2621e0ff352bf09ecbf4650fc34b7e6ba
parent8fbdcd5941
authorLucas Galante <[email protected]>
date2026-07-09 10:51
feat: whole frame through display_list — text as prims, TextItem assembly deleted (Phase 6e)

Flip Application::display_list_text and emit rebuild_layout's PageContent
text tuples as text_with prims; the rebuild check moves into display_list
and the view/view_rounded_quads overrides, the CCE_LEGACY_PAINT fallback,
and the app-side FontSystem are deleted.

Fixes a pre-existing runtime bug: TextItems shaped with the app's own
create_font_system_with_system_fonts() carry fontdb face IDs the engine's
render FontSystem can't resolve, so every font:None label (slider names,
channel values, hex readout) was invisible at runtime. Shaping through
the engine FontSystem via the display-list text path makes them render.

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

 src/main.rs | 79 ++++++++++++++++++++++---------------------------------------
 1 file changed, 28 insertions(+), 51 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 9519540..f54337e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,7 +5,6 @@ use cce_ui::widget::{
 };
 use cce_ui::widget::focus::link_parent_child;
 use cce_ui::layout::RenderTarget;
-use glyphon::FontSystem;
 use wayland_client::QueueHandle;
 use std::io::IsTerminal;
 
@@ -381,7 +380,6 @@ struct ColorApp {
     with_alpha: bool,
     expecting_output: bool,
 
-    font_system: FontSystem,
     cursor_x: f32,
     cursor_y: f32,
     dragging: Option<usize>,
@@ -392,7 +390,9 @@ struct ColorApp {
     ui_context: UiContext,
 
     widgets: Vec<AppWidget>,
-    text_items: Vec<cce_ui::widget::TextItem>,
+    // (content, font_size, x, y, color, font, bounds) — the PageContent text tuples,
+    // emitted as display-list Text prims.
+    texts: Vec<(String, f32, f32, f32, [f32; 4], Option<String>, Option<[f32; 4]>)>,
 }
 
 impl ColorApp {
@@ -500,7 +500,7 @@ impl ColorApp {
     fn rebuild_layout(&mut self) {
         self.ui_context.clear_hierarchy();
         let mut widgets = Vec::new();
-        let mut text_items = Vec::new();
+        let mut texts = Vec::new();
 
         // 1. Setup root window
         self.root_window.set_rect(0.0, 0.0, self.width as f32, self.height as f32);
@@ -598,7 +598,7 @@ impl ColorApp {
         );
 
         // 6. Gather all quads and text labels
-        for pc_part in &[window_pc, custom_pc] {
+        for pc_part in [window_pc, custom_pc] {
             for (c, x, y, w, h, r, corners) in &pc_part.rects {
                 widgets.push(AppWidget {
                     x: *x,
@@ -610,26 +610,11 @@ impl ColorApp {
                     corners: *corners,
                 });
             }
-            for (text, size, x, y, col, font, bounds) in &pc_part.texts {
-                text_items.push(cce_ui::widget::TextItem::new(
-                    &mut self.font_system,
-                    text,
-                    *size,
-                    *x,
-                    *y,
-                    glyphon::Color::rgb(
-                        (col[0] * 255.0) as u8,
-                        (col[1] * 255.0) as u8,
-                        (col[2] * 255.0) as u8,
-                    ),
-                    font.as_deref(),
-                    *bounds,
-                ));
-            }
+            texts.extend(pc_part.texts);
         }
 
         self.widgets = widgets;
-        self.text_items = text_items;
+        self.texts = texts;
         self.ui_context.clear_dirty();
         self.needs_rebuild = false;
     }
@@ -706,8 +691,6 @@ impl Application for ColorApp {
             sliders.push(ColorSlider::new("A", 6));
         }
 
-        let font_system = cce_ui::create_font_system_with_system_fonts();
-
         let mut app = Self {
             root_window,
             apply_btn,
@@ -722,7 +705,6 @@ impl Application for ColorApp {
             alpha: a,
             with_alpha,
             expecting_output,
-            font_system,
             cursor_x: 0.0,
             cursor_y: 0.0,
             dragging: None,
@@ -732,7 +714,7 @@ impl Application for ColorApp {
             needs_rebuild: true,
             ui_context: UiContext::new(),
             widgets: Vec::new(),
-            text_items: Vec::new(),
+            texts: Vec::new(),
         };
 
         app.sync_slider_values();
@@ -799,17 +781,9 @@ impl Application for ColorApp {
         }
     }
 
-    fn view(&mut self, _quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, size: LogicalSize, scale: f64) {
-        if self.needs_rebuild || self.width != size.width as u32 || self.height != size.height as u32 || self.scale_factor != scale {
-            self.width = size.width as u32;
-            self.height = size.height as u32;
-            self.scale_factor = scale;
-            cce_ui::scale::set_scale_factor(scale as f32);
-            self.rebuild_layout();
-        }
-    }
-
-    fn view_rounded_quads(&mut self, quads: &mut Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))>, size: LogicalSize, scale: f64) {
+    fn display_list(&mut self, size: LogicalSize, scale: f64) -> Option<cce_ui::scene::paint::DisplayList> {
+        // Phase 6 single paint path: the whole frame — geometry and text — is this one list.
+        // rebuild_layout flattens the UI (incl. color ramps/gradients) into self.widgets/self.texts.
         if self.needs_rebuild || self.width != size.width as u32 || self.height != size.height as u32 || self.scale_factor != scale {
             self.width = size.width as u32;
             self.height = size.height as u32;
@@ -817,18 +791,6 @@ impl Application for ColorApp {
             cce_ui::scale::set_scale_factor(scale as f32);
             self.rebuild_layout();
         }
-        for w in &self.widgets {
-            quads.push((w.x, w.y, w.w, w.h, w.radius, w.color, w.corners));
-        }
-    }
-
-    fn display_list(&mut self, _size: cce_ui::engine::LogicalSize, _scale: f64) -> Option<cce_ui::scene::paint::DisplayList> {
-        // Phase 3 single paint path (flat-list bridge). rebuild_layout flattens the UI (incl. color
-        // ramps/gradients) into self.widgets, which view_rounded_quads runs above. CCE_LEGACY_PAINT
-        // falls back.
-        if std::env::var("CCE_LEGACY_PAINT").is_ok() {
-            return None;
-        }
         use cce_ui::scene::layout::Rect;
         let mut pc = cce_ui::scene::paint::PaintCtx::new();
         for w in &self.widgets {
@@ -839,11 +801,26 @@ impl Application for ColorApp {
                 pc.quad(rect, w.color);
             }
         }
+        for (text, font_size, x, y, col, font, bounds) in &self.texts {
+            pc.text_with(
+                text.clone(),
+                *x,
+                *y,
+                *font_size,
+                [
+                    (col[0] * 255.0) as u8,
+                    (col[1] * 255.0) as u8,
+                    (col[2] * 255.0) as u8,
+                ],
+                font.clone(),
+                *bounds,
+            );
+        }
         Some(pc.finish())
     }
 
-    fn text_items(&self) -> &[cce_ui::widget::TextItem] {
-        &self.text_items
+    fn display_list_text(&self) -> bool {
+        true
     }
 
     fn clear_color(&self) -> [f32; 4] {