GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
refactor(widget)!: as_ptr/as_ptr_mut leave WidgetHost — 50->48
The transitional pointer getters live on as inherent Adapted<W> methods
(registration-bridge class); concrete call sites resolve unchanged, and
dyn/generic receivers use the plain cast the impl always was.
impl_widget_base! stops generating them.
Verified: 165 tests + workspace suite; designer render pixel-identical
(empty 8% mask); probes canary-silent.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01N4ajhvVZtyEEEus9bodsj3
docs/rfc-core-rebuild.md | 11 +++++++++++
src/context.rs | 2 +-
src/layout.rs | 4 ++--
src/scene/painter.rs | 4 ++--
src/widget/core.rs | 6 ------
src/widget/mod.rs | 10 ++++++----
src/widget/model.rs | 18 +++++++++++-------
7 files changed, 33 insertions(+), 22 deletions(-)
diff --git a/docs/rfc-core-rebuild.md b/docs/rfc-core-rebuild.md
index 581624e..d1b30bb 100644
--- a/docs/rfc-core-rebuild.md
+++ b/docs/rfc-core-rebuild.md
@@ -1877,6 +1877,17 @@ Constraint respected: **each crate still builds standalone** — the new core is
click lands through the tree link; TI page-selector crop
byte-identical (no double-draw); designer /state + full panel
text intact; settings/files/demo canary-silent.
+ FOLLOW-UP: **`as_ptr`/`as_ptr_mut` left the trait (50→48).**
+ They live on as inherent `Adapted<W>` methods (the
+ registration-bridge class) — every concrete call site resolved
+ unchanged; dyn/generic receivers became the plain casts the
+ impl always was (`w as *mut (dyn WidgetHost + 'static)`;
+ `impl_widget_base!` no longer generates them). Designer's
+ `find_widget_index` now honestly takes the thin `*const ()`
+ address its body always compared. Verified: 165 tests +
+ workspace suite; designer capture pixel-identical to the
+ prior slice's (empty 8% mask) + /state live; TI click probe
+ canary-silent.
5. window_runner render plumbing + remaining `as_ptr` sites; then
the `Element` + `Adapted` endgame (own design pass).
Stored-pointer state remaining after slices 1–3, all deliberate:
diff --git a/src/context.rs b/src/context.rs
index 4e4802c..04a15da 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -898,7 +898,7 @@ mod tests {
fn drag_allowed_everywhere_except_blocking_widgets() {
let mut ctx = UiContext::new();
let mut w = Block { base: Widget::new_rect(10.0, 10.0, 50.0, 50.0) };
- let ptr = w.as_ptr_mut();
+ let ptr = &mut w as *mut _ as *mut (dyn crate::widget::WidgetHost + 'static);
ctx.register_widget(w.base.id(), ptr);
ctx.rebuild_spatial_grid();
diff --git a/src/layout.rs b/src/layout.rs
index 931ec54..3f368f8 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -3144,7 +3144,7 @@ impl RenderTarget for PopoverCollector {
pub fn render_widget<T: WidgetHost + 'static>(pc: &mut dyn RenderTarget, w: &mut T, x: f32, y: f32, ww: f32, wh: f32, ctx: &mut UiContext) {
let id = Some(w.base().id());
if let Some(w_id) = id {
- ctx.register_widget(w_id, w.as_ptr_mut());
+ ctx.register_widget(w_id, w as *mut T as *mut (dyn WidgetHost + 'static));
}
w.layout(crate::widget::Point { x, y }, crate::widget::LayoutConstraints::new(ww, ww, wh, wh), ctx);
let (style_r, corners) = w.corner_style();
@@ -3203,7 +3203,7 @@ pub fn render_widget<T: WidgetHost + 'static>(pc: &mut dyn RenderTarget, w: &mut
// drew the geometry via `all_quads`/`all_rounded_quads` above, so we take only the Text prims
// from the walk. This drops the legacy `widget_font` + `text_labels_with_font_and_bounds`
// getters from render_widget — the prim already carries the per-widget font+bounds.
- let w_ptr = w.as_ptr_mut();
+ let w_ptr = w as *mut T as *mut (dyn WidgetHost + 'static);
let mut text_scratch = crate::scene::paint::PaintCtx::new();
crate::scene::painter::paint_root_into(&*ctx, w_ptr, &mut text_scratch);
for item in text_scratch.finish().items {
diff --git a/src/scene/painter.rs b/src/scene/painter.rs
index eec3e18..d322694 100644
--- a/src/scene/painter.rs
+++ b/src/scene/painter.rs
@@ -209,7 +209,7 @@ mod tests {
}
fn reg(ctx: &mut UiContext, w: &mut P) -> (crate::widget::WidgetId, ElemPtr) {
- let ptr = w.as_ptr_mut();
+ let ptr = &mut *w as *mut _ as *mut (dyn crate::widget::WidgetHost + 'static);
let id = w.base.id();
ctx.register_widget(id, ptr);
(id, ptr)
@@ -346,7 +346,7 @@ mod tests {
let mut w = Rounded { base: Widget::new() };
w.base.w = 20.0;
w.base.h = 10.0;
- let ptr = w.as_ptr_mut();
+ let ptr = &mut w as *mut _ as *mut (dyn crate::widget::WidgetHost + 'static);
ctx.register_widget(w.base.id(), ptr);
let list = paint_tree(&ctx, ptr);
diff --git a/src/widget/core.rs b/src/widget/core.rs
index 7904164..ac23e02 100644
--- a/src/widget/core.rs
+++ b/src/widget/core.rs
@@ -639,11 +639,5 @@ macro_rules! impl_widget_base {
fn base_mut(&mut self) -> &mut $crate::widget::Widget { &mut self.base }
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
- fn as_ptr(&self) -> *mut (dyn $crate::widget::WidgetHost + 'static) {
- self as *const Self as *mut Self as *mut (dyn $crate::widget::WidgetHost + 'static)
- }
- fn as_ptr_mut(&mut self) -> *mut (dyn $crate::widget::WidgetHost + 'static) {
- self as *mut Self as *mut (dyn $crate::widget::WidgetHost + 'static)
- }
};
}
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index bead867..09927e9 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -189,12 +189,14 @@ pub trait WidgetHost {
}
}
- // Required (the flip): the old defaults manufactured DummyAny/null-DummyElement
- // stand-ins nothing could legitimately use. `impl_widget_base!` provides all four.
+ // Required (the flip): the old defaults manufactured DummyAny stand-ins nothing
+ // could legitimately use. `impl_widget_base!` provides both. `as_ptr`/`as_ptr_mut`
+ // are GONE from the trait (the plumbing retype): a pointer to a widget you already
+ // hold is a plain cast (`w as *mut (dyn WidgetHost + 'static)`); concrete
+ // registration sites ride the inherent `Adapted<W>` methods (the registration
+ // bridge — derived from a live borrow, never stored beyond the registry).
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
- fn as_ptr(&self) -> *mut (dyn WidgetHost + 'static);
- fn as_ptr_mut(&mut self) -> *mut (dyn WidgetHost + 'static);
fn handle_event(&mut self, event: &Event, ctx: &mut UiContext) -> bool {
// The default serves test shims only (Adapted overrides this): base hover
diff --git a/src/widget/model.rs b/src/widget/model.rs
index 0732d59..9e3724e 100644
--- a/src/widget/model.rs
+++ b/src/widget/model.rs
@@ -719,6 +719,17 @@ impl<W: Layout + Paint + Input + 'static> Adapted<W> {
}
impl<W: Layout + Paint + Input + 'static> Adapted<W> {
+ /// This widget as a type-erased host pointer (off the `WidgetHost` trait — the
+ /// plumbing retype). Registration-bridge material: derived from a live borrow at the
+ /// call, stored only in the `WidgetTree` registry.
+ pub fn as_ptr(&self) -> *mut (dyn WidgetHost + 'static) {
+ self as *const Self as *mut Self as *mut (dyn WidgetHost + 'static)
+ }
+
+ pub fn as_ptr_mut(&mut self) -> *mut (dyn WidgetHost + 'static) {
+ self as *mut Self as *mut (dyn WidgetHost + 'static)
+ }
+
/// Movement bounds pushed in by hosts (off the `WidgetHost` trait since 6bd — the one
/// production caller is concrete: designer's network panel).
pub fn set_drag_bounds(&mut self, bx: f32, by: f32, bw: f32, bh: f32) {
@@ -1004,13 +1015,6 @@ impl<W: Layout + Paint + Input + 'static> WidgetHost for Adapted<W> {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
&mut self.inner
}
- fn as_ptr(&self) -> *mut (dyn WidgetHost + 'static) {
- self as *const Self as *mut Self as *mut (dyn WidgetHost + 'static)
- }
- fn as_ptr_mut(&mut self) -> *mut (dyn WidgetHost + 'static) {
- self as *mut Self as *mut (dyn WidgetHost + 'static)
- }
-
fn set_visible(&mut self, visible: bool) {
if self.visible != visible {
self.visible = visible;