git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commita7115d8dd81efab2c9925979e4a405b4362269f8
parent995bf197b4
authorLucas Galante <[email protected]>
date2026-07-11 23:29
refactor: two Plates + root Backplate dissolved (Phase 6as)

The PARAM plate and network panel become an app-local PassivePlate
replicating Plate's exact emission surface: configured color at
plate_opacity x the network fade, alpha negated under blur, plate corner
radius/rounding, config plate border, and the circular hit shape while
the network pane is round (set_curved_circle). The root Backplate was a
transparent draw-order shim rebuilt every frame — collect_vertices now
registers the visible widgets and draws each top-level widget directly
in the sorted order. Both A/B modes: status-sliver only (first pass
caught the missing plate corners/border as 1px panel outlines).
Designer no longer constructs Plate or Backplate.

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

 src/app.rs    | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 src/render.rs |  44 +++++++++++++++-------
 2 files changed, 133 insertions(+), 29 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index cfaedc2..5d9e314 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -35,7 +35,7 @@ use wayland_client::{
 };
 
 use wgpu::util::DeviceExt;
-use cce_ui::widget::{Breadcrumb, Canvas, MenuBar, Plate, ParametersBg, Splitter, Spreadsheet, StatusBar, TextLabel, Viewport3D, Element, GraphNode, Graph, Button, Checkbox, Label, Dropdown};
+use cce_ui::widget::{Breadcrumb, Canvas, MenuBar, ParametersBg, Splitter, Spreadsheet, StatusBar, TextLabel, Viewport3D, Element, GraphNode, Graph, Button, Checkbox, Label, Dropdown};
 use cce_ui::colors;
 use glyphon::{Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, TextAtlas, TextRenderer, Viewport};
 use glam::{Mat4, Vec3};
@@ -519,6 +519,97 @@ impl NodePalette {
     }
 }
 
