GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
refactor(widget)!: DELETE the as_*_controller capability system (Phase 6aw)
Option 2 executed: capability discovery dies with its callers retyped.
Deleted in one motion, since nothing outside them remains:
- Element's 12 as_*_controller/as_page_selector discovery hooks (the
dynamic "are you an X?" queries over dyn Element),
- Adapted's 12 forwarding impls,
- the 14 Input capability hooks (menu_controller/page_selector/...)
and their per-widget impls in Breadcrumb, Spreadsheet, Node,
Paginator, Graph, ContentBg, MenuBar, ParametersBg — they existed
only to feed the Adapted forwards.
serialize_widgets' menu-state dump (the one cce-ui production caller)
now downcasts to the two MenuController implementors a roster can hold
(MenuBar / Paginator) via as_any — which exposes the INNER widget of an
adapter, so the controller traits are reached directly on it. In-file
tests rewritten to call the controller traits through the concrete
adapters by deref. Element: 104 -> 92 methods.
168 tests pass. A/B on the live compositor: designer static diff = the
known 2px status sliver; TI sub-threshold; designer HTTP menu_click
(the retyped dynamic-index path) verified live — View > Circular Pane
toggles the layout.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018u7qTwzX95dd5ysAkaSCLk
src/widget/container/breadcrumb.rs | 17 ++---
src/widget/container/content_bg.rs | 6 --
src/widget/container/menu.rs | 32 ++--------
src/widget/container/paginator.rs | 28 ++------
src/widget/container/parameters_bg.rs | 26 ++------
src/widget/container/spreadsheet.rs | 11 +---
src/widget/display/graph.rs | 12 +---
src/widget/display/node.rs | 22 +------
src/widget/display/serialize.rs | 10 ++-
src/widget/mod.rs | 14 +---
src/widget/model.rs | 116 ++++------------------------------
11 files changed, 48 insertions(+), 246 deletions(-)
diff --git a/src/widget/container/breadcrumb.rs b/src/widget/container/breadcrumb.rs
index 38dd772..372c133 100644
--- a/src/widget/container/breadcrumb.rs
+++ b/src/widget/container/breadcrumb.rs
@@ -1,6 +1,6 @@
-//! Narrow-trait `Breadcrumb` (Phase 5k) — the first controller widget across: it re-exposes its
-//! [`PathController`] impl through the `Input` capability hooks, so the legacy
-//! `Element::as_path_controller` downcasts (cce-designer's `path_mut`) keep working. Segment
+//! Narrow-trait `Breadcrumb` (Phase 5k) — the first controller widget across: its
+//! [`PathController`] impl is reached through the concrete `Adapted<Breadcrumb>` by deref
+//! (cce-designer's `path_mut` downcasts the roster entry; Phase 6aw). Segment
//! geometry (hit zones, hover overlay, per-segment text) is derived from the paint rect in one
//! place; the right-press records the clicked segment *before* opening the shared context menu
//! via [`EventCtx::open_context_menu`], so the menu header shows that segment's path.
@@ -184,12 +184,6 @@ impl Input for Breadcrumb {
}
}
- fn path_controller(&self) -> Option<&dyn PathController> {
- Some(self)
- }
- fn path_controller_mut(&mut self) -> Option<&mut dyn PathController> {
- Some(self)
- }
fn copy_path(&self) {
let idx = self.right_clicked_seg.unwrap_or(self.path.len());
@@ -270,10 +264,7 @@ mod tests {
#[test]
fn path_controller_reachable_through_element() {
let mut breadcrumb = Breadcrumb::new();
- let elem: &mut dyn Element = &mut breadcrumb;
- elem.as_path_controller_mut()
- .expect("Breadcrumb exposes PathController through the adapter")
- .set_path(&["a".to_string()]);
+ PathController::set_path(&mut *breadcrumb, &["a".to_string()]);
assert_eq!(breadcrumb.path, vec!["a".to_string()]);
}
}
diff --git a/src/widget/container/content_bg.rs b/src/widget/container/content_bg.rs
index 49949d4..1f61832 100644
--- a/src/widget/container/content_bg.rs
+++ b/src/widget/container/content_bg.rs
@@ -33,12 +33,6 @@ impl Input for ContentBg {
false
}
- fn graph_controller(&self) -> Option<&dyn GraphController> {
- Some(self)
- }
- fn graph_controller_mut(&mut self) -> Option<&mut dyn GraphController> {
- Some(self)
- }
}
impl Paint for ContentBg {
diff --git a/src/widget/container/menu.rs b/src/widget/container/menu.rs
index d0d9043..110a71e 100644
--- a/src/widget/container/menu.rs
+++ b/src/widget/container/menu.rs
@@ -947,18 +947,6 @@ impl Input for MenuBar {
}
}
- fn menu_controller(&self) -> Option<&dyn MenuController> {
- Some(self)
- }
- fn menu_controller_mut(&mut self) -> Option<&mut dyn MenuController> {
- Some(self)
- }
- fn page_selector(&self) -> Option<&dyn PageSelector> {
- Some(self)
- }
- fn page_selector_mut(&mut self) -> Option<&mut dyn PageSelector> {
- Some(self)
- }
}
impl MenuController for MenuBar {
@@ -1121,23 +1109,18 @@ mod tests {
assert!(bw > 0.0, "strip laid out");
assert!(mb.mouse_input(MouseButton::Left, ElementState::Pressed, bx + bw / 2.0, by + bh / 2.0, &mut ctx));
assert!(mb.mouse_input(MouseButton::Left, ElementState::Released, bx + bw / 2.0, by + bh / 2.0, &mut ctx));
- let elem: &dyn Element = &mb;
- assert!(elem.as_menu_controller().unwrap().is_menu_open(), "dropdown open");
+ assert!(MenuController::is_menu_open(&*mb), "dropdown open");
assert!(Element::focused(&mb, &ctx), "bar holds focus while open");
let (dx, dy, _, _) = Element::popover_rect(&mb).expect("dropdown popover");
// Click the second item ("Save"): menu_click reports (0, 1) and everything closes.
assert!(mb.mouse_input(MouseButton::Left, ElementState::Pressed, dx + 10.0, dy + DROPDOWN_ITEM_H * 1.5, &mut ctx));
- {
- let elem: &mut dyn Element = &mut mb;
- assert_eq!(elem.as_menu_controller_mut().unwrap().menu_click(), Some((0, 1)));
- assert!(!elem.as_menu_controller().unwrap().is_menu_open());
- }
+ assert_eq!(MenuController::menu_click(&mut *mb), Some((0, 1)));
+ assert!(!MenuController::is_menu_open(&*mb));
assert!(!Element::focused(&mb, &ctx), "focus released after the click");
- // The PageSelector capability rides the same hooks.
- let elem: &dyn Element = &mb;
- assert!(elem.as_page_selector().unwrap().sidebar_w() > 0.0);
+ // The PageSelector capability is reached through the concrete adapter too.
+ assert!(PageSelector::sidebar_w(&*mb) > 0.0);
}
#[test]
@@ -1149,9 +1132,8 @@ mod tests {
Element::set_rect(&mut mb, 0.0, 0.0, 400.0, 24.0);
Element::set_visible(&mut mb, false);
- let elem: &dyn Element = &mb;
- assert!(!elem.as_menu_controller().unwrap().is_menu_bar(), "hidden bar is not a menu bar");
+ assert!(!MenuController::is_menu_bar(&*mb), "hidden bar is not a menu bar");
assert!(!Element::hit_test(&mb, 10.0, 10.0, &ctx));
- assert!(elem.as_menu_controller().unwrap().get_menu_items_at(10.0, 10.0).is_none());
+ assert!(MenuController::get_menu_items_at(&*mb, 10.0, 10.0).is_none());
}
}
diff --git a/src/widget/container/paginator.rs b/src/widget/container/paginator.rs
index be09364..2221f27 100644
--- a/src/widget/container/paginator.rs
+++ b/src/widget/container/paginator.rs
@@ -248,18 +248,6 @@ impl Input for Paginator {
}
}
- fn menu_controller(&self) -> Option<&dyn MenuController> {
- Some(self)
- }
- fn menu_controller_mut(&mut self) -> Option<&mut dyn MenuController> {
- Some(self)
- }
- fn page_selector(&self) -> Option<&dyn PageSelector> {
- Some(self)
- }
- fn page_selector_mut(&mut self) -> Option<&mut dyn PageSelector> {
- Some(self)
- }
}
impl MenuController for Paginator {
@@ -310,17 +298,13 @@ mod tests {
p.mouse_input(MouseButton::Left, ElementState::Pressed, bx + bw / 2.0, by + bh / 2.0, &mut ctx);
p.mouse_input(MouseButton::Left, ElementState::Released, bx + bw / 2.0, by + bh / 2.0, &mut ctx);
assert_eq!(p.selected_page, 1);
- {
- let elem: &mut dyn Element = &mut p;
- assert_eq!(elem.as_menu_controller_mut().unwrap().menu_click(), Some((1, 0)));
- assert_eq!(elem.as_menu_controller_mut().unwrap().menu_click(), None, "click drained");
- }
+ assert_eq!(MenuController::menu_click(&mut *p), Some((1, 0)));
+ assert_eq!(MenuController::menu_click(&mut *p), None, "click drained");
- // The PageSelector capability rides the wrapper (cce-test-interface's downcast).
- let elem: &dyn Element = &p;
- let ps = elem.as_page_selector().unwrap();
- assert_eq!(ps.selected_page(), 1);
- assert!(ps.sidebar_w() > 0.0);
+ // The PageSelector capability is reached through the concrete adapter (the
+ // cce-test-interface downcast shape).
+ assert_eq!(PageSelector::selected_page(&*p), 1);
+ assert!(PageSelector::sidebar_w(&*p) > 0.0);
}
#[test]
diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index 7ab0e73..199d15e 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -767,12 +767,6 @@ impl Input for ParametersBg {
false
}
- fn param_controller(&self) -> Option<&dyn ParamController> {
- Some(self)
- }
- fn param_controller_mut(&mut self) -> Option<&mut dyn ParamController> {
- Some(self)
- }
// --- The host-driven drag surface (the designer routes pointer drags here directly). ---
@@ -1893,10 +1887,7 @@ mod tests {
.iter()
.map(|(a, b, c)| (a.to_string(), b.to_string(), c.to_string()))
.collect();
- {
- let elem: &mut dyn Element = &mut p;
- elem.as_param_controller_mut().unwrap().set_display_params(¶ms);
- }
+ ParamController::set_display_params(&mut *p, ¶ms);
Element::set_rect(&mut p, 0.0, 0.0, 300.0, 400.0);
p
}
@@ -1908,9 +1899,7 @@ mod tests {
("Mode", "b", "choice:a,b,c"),
("On", "true", "checkbox"),
]);
- let elem: &dyn Element = &p;
- let pc = elem.as_param_controller().unwrap();
- assert_eq!(pc.node_params().len(), 3);
+ assert_eq!(ParamController::node_params(&*p).len(), 3);
assert!(p.sliders[0].is_some() && p.choices[1].is_some() && p.checkboxes[2].is_some());
// Rows were laid out from the cached rect.
let (sx, _, sw, _) = p.sliders[0].as_ref().unwrap().rect();
@@ -1926,8 +1915,7 @@ mod tests {
// every left press, so the return is true either way — assert the value flip).
p.mouse_input(MouseButton::Left, ElementState::Pressed, cx + 6.0, cy + ch / 2.0, &mut ctx);
p.mouse_input(MouseButton::Left, ElementState::Released, cx + 6.0, cy + ch / 2.0, &mut ctx);
- let elem: &dyn Element = &p;
- assert_eq!(elem.as_param_controller().unwrap().node_params()[0].1, "true");
+ assert_eq!(ParamController::node_params(&*p)[0].1, "true");
// Code editor: focus it via a click, type, then unfocus commits the buffer.
let mut p = panel_with(&[("Src", "let x = 1;", "code")]);
@@ -1940,8 +1928,7 @@ mod tests {
Element::unfocus(&mut p);
assert_eq!(p.focused_param, None);
assert!(p.code_editor.is_none());
- let elem: &dyn Element = &p;
- assert!(elem.as_param_controller().unwrap().node_params()[0].1.contains('y'), "editor buffer committed on unfocus");
+ assert!(ParamController::node_params(&*p)[0].1.contains('y'), "editor buffer committed on unfocus");
}
#[test]
@@ -1983,10 +1970,7 @@ mod tests {
.map(|i| (format!("P{i}"), "1.00".to_string(), "slider:0:2".to_string()))
.collect();
let mut p = ParametersBg::new();
- {
- let elem: &mut dyn Element = &mut p;
- elem.as_param_controller_mut().unwrap().set_display_params(&rows);
- }
+ ParamController::set_display_params(&mut *p, &rows);
Element::set_rect(&mut p, 0.0, 0.0, 300.0, 200.0);
assert!(p.content_h > 200.0);
assert!(Element::is_scrollable(&p));
diff --git a/src/widget/container/spreadsheet.rs b/src/widget/container/spreadsheet.rs
index c1cb14d..a3cfdb5 100644
--- a/src/widget/container/spreadsheet.rs
+++ b/src/widget/container/spreadsheet.rs
@@ -350,12 +350,6 @@ impl Input for Spreadsheet {
true
}
- fn spreadsheet_controller(&self) -> Option<&dyn SpreadsheetController> {
- Some(self)
- }
- fn spreadsheet_controller_mut(&mut self) -> Option<&mut dyn SpreadsheetController> {
- Some(self)
- }
}
impl SpreadsheetController for Spreadsheet {
@@ -379,10 +373,7 @@ mod tests {
Element::set_rect(&mut s, 0.0, 0.0, 200.0, 124.0); // viewport: 100 = ~4 rows of 24
let data: Vec<Vec<String>> =
(0..rows).map(|i| vec![format!("r{i}"), format!("v{i}")]).collect();
- let elem: &mut dyn Element = &mut s;
- elem.as_spreadsheet_controller_mut()
- .expect("Spreadsheet exposes SpreadsheetController")
- .set_spreadsheet_data(vec!["a".into(), "b".into()], data);
+ SpreadsheetController::set_spreadsheet_data(&mut *s, vec!["a".into(), "b".into()], data);
s
}
diff --git a/src/widget/display/graph.rs b/src/widget/display/graph.rs
index 4b86ffd..3d5677e 100644
--- a/src/widget/display/graph.rs
+++ b/src/widget/display/graph.rs
@@ -812,12 +812,6 @@ impl Input for Graph {
self.commit_drag();
}
- fn graph_controller(&self) -> Option<&dyn GraphController> {
- Some(self)
- }
- fn graph_controller_mut(&mut self) -> Option<&mut dyn GraphController> {
- Some(self)
- }
}
impl Graph {
@@ -1085,11 +1079,7 @@ mod tests {
// Node b's input port: node b at (200, 160, 80, 40) => top-center (240, 160).
assert!(g.mouse_input(MouseButton::Left, ElementState::Pressed, 240.0, 160.0, &mut ctx));
- let elem: &mut dyn Element = &mut g;
- let pending = elem
- .as_graph_controller_mut()
- .expect("Graph exposes GraphController")
- .take_pending_connection();
+ let pending = GraphController::take_pending_connection(&mut *g);
assert_eq!(pending, Some(("b".to_string(), "alpha".to_string())));
}
diff --git a/src/widget/display/node.rs b/src/widget/display/node.rs
index 35bd841..3c3409f 100644
--- a/src/widget/display/node.rs
+++ b/src/widget/display/node.rs
@@ -222,18 +222,6 @@ impl Input for Node {
self.bounds = Some((bx, by, bw, bh));
}
- fn param_controller(&self) -> Option<&dyn ParamController> {
- Some(self)
- }
- fn param_controller_mut(&mut self) -> Option<&mut dyn ParamController> {
- Some(self)
- }
- fn geom_controller(&self) -> Option<&dyn GeomController> {
- Some(self)
- }
- fn geom_controller_mut(&mut self) -> Option<&mut dyn GeomController> {
- Some(self)
- }
}
impl ParamController for Node {
@@ -272,8 +260,7 @@ mod tests {
// Toggle zone: (100+120-30, 100+11) => 18x18 at (190, 111).
assert!(node.mouse_input(MouseButton::Left, ElementState::Pressed, 195.0, 115.0, &mut ctx));
- let elem: &mut dyn Element = &mut node;
- let geom = elem.as_geom_controller_mut().expect("Node exposes GeomController");
+ let geom: &mut dyn GeomController = &mut *node;
assert!(!geom.geom_visible(), "toggle click hides geometry");
assert!(geom.take_geom_toggle(), "toggle flag set once");
assert!(!geom.take_geom_toggle(), "…and drained");
@@ -290,12 +277,9 @@ mod tests {
#[test]
fn param_controller_roundtrips_through_element() {
let mut node = Node::new(0.0, 0.0, 10.0, 10.0, "n").with_params(&[("k", "v")]);
- let elem: &mut dyn Element = &mut node;
- let params = elem.as_param_controller().expect("Node exposes ParamController").node_params();
+ let params = ParamController::node_params(&*node);
assert_eq!(params, vec![("k".to_string(), "v".to_string(), "string".to_string())]);
- elem.as_param_controller_mut()
- .unwrap()
- .set_display_params(&[("a".to_string(), "b".to_string(), "int".to_string())]);
+ ParamController::set_display_params(&mut *node, &[("a".to_string(), "b".to_string(), "int".to_string())]);
assert_eq!(node.parameters.len(), 1);
assert_eq!(node.parameters[0].2, "int");
}
diff --git a/src/widget/display/serialize.rs b/src/widget/display/serialize.rs
index 0def10d..96bc905 100644
--- a/src/widget/display/serialize.rs
+++ b/src/widget/display/serialize.rs
@@ -23,7 +23,15 @@ fn serialize_single_widget(w: &dyn Element, json: &mut String) {
let mut is_menu_open = false;
let mut is_vertical = false;
let mut checked_states = Vec::new();
- if let Some(mc) = w.as_menu_controller() {
+ // Concrete capability lookup (Phase 6aw): the MenuController implementors a serialized
+ // roster can hold are Adapted<MenuBar> and Adapted<Paginator> — Element's discovery
+ // hooks are gone.
+ let mc: Option<&dyn MenuController> = w
+ .as_any()
+ .downcast_ref::<MenuBar>()
+ .map(|m| m as &dyn MenuController)
+ .or_else(|| w.as_any().downcast_ref::<Paginator>().map(|p| p as &dyn MenuController));
+ if let Some(mc) = mc {
menu_items = mc.menu_items();
is_menu_open = mc.is_menu_open();
is_vertical = mc.is_vertical();
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 943ed1e..66ad217 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -593,24 +593,12 @@ pub trait Element {
fn is_child_visible(&self, _child_id: WidgetId) -> bool { true }
fn set_modifiers(&mut self, _ctrl: bool, _shift: bool, _alt: bool) {}
- fn as_page_selector(&self) -> Option<&dyn PageSelector> { None }
- fn as_page_selector_mut(&mut self) -> Option<&mut dyn PageSelector> { None }
- fn as_menu_controller(&self) -> Option<&dyn MenuController> { None }
- fn as_menu_controller_mut(&mut self) -> Option<&mut dyn MenuController> { None }
- fn as_graph_controller(&self) -> Option<&dyn GraphController> { None }
- fn as_graph_controller_mut(&mut self) -> Option<&mut dyn GraphController> { None }
- fn as_spreadsheet_controller_mut(&mut self) -> Option<&mut dyn SpreadsheetController> { None }
- fn as_path_controller(&self) -> Option<&dyn PathController> { None }
- fn as_path_controller_mut(&mut self) -> Option<&mut dyn PathController> { None }
- fn as_param_controller(&self) -> Option<&dyn ParamController> { None }
- fn as_param_controller_mut(&mut self) -> Option<&mut dyn ParamController> { None }
- fn as_geom_controller_mut(&mut self) -> Option<&mut dyn GeomController> { None }
-
fn parent(&self, ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> {
let base = self.base()?;
ctx.tree.parent_ptr(base.id())
}
+
fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>, ctx: &mut UiContext) {
if let Some(base) = self.base() {
let id = base.id();
diff --git a/src/widget/model.rs b/src/widget/model.rs
index 26b8506..e3971a8 100644
--- a/src/widget/model.rs
+++ b/src/widget/model.rs
@@ -29,9 +29,7 @@
use crate::scene::layout::{Rect, Size, Style};
use crate::scene::paint::{PaintCtx, Prim};
use crate::widget::{
- Element, Event, GeomController, GraphController, MenuController, PageSelector,
- ParamController, PathController, SpreadsheetController, TextLabel, UiContext, Widget,
- WidgetId,
+ Element, Event, TextLabel, UiContext, Widget, WidgetId,
};
/// Layout inputs for the scene layout engine — the RFC's `Widget` concern, named `Layout` here to
@@ -608,42 +606,6 @@ pub trait Input {
// implements the trait. Dies with `Element`: the end state reaches a controller through the
// concrete `Adapted<W>` (or a `&dyn XController` held directly), per RFC §3.5.
- fn menu_controller(&self) -> Option<&dyn MenuController> {
- None
- }
- fn menu_controller_mut(&mut self) -> Option<&mut dyn MenuController> {
- None
- }
- fn graph_controller(&self) -> Option<&dyn GraphController> {
- None
- }
- fn graph_controller_mut(&mut self) -> Option<&mut dyn GraphController> {
- None
- }
- fn spreadsheet_controller(&self) -> Option<&dyn SpreadsheetController> {
- None
- }
- fn spreadsheet_controller_mut(&mut self) -> Option<&mut dyn SpreadsheetController> {
- None
- }
- fn path_controller(&self) -> Option<&dyn PathController> {
- None
- }
- fn path_controller_mut(&mut self) -> Option<&mut dyn PathController> {
- None
- }
- fn param_controller(&self) -> Option<&dyn ParamController> {
- None
- }
- fn param_controller_mut(&mut self) -> Option<&mut dyn ParamController> {
- None
- }
- fn geom_controller(&self) -> Option<&dyn GeomController> {
- None
- }
- fn geom_controller_mut(&mut self) -> Option<&mut dyn GeomController> {
- None
- }
/// Copy this widget's path/content to the clipboard — the context menu's "Copy Path" action
/// calls `Element::copy_path` on its target (Breadcrumb is the only implementor).
@@ -676,12 +638,6 @@ pub trait Input {
base_focused
}
- fn page_selector(&self) -> Option<&dyn PageSelector> {
- None
- }
- fn page_selector_mut(&mut self) -> Option<&mut dyn PageSelector> {
- None
- }
}
/// Wraps a narrow-trait widget `W` so it lives in the legacy `*mut dyn Element` tree. Carries the
@@ -1038,12 +994,6 @@ impl<W: Layout + Paint + Input + 'static> Element for Adapted<W> {
Input::is_focused(&self.inner, self.base.focused)
}
- fn as_page_selector(&self) -> Option<&dyn PageSelector> {
- Input::page_selector(&self.inner)
- }
- fn as_page_selector_mut(&mut self) -> Option<&mut dyn PageSelector> {
- Input::page_selector_mut(&mut self.inner)
- }
fn layout(&mut self, origin: crate::widget::Point, constraints: crate::widget::LayoutConstraints, ctx: &mut UiContext) {
// The Element default (measure + set_rect), plus recursive child layout for visible
@@ -1498,36 +1448,6 @@ impl<W: Layout + Paint + Input + 'static> Element for Adapted<W> {
}
// --- Controller downcasts -> the `Input` capability hooks ---
- fn as_menu_controller(&self) -> Option<&dyn MenuController> {
- Input::menu_controller(&self.inner)
- }
- fn as_menu_controller_mut(&mut self) -> Option<&mut dyn MenuController> {
- Input::menu_controller_mut(&mut self.inner)
- }
- fn as_graph_controller(&self) -> Option<&dyn GraphController> {
- Input::graph_controller(&self.inner)
- }
- fn as_graph_controller_mut(&mut self) -> Option<&mut dyn GraphController> {
- Input::graph_controller_mut(&mut self.inner)
- }
- fn as_spreadsheet_controller_mut(&mut self) -> Option<&mut dyn SpreadsheetController> {
- Input::spreadsheet_controller_mut(&mut self.inner)
- }
- fn as_path_controller(&self) -> Option<&dyn PathController> {
- Input::path_controller(&self.inner)
- }
- fn as_path_controller_mut(&mut self) -> Option<&mut dyn PathController> {
- Input::path_controller_mut(&mut self.inner)
- }
- fn as_param_controller(&self) -> Option<&dyn ParamController> {
- Input::param_controller(&self.inner)
- }
- fn as_param_controller_mut(&mut self) -> Option<&mut dyn ParamController> {
- Input::param_controller_mut(&mut self.inner)
- }
- fn as_geom_controller_mut(&mut self) -> Option<&mut dyn GeomController> {
- Input::geom_controller_mut(&mut self.inner)
- }
fn copy_path(&self) {
Input::copy_path(&self.inner)
}
@@ -1738,6 +1658,7 @@ impl<W: Layout + Paint + Input + 'static> Element for Adapted<W> {
#[cfg(test)]
mod tests {
use super::*;
+ use crate::widget::PathController;
use crate::scene::bridge::layout_subtree;
use crate::scene::layout::{CrossAlign, Size, Style};
use crate::scene::paint::Prim;
@@ -1893,9 +1814,9 @@ mod tests {
assert!(!unsafe { (*ptr).hovered() }, "base hover flag cleared");
}
- /// A narrow widget that is also a controller: it re-exposes its [`PathController`] impl
- /// through the `Input` capability hooks, and the adapter forwards the legacy
- /// `Element::as_path_controller` downcasts to them.
+ /// A narrow widget that is also a controller: the controller trait is reached through the
+ /// concrete `Adapted<W>` by deref (Phase 6aw -- the `Element::as_*_controller` discovery
+ /// hooks are deleted).
struct Crumbs {
segs: Vec<String>,
clicked: Option<usize>,
@@ -1907,12 +1828,6 @@ mod tests {
}
}
impl Input for Crumbs {
- fn path_controller(&self) -> Option<&dyn PathController> {
- Some(self)
- }
- fn path_controller_mut(&mut self) -> Option<&mut dyn PathController> {
- Some(self)
- }
}
impl PathController for Crumbs {
fn set_path(&mut self, segments: &[String]) {
@@ -1924,25 +1839,16 @@ mod tests {
}
#[test]
- fn controller_capability_forwards_through_the_element_downcast() {
+ fn controller_capability_reached_through_the_concrete_adapter() {
let mut w = Box::new(Adapted::new(Crumbs { segs: Vec::new(), clicked: Some(2) }));
- let elem: &mut dyn Element = w.as_mut();
- // The legacy downcast pair reaches the narrow widget's controller impl…
- elem.as_path_controller_mut()
- .expect("Adapted forwards as_path_controller_mut")
- .set_path(&["home".to_string(), "user".to_string()]);
- assert!(elem.as_path_controller().is_some(), "shared-ref downcast forwards too");
- assert_eq!(elem.as_path_controller_mut().unwrap().path_click(), Some(2));
+ // The controller trait is reached by deref through the concrete Adapted<W>...
+ PathController::set_path(&mut **w, &["home".to_string(), "user".to_string()]);
+ assert_eq!(PathController::path_click(&mut **w), Some(2));
- // …and lands on the same state the concrete widget sees.
+ // ...and lands on the same state the concrete widget sees.
assert_eq!(w.inner().segs, vec!["home".to_string(), "user".to_string()]);
- assert_eq!(w.inner().clicked, None, "path_click drained through the forward");
-
- // Capabilities the widget does not expose stay None (the Element defaults).
- let elem: &dyn Element = w.as_ref();
- assert!(elem.as_menu_controller().is_none());
- assert!(elem.as_graph_controller().is_none());
+ assert_eq!(w.inner().clicked, None, "path_click drained through the deref");
}
/// Phase 6: the paint walk's text prims carry the widget's font and clip rect (what the
/// display-list text path renders), not the bare `Paint::paint` text.