graphic design tool
git clone https://git.lucas.co/cce-designer.git
refactor: register widgets by reference (UiContext::register_host)
register_widget(id, ptr) took a raw pointer in a safe function; cce-ui is
making it an unsafe fn now that every app registers through
register_host(&mut w), which takes the widget's own id and a live borrow.
Registration loops that existed only to hand the registry raw pointers
(and the unsafe blocks and self_ptr casts around them) are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/app.rs | 5 +----
src/main.rs | 6 ++----
src/render.rs | 12 ++++++------
3 files changed, 9 insertions(+), 14 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index 0f2b992..7cc7bb6 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -8598,10 +8598,7 @@ pub(crate) fn geometry_to_spreadsheet_columns(geom: &Detail) -> (Vec<String>, Ve
state.sync_cursor_and_selection();
state.sync_parameters_pane();
for i in 0..WIDGET_COUNT {
- let w = state.slots.get_dyn_mut(i);
- let id = w.base().id();
- let ptr = w as *mut (dyn WidgetHost + 'static);
- state.ui_context.register_widget(id, ptr);
+ state.ui_context.register_host(state.slots.get_dyn_mut(i));
}
// The layout baseline waits for the layout pass above (it clamps the
// plate fields), and the title computed earlier must be re-read
diff --git a/src/main.rs b/src/main.rs
index 52ef29a..b8e8b3b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15445,8 +15445,7 @@ mod tests {
let mut d = Dialog::new();
d.set_visible(true);
WidgetHost::set_rect(&mut d, 0.0, 0.0, 520.0, 420.0);
- let (id, ptr) = (d.id(), d.as_ptr_mut());
- ctx.register_widget(id, ptr);
+ ctx.register_host(&mut d);
let plain = |i: usize| Row { id: format!("c{i}"), label: format!("Command {i}"), chord: String::new(), control: None, truncate_head: false };
d.set_rows(vec![
Row { id: "zoom_level".into(), label: "Zoom".into(), chord: String::new(), control: Some(Control::Slider { value: 100.0, min: 20.0, max: 320.0, dec: 0, step: 10.0, suffix: "%" }), truncate_head: false },
@@ -15519,8 +15518,7 @@ mod tests {
let mut d = Dialog::new();
d.set_visible(true);
WidgetHost::set_rect(&mut d, 0.0, 0.0, 520.0, 420.0);
- let (id, ptr) = (d.id(), d.as_ptr_mut());
- ctx.register_widget(id, ptr);
+ ctx.register_host(&mut d);
d.set_rows(vec![
Row { id: "zoom_level".into(), label: "Zoom".into(), chord: String::new(), control: Some(Control::Slider { value: 100.0, min: 20.0, max: 320.0, dec: 0, step: 10.0, suffix: "%" }), truncate_head: false },
Row { id: "show_grid".into(), label: "Show Grid".into(), chord: "Ctrl+G".into(), control: Some(Control::Toggle(true)), truncate_head: false },
diff --git a/src/render.rs b/src/render.rs
index 28ff4ff..7ffd9cb 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -227,17 +227,17 @@ impl State {
// root plate container DISSOLVED (Phase 6as): register the widgets (registry consumers:
// coverage/parent walks) and paint each top-level widget directly in sorted order.
self.ui_context.clear_hierarchy();
- let widget_ptrs: Vec<*mut (dyn WidgetHost + 'static)> = (0..WIDGET_COUNT)
- .map(|i| self.slots.get_dyn(i) as *const (dyn WidgetHost + 'static) as *mut (dyn WidgetHost + 'static))
- .collect();
// Register ALL slots, visible or not (id-rooted router): the wheel loop and the
// hidden-widget broadcasts dispatch by id over the whole roster, and visibility
// gates behavior inside the widget — an unregistered hidden root would drop the
- // event before that gate.
+ // event before that gate. Before the paint pointers below are taken, so the
+ // `&mut` each registration borrows does not outlive them.
for i in 0..WIDGET_COUNT {
- let w = self.slots.get_dyn(i);
- self.ui_context.register_widget(w.base().id(), w as *const (dyn WidgetHost + 'static) as *mut (dyn WidgetHost + 'static));
+ self.ui_context.register_host(self.slots.get_dyn_mut(i));
}
+ let widget_ptrs: Vec<*mut (dyn WidgetHost + 'static)> = (0..WIDGET_COUNT)
+ .map(|i| self.slots.get_dyn(i) as *const (dyn WidgetHost + 'static) as *mut (dyn WidgetHost + 'static))
+ .collect();
// The dialog's open dropdown, AFTER the dialog and after the wipe
// above (which would drop it from the tree, leaving an id the
// engine's clamp cannot resolve): the clamp lets an occluder's own