+
+/// Dissolved cce-ui `Plate` (Phase 6as): a passive translucent panel — configured color
+/// at `plate_opacity` times the network fade, alpha negated when blur is on (the
+/// scenefx blur marker) — with no children and no events.
+pub struct PassivePlate {
+    base: cce_ui::widget::Widget,
+    visible: bool,
+    color: [f32; 4],
+    blur: bool,
+    pub network_opacity: f32,
+    /// Circular hit shape while the network pane is round (the legacy Plate marker).
+    curved_circle: Option<(f32, f32, f32)>,
+}
+
+impl PassivePlate {
+    pub fn new(color: [f32; 4], blur: bool) -> Self {
+        Self {
+            base: cce_ui::widget::Widget::new(),
+            visible: true,
+            color,
+            blur,
+            network_opacity: 1.0,
+            curved_circle: None,
+        }
+    }
+
+    pub fn set_network_opacity(&mut self, opacity: f32) {
+        self.network_opacity = opacity;
+    }
+
+    pub fn set_curved_circle(&mut self, circle: Option<(f32, f32, f32)>) {
+        self.curved_circle = circle;
+    }
+}
+
+impl cce_ui::widget::Element for PassivePlate {
+    cce_ui::impl_widget_base!(PassivePlate);
+
+    fn visible(&self) -> bool {
+        self.visible
+    }
+
+    fn set_visible(&mut self, visible: bool) {
+        self.visible = visible;
+    }
+
+    fn color(&self) -> [f32; 4] {
+        let mut c = self.color;
+        c[3] *= cce_ui::layout::plate_opacity();
+        c[3] *= self.network_opacity;
+        if self.blur && cce_ui::colors::plate_blur() {
+            c[3] = -c[3].abs();
+        }
+        c
+    }
+
+    fn rounded_corners(&self) -> (bool, bool, bool, bool) {
+        let r = cce_ui::layout::plate_corner_radius();
+        if r > 0.0 {
+            (true, true, true, true)
+        } else {
+            (false, false, false, false)
+        }
+    }
+
+    fn corner_radius(&self) -> f32 {
+        cce_ui::layout::plate_corner_radius()
+    }
+
+    fn solid_border(&self) -> Option<([f32; 4], f32)> {
+        if let Some(bc) = cce_ui::colors::plate_border_color() {
+            Some((bc, cce_ui::colors::plate_border_thickness()))
+        } else {
+            None
+        }
+    }
+
+    fn hit_test(&self, px: f32, py: f32, ctx: &cce_ui::context::UiContext) -> bool {
+        if ctx.is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
+            return false;
+        }
+        if let Some((cx, cy, r)) = self.curved_circle {
+            let dx = px - cx;
+            let dy = py - cy;
+            return dx * dx + dy * dy <= r * r;
+        }
+        let (x, y, w, h) = self.rect();
+        px >= x && px < x + w && py >= y && py < y + h
+    }
+}
+
 impl Element for NodePalette {
     // Leaf legacy widget: own labels via paint_self (cce-ui's default no longer drains
     // the text getters; the render loop's walk text reads it).
@@ -769,7 +860,6 @@ pub struct State {
     pub origin_size: f32,
     pub camera_pivot_size: f32,
 
-    pub root_window: Box<cce_ui::widget::Backplate>,
     pub fs_root: FsNode,
     pub node_templates: Vec<NodeTemplate>,
     pub current_path: Vec<usize>,
@@ -2757,7 +2847,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             Box::new(Splitter::new(SPLITTER_W)),
             Box::new(Viewport3D::new()),
             Box::new(Splitter::new(SPLITTER_W)),
-            Box::new(Plate::new(0.0, 0.0, 0.0, 0.0).with_color(colors::PARAM_BG).with_blur(true)),
+            Box::new(PassivePlate::new(colors::PARAM_BG, true)),
             Box::new(ParametersBg::new()),
             Box::new(Canvas::new()),
             Box::new(MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("0: Network").with_label("Network Menu Bar").with_item("File", &["New", "Save", "Save As"]).with_item("Edit", &["Undo", "Redo"]).with_item("View", &["Zoom In", "Zoom Out", "Circular Pane", "Detach Pane", "Close Pane"]).with_context_options(context_opts.clone(), 0)),
@@ -2785,7 +2875,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
         spreadsheet_menubar.set_visible(false);
         widgets.push(Box::new(spreadsheet_menubar));
 
-        let network_panel = Plate::new(0.0, 0.0, 0.0, 0.0).with_color([0.10, 0.10, 0.13, 0.95]);
+        let network_panel = PassivePlate::new([0.10, 0.10, 0.13, 0.95], false);
         widgets.push(Box::new(network_panel));
 
         let mut positions = Vec::with_capacity(widgets.len());
@@ -2861,11 +2951,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             shortcut_manager,
             pending_action: None,
             exit_requested: false,
-            root_window: Box::new(
-                cce_ui::widget::Backplate::new(0.0, 0.0, lw, lh)
-                    .with_background([0.0, 0.0, 0.0, 0.0])
-                    .with_border([0.0, 0.0, 0.0, 0.0], 0.0)
-            ),
             widgets,
             positions,
             splitter_layout,
@@ -3055,7 +3140,11 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
         state.sync_cursor_and_selection();
         state.sync_parameters_pane();
         for w in &mut state.widgets {
-            state.root_window.add_child(w.as_ptr_mut(), &mut state.ui_context);
+            if let Some(b) = w.base() {
+                let id = b.id();
+                let ptr = w.as_ptr_mut();
+                state.ui_context.register_widget(id, ptr);
+            }
         }
         state
     }
@@ -3090,7 +3179,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
         if let Some(breadcrumb) = self.widgets[BREADCRUMB_IDX].as_any_mut().downcast_mut::<cce_ui::widget::Breadcrumb>() {
             breadcrumb.set_network_opacity(self.network_opacity);
         }
-        if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<cce_ui::widget::Plate>() {
+        if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<PassivePlate>() {
             plate.set_network_opacity(self.network_opacity);
         }
     }
@@ -3265,7 +3354,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             self.positions[CONTENT_IDX] = (cx - r, cy - r + 45.0 + BREADCRUMB_H, 2.0 * r, 2.0 * r - (45.0 + BREADCRUMB_H));
             self.positions[NETWORK_PANEL_IDX] = (cx - r, cy - r, 2.0 * r, 2.0 * r);
             self.widgets[NETWORK_PANEL_IDX].set_rect(cx - r, cy - r, 2.0 * r, 2.0 * r);
-            if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<cce_ui::widget::Plate>() {
+            if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<PassivePlate>() {
                 plate.set_curved_circle(Some((cx, cy, r)));
             }
             self.positions[SPLITTER1_IDX] = (0.0, 0.0, 0.0, 0.0);
@@ -3469,7 +3558,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                 self.positions[CONTENT_IDX] = (cx - r, cy - r + 45.0 + BREADCRUMB_H, 2.0 * r, 2.0 * r - (45.0 + BREADCRUMB_H));
                 self.positions[NETWORK_PANEL_IDX] = (cx - r, cy - r, 2.0 * r, 2.0 * r);
                 self.widgets[NETWORK_PANEL_IDX].set_rect(cx - r, cy - r, 2.0 * r, 2.0 * r);
-                if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<cce_ui::widget::Plate>() {
+                if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<PassivePlate>() {
                     plate.set_curved_circle(Some((cx, cy, r)));
                 }
                 self.widgets[NETWORK_PANEL_IDX].set_drag_bounds(0.0, 0.0, self.width, self.height);
@@ -3557,7 +3646,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                 self.positions[CONTENT_IDX] = (px, py + mb_h + bc_h, pw, content_h);
                 self.positions[NETWORK_PANEL_IDX] = (px, py, pw, ph);
                 self.widgets[NETWORK_PANEL_IDX].set_rect(px, py, pw, ph);
-                if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<cce_ui::widget::Plate>() {
+                if let Some(plate) = self.widgets[NETWORK_PANEL_IDX].as_any_mut().downcast_mut::<PassivePlate>() {
                     plate.set_curved_circle(None);
                 }
                 self.positions[SPLITTER1_IDX] = (0.0, 0.0, 0.0, 0.0);
@@ -3894,7 +3983,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             self.physical_height = height;
             self.width = width as f32 / self.scale as f32;
             self.height = height as f32 / self.scale as f32;
-            self.root_window.set_rect(0.0, 0.0, self.width, self.height);
             self.wgpu_adapter.resize(width, height);
 
             let (tex, view) = self.create_depth_texture();
diff --git a/src/render.rs b/src/render.rs
index 8e31dbb..30083a6 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -101,27 +101,43 @@ impl State {
             (self.has_any_open_menu(i), base_key)
         });
 
+        // Root Backplate DISSOLVED (Phase 6as): it was a transparent draw-order shim —
+        // register the visible widgets (registry consumers: coverage/parent walks) and
+        // draw each top-level widget directly in the sorted order.
         self.ui_context.clear_hierarchy();
-        self.root_window.clear_children(&mut self.ui_context);
+        let widget_ptrs: Vec<*mut (dyn Element + 'static)> = self
+            .widgets
+            .iter()
+            .map(|w| w.as_ptr())
+            .collect();
         for &i in &draw_order {
             if self.widgets[i].visible() {
-                self.root_window.add_child(self.widgets[i].as_ptr_mut(), &mut self.ui_context);
+                if let Some(b) = self.widgets[i].base() {
+                    self.ui_context.register_widget(b.id(), self.widgets[i].as_ptr());
+                }
             }
         }
 
         let mut visited = vec![false; self.widgets.len()];
-        self.draw_element_recursive(
-            self.root_window.as_ref(),
-            verts,
-            sw,
-            sh,
-            [0.0, 0.0, 0.0],
-            show_cursor,
-            node_area_y,
-            &mut visited,
-            clip,
-            clip_circle_val,
-        );
+        for &i in &draw_order {
+            if !self.widgets[i].visible() {
+                continue;
+            }
+            unsafe {
+                self.draw_element_recursive(
+                    &*widget_ptrs[i],
+                    verts,
+                    sw,
+                    sh,
+                    [0.0, 0.0, 0.0],
+                    show_cursor,
+                    node_area_y,
+                    &mut visited,
+                    clip,
+                    clip_circle_val,
+                );
+            }
+        }
     }
 
     pub(crate) fn draw_widget_recursive(